From dbe09c237c00e22c38190eae0a6a266840b75830 Mon Sep 17 00:00:00 2001 From: JanUlrich Date: Wed, 17 Aug 2016 00:45:14 +0200 Subject: [PATCH] Lambda Bytecodegenerierung implementieren --- .../bytecode/ClassGenerator.java | 62 ++++++++++--- .../bytecode/DHBWInstructionFactory.java | 87 +----------------- .../bytecode/MethodGenerator.java | 35 +++++--- .../syntaxtree/FormalParameter.java | 4 + .../statement/LambdaExpression.java | 89 ++++++++++++++++--- .../statement/LocalOrFieldVarOrClassname.java | 13 ++- 6 files changed, 168 insertions(+), 122 deletions(-) diff --git a/src/de/dhbwstuttgart/bytecode/ClassGenerator.java b/src/de/dhbwstuttgart/bytecode/ClassGenerator.java index 073dcbe4..14c12a85 100644 --- a/src/de/dhbwstuttgart/bytecode/ClassGenerator.java +++ b/src/de/dhbwstuttgart/bytecode/ClassGenerator.java @@ -1,5 +1,6 @@ package de.dhbwstuttgart.bytecode; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -90,29 +91,47 @@ public class ClassGenerator extends ClassGen{ throw new DebugException("Ungültige Operation. ClassGenerator muss ein DHBWConstantPool besitzen"); } + private List innerClasses = new ArrayList<>(); /** * Anmerkung: Kann in diesem Zustand nur einmal aufgerufen werden. * @param innerClassAttribute */ public void addInnerClass(InnerClass innerClassAttribute) { - //TODO: Muss vor dem ausführen von getJavaClass ausgeführt werden. getJavaClass überschreiben! - int numberOfInnerClasses = 1; - InnerClass[] innerClasses = new InnerClass[numberOfInnerClasses]; - innerClasses[numberOfInnerClasses-1] = innerClassAttribute; - int innerClassesUTF8 = this.getConstantPool().addUtf8("InnerClasses"); - this.addAttribute(new InnerClasses(innerClassesUTF8,numberOfInnerClasses*8+2,innerClasses,this.getConstantPool().getConstantPool())); + for(InnerClass innerClass : innerClasses){ + //Equal-Check: + if(innerClassCompare(innerClassAttribute, innerClass)){ + return; //Ist die Innere Klasse bereits vorhanden, ist ein weiteres Anfügen nicht notwendig + } + } + innerClasses.add(innerClassAttribute); + } + + /** + * Prüft die InnerClass Attribute a und b auf Gleichheit. + * Hierbei wird davon ausgegangen, dass beide den gleichen ConstantPool benutzen und dieser keine redundanten Einträge enthält + * @param a + * @param b + * @return + */ + private boolean innerClassCompare(InnerClass a, InnerClass b){ + if(a.getInnerAccessFlags() != b.getInnerAccessFlags()){ + return false; + }else if(a.getInnerClassIndex() != b.getInnerClassIndex()){ + return false; + }else if(a.getInnerNameIndex() != b.getInnerNameIndex()){ + return false; + }else if(a.getOuterClassIndex() != b.getOuterClassIndex()){ + return false; + } + return true; } + private List bootstrapMethods= new ArrayList<>(); public int addBootstrapMethod(BootstrapMethod bMethod) { - int numberOfBootstrapMethods = 1; - int name_index = this.getConstantPool().addUtf8("BootstrapMethods"); - int length = 2 + numberOfBootstrapMethods * 4 + bMethod.getNumBootstrapArguments() * 2; - BootstrapMethod[] bootstrap_methods = new BootstrapMethod[numberOfBootstrapMethods]; - bootstrap_methods[numberOfBootstrapMethods-1] = bMethod; - BootstrapMethods bootstrapAttribute = new BootstrapMethods(name_index, length, bootstrap_methods, this.getConstantPool().getConstantPool()); - this.addAttribute(bootstrapAttribute); - return numberOfBootstrapMethods-1; + bootstrapMethods.add(bMethod); + return bootstrapMethods.size()-1; } + public void addUsedTPH(TypePlaceholder tph){ if(! this.getUsedTPH().contains(tph, (a, b)->{ @@ -139,6 +158,21 @@ public class ClassGenerator extends ClassGen{ this.addAttribute(new Signature(cp.addUtf8("Signature"),2,cp.addUtf8(classSignature),cp.getConstantPool())); } + //InnerClass Attribut hinzufügen: + int innerClassesUTF8 = this.getConstantPool().addUtf8("InnerClasses"); + this.addAttribute(new InnerClasses(innerClassesUTF8,innerClasses.size()*8+2, + innerClasses.toArray(new InnerClass[innerClasses.size()]),this.getConstantPool().getConstantPool())); + + //Bootstrap Methoden generieren: + int name_index = this.getConstantPool().addUtf8("BootstrapMethods"); + int length = 2 + bootstrapMethods.size() * 4;// + bMethod.getNumBootstrapArguments() * 2; + for(BootstrapMethod bMethod : bootstrapMethods){ + length += 2 * bMethod.getNumBootstrapArguments(); + } + BootstrapMethod[] bootstrap_methods = bootstrapMethods.toArray(new BootstrapMethod[bootstrapMethods.size()]); + BootstrapMethods bootstrapAttribute = new BootstrapMethods(name_index, length, bootstrap_methods, this.getConstantPool().getConstantPool()); + this.addAttribute(bootstrapAttribute); + return super.getJavaClass(); } diff --git a/src/de/dhbwstuttgart/bytecode/DHBWInstructionFactory.java b/src/de/dhbwstuttgart/bytecode/DHBWInstructionFactory.java index d9e9e821..3b31a935 100644 --- a/src/de/dhbwstuttgart/bytecode/DHBWInstructionFactory.java +++ b/src/de/dhbwstuttgart/bytecode/DHBWInstructionFactory.java @@ -45,96 +45,17 @@ public class DHBWInstructionFactory extends InstructionFactory{ /** * * @param interfaceMethodName - Momentan immer "apply" - * @param invokeDynamicMethodType - Der Methodentyp - * @param interfaceMethodType - * @param lambdaMethod + * @param invokeDynamicMethodType - Der Typ der Methode, welche Dynamisch aufgerufen werden soll * @return */ - public INVOKEDYNAMIC createInvokeDynamic( String interfaceMethodName, String invokeDynamicMethodType, FunN interfaceMethodType, MethodGen lambdaMethod, TypeinferenceResultSet rs) { - //Zuerst die Bootstrap-Methode erstellen: Diese müssen dann in ein BootstrapMethods-Attribut zusammengefasst und dem Classfile hinzugefügt werden - //this.cp.addMethodref(lambdaMethod); - - ArrayList arguments = new ArrayList(); - /* - * Die Argumente für die Bootstrap-Methode. Im Decompilierten Beispiel sind das: - * #19 ()V - * #20 invokespecial Lambda3.lambda$methode$0:()V - * #19 ()V - * - * #20 ist ein MethodHandle_info Structure - * Die Argumente werden der Bootstrap Methode beim Aufrufen übergeben: @see https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html#metafactory-java.lang.invoke.MethodHandles.Lookup-java.lang.String-java.lang.invoke.MethodType-java.lang.invoke.MethodType-java.lang.invoke.MethodHandle-java.lang.invoke.MethodType- - * TODO: Die korrekten Argumente anfügen. - * - samMethodType - Signature and return type of method to be implemented by the function object. - * - implMethod - A direct method handle describing the implementation method which should be called (with suitable adaptation of argument types, return types, and with captured arguments prepended to the invocation arguments) at invocation time. - * - instantiatedMethodType - The signature and return type that should be enforced dynamically at invocation time. This may be the same as samMethodType, or may be a specialization of it. - */ - - String lambdaTypeParameterList = "()"; - ConstantMethodType lambdaMethodType1 = new ConstantMethodType(this.cp.addUtf8(interfaceMethodType.getBytecodeInvokeDynamicSignatureUpperBound(cg))); //TODO: Hier den Grund finden, warum Object stehen muss. - ConstantMethodType lambdaMethodType = new ConstantMethodType(this.cp.addUtf8(interfaceMethodType.getBytecodeInvokeDynamicSignature(cg, rs))); - int implMethodKind = 7; // 7 = InvokeSpecial @see https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5 - ConstantMethodHandle implMethod = new ConstantMethodHandle(implMethodKind,cg.getConstantPool().addMethodref(lambdaMethod)); //Das zweite Argument ist der MethodRef zur LambdaMethode - - //Argumentliste für die Bootstrap Methode zusammensetzen: - arguments.add(cp.addConstant(lambdaMethodType1)); - arguments.add(cp.addConstant(implMethod)); - arguments.add(cp.addConstant(lambdaMethodType)); - - - int innerClassIndex = cp.addClass("java.lang.invoke.MethodHandles$Lookup"); - int innerClassName = cp.addUtf8("Lookup"); - int outerClassIndex = cp.addClass("java.lang.invoke.MethodHandles"); - - int accessFlags = Constants.ACC_FINAL + Constants.ACC_STATIC + Constants.ACC_PUBLIC; - InnerClass innerClassAttribute = new InnerClass(innerClassIndex, outerClassIndex, innerClassName,accessFlags); - //Diese InnereKlasse muss später im ClassFile vorkommen, da sie von der Bootstrap Methode benutzt wird. - //TODO: Dies kann man möglicherweise auslagern. Die ClassGen Klasse erweiter, welche sich dann die InnerenKlassen mertk, welche gebraucht werden - cg.addInnerClass(innerClassAttribute); - - String bootstrapSignature = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; - int lambdaMetafactoryRefIndex = cp.addMethodref("java.lang.invoke.LambdaMetafactory", "metafactory", bootstrapSignature); - //reference kind 6 steht für invokestatic - int lambdaMetafactoryHandle = cp.addConstant(new ConstantMethodHandle(6, lambdaMetafactoryRefIndex)); - - int[] argumentsArray = new int[arguments.size()]; - for(int i = 0; i{}; ist es: run:(LLambda3;)Ljava/lang/Runnable; - * Wieso das zusätzliche Argument vom Typ (this) - * - * TODO: bootstrap_methode den Klassenattributen hinzufügen - * - */ - + public INVOKEDYNAMIC createInvokeDynamic( String interfaceMethodName, String invokeDynamicMethodType, BootstrapMethod bMethod) { + int bootstrap_method_attr_index = cg.addBootstrapMethod(bMethod); - //TODO: der Typ der Lambda Methode muss den Typ von this als erstes Argument enthalten, damit this innerhalb der Lambda Methode verwendet werden kann int invokeDynamicFunktionstyp = cp.addNameAndType(interfaceMethodName, invokeDynamicMethodType); ConstantInvokeDynamic cInvokeDynamic = new ConstantInvokeDynamic(bootstrap_method_attr_index, invokeDynamicFunktionstyp); - index = cp.addConstant(cInvokeDynamic); + int index = cp.addConstant(cInvokeDynamic); return new INVOKEDYNAMIC(index); } diff --git a/src/de/dhbwstuttgart/bytecode/MethodGenerator.java b/src/de/dhbwstuttgart/bytecode/MethodGenerator.java index 6ce372cc..7784e5a9 100644 --- a/src/de/dhbwstuttgart/bytecode/MethodGenerator.java +++ b/src/de/dhbwstuttgart/bytecode/MethodGenerator.java @@ -52,7 +52,8 @@ public class MethodGenerator extends MethodGen{ } } - public Method createMethod(ClassGenerator cg, ParameterList parameter, de.dhbwstuttgart.syntaxtree.type.Type retType, Block block, TypeinferenceResultSet rs){ + public Method createMethod(ClassGenerator cg, ParameterList parameter, de.dhbwstuttgart.syntaxtree.type.Type retType, + Block block, TypeinferenceResultSet rs){ MethodGen method = this; DHBWInstructionFactory factory = cg.getInstructionFactory(); @@ -75,16 +76,9 @@ public class MethodGenerator extends MethodGen{ method.setMaxStack(); //Die Stack Größe automatisch berechnen lassen (erst nach dem alle Instructions angehängt wurden) method.setMaxLocals(); //Die korrekte Signatur für die Methode anhängen. Hier sind dann auch die Parameter von RefTypes enthalten: - String paramTypesSig = "("; - for(FormalParameter p : parameter){ - paramTypesSig += p.getType().getBytecodeSignature(cg, rs); - - Logger.getLogger("MethodGenerator").error(paramTypesSig, Section.CODEGEN); - } - paramTypesSig += ")"; - String retTypeSig = retType.getBytecodeSignature(cg, rs); + String methodDescriptor = getMethodDescriptor(parameter, retType, cg, rs); - method.addAttribute(factory.createSignatureAttribute(paramTypesSig+retTypeSig)); + method.addAttribute(factory.createSignatureAttribute(methodDescriptor)); System.out.println(this.getInstructionList().size()); StackMap stackMap = new StackMapTableGen(this, cp).getStackMap(); @@ -93,6 +87,27 @@ public class MethodGenerator extends MethodGen{ return method.getMethod(); } + /** + * Erstellt den Deskriptor für eine Method mit den angegebenen Parametern und Return Typ. + * Dies ist eine optionale Angabe im java Bytecode und enthält im Gegensatz zur Signatur der Methode auch Generics. + * @param parameter + * @param returnType + * @param cg + * @param rs + * @return + */ + public static String getMethodDescriptor(ParameterList parameter, de.dhbwstuttgart.syntaxtree.type.Type returnType, ClassGenerator cg, TypeinferenceResultSet rs){ + String paramTypesSig = "("; + for(FormalParameter p : parameter){ + paramTypesSig += p.getType().getBytecodeSignature(cg, rs); + + //Logger.getLogger("MethodGenerator").error(paramTypesSig, Section.CODEGEN); + } + paramTypesSig += ")"; + String retTypeSig = returnType.getBytecodeSignature(cg, rs); + return paramTypesSig+retTypeSig; + } + public LocalVariableInstruction createLoad(org.apache.bcel.generic.Type bytecodeType, String variableName) { return InstructionFactory.createLoad(bytecodeType, getStoreIndex(variableName)); } diff --git a/src/de/dhbwstuttgart/syntaxtree/FormalParameter.java b/src/de/dhbwstuttgart/syntaxtree/FormalParameter.java index bc529a12..70ebd5c7 100755 --- a/src/de/dhbwstuttgart/syntaxtree/FormalParameter.java +++ b/src/de/dhbwstuttgart/syntaxtree/FormalParameter.java @@ -39,6 +39,10 @@ public class FormalParameter extends SyntaxTreeNode implements Typeable, TypeIns this.set_DeclId(name); } + public FormalParameter(String name, Type type){ + this(new DeclId(name)); + this.setType(type); + } @Override public boolean equals(Object object) { diff --git a/src/de/dhbwstuttgart/syntaxtree/statement/LambdaExpression.java b/src/de/dhbwstuttgart/syntaxtree/statement/LambdaExpression.java index b5d9e62d..3c2044cf 100755 --- a/src/de/dhbwstuttgart/syntaxtree/statement/LambdaExpression.java +++ b/src/de/dhbwstuttgart/syntaxtree/statement/LambdaExpression.java @@ -1,10 +1,14 @@ package de.dhbwstuttgart.syntaxtree.statement; -import java.util.Hashtable; +import java.util.ArrayList; import org.apache.bcel.Const; +import org.apache.bcel.Constants; import org.apache.bcel.classfile.BootstrapMethod; +import org.apache.bcel.classfile.ConstantMethodHandle; +import org.apache.bcel.classfile.ConstantMethodType; import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.InnerClass; import org.apache.bcel.generic.BIPUSH; import org.apache.bcel.generic.ClassGen; import org.apache.bcel.generic.ConstantPoolGen; @@ -12,9 +16,11 @@ import org.apache.bcel.generic.InstructionFactory; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.MethodGen; import org.apache.bcel.generic.INVOKEDYNAMIC; +import org.apache.bcel.generic.Instruction; import de.dhbwstuttgart.typeinference.Menge; import de.dhbwstuttgart.bytecode.ClassGenerator; +import de.dhbwstuttgart.bytecode.DHBWConstantPoolGen; import de.dhbwstuttgart.bytecode.MethodGenerator; import de.dhbwstuttgart.bytecode.TypePlaceholderType; import de.dhbwstuttgart.parser.JavaClassName; @@ -207,28 +213,87 @@ public class LambdaExpression extends Expr{ for(FormalParameter fp : this.params)ret.add(fp); return ret; } - + @Override public InstructionList genByteCode(ClassGenerator cg, TypeinferenceResultSet rs) { - ConstantPoolGen cp = cg.getConstantPool(); + DHBWConstantPoolGen cp = cg.getConstantPool(); InstructionList il = new InstructionList(); - org.apache.bcel.generic.Type[] paramTypes = params.getBytecodeTypeList(cg, rs); + /* + * Anpassen der Parameter + */ + ParameterList lambdaMethodParams = new ParameterList(); + //if(isStatic) //TODO: Abfrage, ob der Lambda-Ausdruck sich in einem statischen Kontext befindet + lambdaMethodParams.set_AddParameter(new FormalParameter("this", this.getParentClass().getType())); + il.append(InstructionFactory.createLoad( org.apache.bcel.generic.Type.OBJECT, 0)); //This auf den Stack legen + + for(Statement s : method_body.get_Statement()){ + if(s instanceof LocalOrFieldVarOrClassname){ + LocalOrFieldVarOrClassname var = (LocalOrFieldVarOrClassname) s; + lambdaMethodParams.set_AddParameter(new FormalParameter(var.get_Name(),var.getType())); + //Direkt die Load instruktionen für die Parameter anhängen: + il.append(var.createLoad(cg, rs)); //Hier kann noch der cg vom Lambda-Ausdruck verwendet werden + } + } + org.apache.bcel.generic.Type[] additionalParameters = lambdaMethodParams.getBytecodeTypeList(cg, rs); + for(FormalParameter param : params){ + lambdaMethodParams.set_AddParameter(param); + } + + /* + * Generieren der Methode + */ org.apache.bcel.generic.Type retType = method_body.getType().getBytecodeType(cg, rs); - MethodGenerator lambdaMethod = new MethodGenerator(0, retType, - paramTypes, params.getParameterNameArray(), cg.createLambdaMethodName(), + lambdaMethodParams.getBytecodeTypeList(cg, rs), lambdaMethodParams.getParameterNameArray(), cg.createLambdaMethodName(), this.getParentClass().getName().toString(), new InstructionList(), cg.getConstantPool()); - //lambdaMethod.stripAttributes(true); - //lambdaMethod.getInstructionList().append(this.method_body.genByteCode(cg)); - lambdaMethod.setAccessFlags(Const.ACC_PRIVATE+Const.ACC_SYNTHETIC); + cg.setMethodeGenerator(lambdaMethod); + cg.addMethod(lambdaMethod.createMethod(cg, lambdaMethodParams, method_body.getType(), method_body, rs)); + /* + * Generieren der Bootstrap Methode + */ + //Argumentliste für die Bootstrap Methode zusammensetzen: + ArrayList arguments = new ArrayList(); + ConstantMethodType functionalMethodType = new ConstantMethodType(cp.addUtf8(lambdaType.getBytecodeInvokeDynamicSignature(cg, rs))); + + int implMethodKind = 7; // 7 = InvokeSpecial @see https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5 + ConstantMethodHandle implMethod = new ConstantMethodHandle(implMethodKind,cg.getConstantPool().addMethodref(lambdaMethod)); //Das zweite Argument ist der MethodRef zur LambdaMethode + + arguments.add(cp.addConstant(functionalMethodType)); + arguments.add(cp.addConstant(implMethod)); + arguments.add(cp.addConstant(functionalMethodType)); + + String bootstrapSignature = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; + int lambdaMetafactoryRefIndex = cp.addMethodref("java.lang.invoke.LambdaMetafactory", "metafactory", bootstrapSignature); + //reference kind 6 steht für invokestatic + int lambdaMetafactoryHandle = cp.addConstant(new ConstantMethodHandle(6, lambdaMetafactoryRefIndex)); + + int[] argumentsArray = new int[arguments.size()]; + for(int i = 0; i