Bytecode fuer Lambdas in denen Variablen von lexical scope verwendet werden

This commit is contained in:
Fayez Abu Alia 2018-11-23 14:30:15 +01:00
parent 7bfc222037
commit 5c74c69b9e
5 changed files with 135 additions and 12 deletions

View File

@ -46,7 +46,8 @@ public class BytecodeGen implements ASTVisitor {
ClassWriter cw =new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
String type;
public static RefTypeOrTPHOrWildcardOrGeneric THISTYPE = null;
String className;
private boolean isInterface;
private List<ResultSet> listOfResultSets;

View File

@ -109,7 +109,7 @@ public class BytecodeGenMethod implements StatementVisitor {
}
public BytecodeGenMethod(LambdaExpression lambdaExpression, ResultSet resultSet, MethodVisitor mv,
public BytecodeGenMethod(LambdaExpression lambdaExpression, ArrayList<String> usedVars, ResultSet resultSet, MethodVisitor mv,
int indexOfFirstParamLam, boolean isInterface, HashMap<String, byte[]> classFiles, String path, int lamCounter, SourceFile sf) {
this.resultSet = resultSet;
@ -121,6 +121,12 @@ public class BytecodeGenMethod implements StatementVisitor {
this.sf = sf;
Iterator<FormalParameter> itr = lambdaExpression.params.iterator();
int i = indexOfFirstParamLam;
for(String var : usedVars) {
this.paramsAndLocals.put(var, i);
i++;
}
while (itr.hasNext()) {
FormalParameter fp = itr.next();
this.paramsAndLocals.put(fp.getName(), i);
@ -520,7 +526,7 @@ public class BytecodeGenMethod implements StatementVisitor {
@Override
public void visit(LambdaExpression lambdaExpression) {
this.lamCounter++;
String typeErasure = "(";
Iterator<FormalParameter> itr = lambdaExpression.params.iterator();
while (itr.hasNext()) {
@ -560,16 +566,31 @@ public class BytecodeGenMethod implements StatementVisitor {
this.kindOfLambda = new KindOfLambda(lambdaExpression);
if (kindOfLambda.isInstanceCapturingLambda()) {
// if(!kindOfLambda.getArgumentList().contains(BytecodeGen.THISTYPE))
// kindOfLambda.getArgumentList().add(0, BytecodeGen.THISTYPE);
mv.visitVarInsn(Opcodes.ALOAD, 0);
for(String v : kindOfLambda.getUsedVars()) {
mv.visitVarInsn(Opcodes.ALOAD, paramsAndLocals.get(v));
}
staticOrSpecial = Opcodes.H_INVOKESPECIAL;
indexOfFirstParamLam = 1;
} else {
staticOrSpecial = Opcodes.H_INVOKESTATIC;
staticOrInstance = Opcodes.ACC_STATIC;
}
String newDesc = "(";
int pos = 0;
if(kindOfLambda.isHasThis()) {
pos = 1;
}
for(int i=pos;i<kindOfLambda.getArgumentList().size();i++) {
String t = "L" + getResolvedType(kindOfLambda.getArgumentList().get(i)) + ";";
newDesc += t;
}
newDesc += lamDesc.substring(1);
// first check if capturing lambda then invokestatic or invokespecial
Handle arg2 = new Handle(staticOrSpecial, this.className, methodName, arg3.toString(), false);
Handle arg2 = new Handle(staticOrSpecial, this.className, methodName, newDesc, false);
// Descriptor of functional interface methode
SamMethod samMethod = new SamMethod(kindOfLambda.getArgumentList(), lambdaExpression.getType());
// Desc: (this/nothing)TargetType
@ -577,9 +598,11 @@ public class BytecodeGenMethod implements StatementVisitor {
mv.visitInvokeDynamicInsn("apply", fiMethodDesc, bootstrap, arg1, arg2, arg3);
MethodVisitor mvLambdaBody = cw.visitMethod(Opcodes.ACC_PRIVATE + staticOrInstance + Opcodes.ACC_SYNTHETIC,
methodName, arg3.toString(), null, null);
new BytecodeGenMethod(lambdaExpression, this.resultSet, mvLambdaBody, indexOfFirstParamLam, isInterface,
methodName, newDesc, null, null);
ArrayList<String> usedVars = kindOfLambda.getUsedVars();
new BytecodeGenMethod(lambdaExpression, usedVars,this.resultSet, mvLambdaBody, indexOfFirstParamLam, isInterface,
classFiles,this.path, lamCounter, sf);
mvLambdaBody.visitMaxs(0, 0);
@ -654,7 +677,10 @@ public class BytecodeGenMethod implements StatementVisitor {
if (!fieldVar.receiver.getClass().equals(StaticClassName.class)) {
mv.visitFieldInsn(Opcodes.GETFIELD, getResolvedType(fieldVar.receiver.getType()), fieldName, fieldDesc);
}
if (isBinaryExp) {
doUnboxing(getResolvedType(fieldVar.getType()));
}
// mv.visitFieldInsn(Opcodes.GETSTATIC,
// fieldVar.receiver.getType().toString().replace(".", "/"),
// fieldVar.fieldVarName, fieldVar.getType().toString());
@ -1081,6 +1107,10 @@ public class BytecodeGenMethod implements StatementVisitor {
@Override
public void visit(This aThis) {
if(BytecodeGen.THISTYPE == null)
BytecodeGen.THISTYPE = aThis.getType();
mv.visitVarInsn(Opcodes.ALOAD, 0);
}

View File

@ -4,22 +4,33 @@ import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.syntaxtree.statement.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
import de.dhbwstuttgart.syntaxtree.FormalParameter;
import de.dhbwstuttgart.syntaxtree.ParameterList;
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
import de.dhbwstuttgart.syntaxtree.statement.Literal;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
public class KindOfLambda implements StatementVisitor{
private ParameterList params;
private boolean isInstanceCapturingLambda = false;
private List<RefTypeOrTPHOrWildcardOrGeneric> argumentList = new ArrayList<>();
private ArrayList<String> usedVars = new ArrayList<>();
private boolean hasThis = false;
public KindOfLambda(LambdaExpression lambdaExpression) {
this.params = lambdaExpression.params;
lambdaExpression.methodBody.accept(this);
}
public ArrayList<String> getUsedVars() {
return usedVars;
}
public boolean isInstanceCapturingLambda() {
return this.isInstanceCapturingLambda;
}
@ -28,6 +39,10 @@ public class KindOfLambda implements StatementVisitor{
return argumentList;
}
public boolean isHasThis() {
return hasThis;
}
@Override
public void visit(ArgumentList argumentList) {
// TODO Auto-generated method stub
@ -95,8 +110,26 @@ public class KindOfLambda implements StatementVisitor{
@Override
public void visit(LocalVar localVar) {
// TODO Auto-generated method stub
if(!contain(params, localVar.name)) {
argumentList.add(localVar.getType());
if(hasThis) {
usedVars.add(1, localVar.name);
} else {
usedVars.add(0, localVar.name);
}
if(!isInstanceCapturingLambda)
isInstanceCapturingLambda=true;
}
}
private boolean contain(ParameterList params2, String name) {
Iterator<FormalParameter> itr = params2.iterator();
while(itr.hasNext()) {
FormalParameter fp = itr.next();
if(fp.getName().equals(name))
return true;
}
return false;
}
@Override
@ -157,9 +190,13 @@ public class KindOfLambda implements StatementVisitor{
@Override
public void visit(This aThis) {
if(!hasThis) {
hasThis = true;
this.argumentList.add(0,aThis.getType());
}
if(!isInstanceCapturingLambda) {
this.isInstanceCapturingLambda = true;
this.argumentList.add(aThis.getType());
}
}

View File

@ -0,0 +1,43 @@
/**
*
*/
package bytecode;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;
import de.dhbwstuttgart.core.JavaTXCompiler;
/**
* @author fayez
*
*/
public class LambdaCapturetest {
private static String path;
private static File fileToTest;
private static JavaTXCompiler compiler;
private static ClassLoader loader;
private static Class<?> classToTest;
private static String pathToClassFile;
private static Object instanceOfClass;
@Test
public void generateBC() throws Exception {
path = System.getProperty("user.dir")+"/test/bytecode/javFiles/LambdaCapture.jav";
fileToTest = new File(path);
compiler = new JavaTXCompiler(fileToTest);
compiler.generateBytecode(System.getProperty("user.dir")+"/testBytecode/generatedBC/");
pathToClassFile = System.getProperty("user.dir")+"/testBytecode/generatedBC/";
loader = new URLClassLoader(new URL[] {new URL("file://"+pathToClassFile)});
classToTest = loader.loadClass("LambdaCapture");
instanceOfClass = classToTest.getDeclaredConstructor().newInstance();
}
}

View File

@ -0,0 +1,12 @@
import java.lang.Integer;
public class LambdaCapture {
Integer i = 8;
f;
public LambdaCapture(){
Integer w = 7;
f = j ->{
return w+i;};
}
}