forked from JavaTX/JavaCompilerCore
Bytecode fuer Lambdas in denen Variablen von lexical scope verwendet werden
This commit is contained in:
parent
7bfc222037
commit
5c74c69b9e
@ -46,7 +46,8 @@ public class BytecodeGen implements ASTVisitor {
|
|||||||
ClassWriter cw =new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
|
ClassWriter cw =new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
|
||||||
|
|
||||||
String type;
|
String type;
|
||||||
|
|
||||||
|
public static RefTypeOrTPHOrWildcardOrGeneric THISTYPE = null;
|
||||||
String className;
|
String className;
|
||||||
private boolean isInterface;
|
private boolean isInterface;
|
||||||
private List<ResultSet> listOfResultSets;
|
private List<ResultSet> listOfResultSets;
|
||||||
|
@ -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) {
|
int indexOfFirstParamLam, boolean isInterface, HashMap<String, byte[]> classFiles, String path, int lamCounter, SourceFile sf) {
|
||||||
|
|
||||||
this.resultSet = resultSet;
|
this.resultSet = resultSet;
|
||||||
@ -121,6 +121,12 @@ public class BytecodeGenMethod implements StatementVisitor {
|
|||||||
this.sf = sf;
|
this.sf = sf;
|
||||||
Iterator<FormalParameter> itr = lambdaExpression.params.iterator();
|
Iterator<FormalParameter> itr = lambdaExpression.params.iterator();
|
||||||
int i = indexOfFirstParamLam;
|
int i = indexOfFirstParamLam;
|
||||||
|
|
||||||
|
for(String var : usedVars) {
|
||||||
|
this.paramsAndLocals.put(var, i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
while (itr.hasNext()) {
|
while (itr.hasNext()) {
|
||||||
FormalParameter fp = itr.next();
|
FormalParameter fp = itr.next();
|
||||||
this.paramsAndLocals.put(fp.getName(), i);
|
this.paramsAndLocals.put(fp.getName(), i);
|
||||||
@ -520,7 +526,7 @@ public class BytecodeGenMethod implements StatementVisitor {
|
|||||||
@Override
|
@Override
|
||||||
public void visit(LambdaExpression lambdaExpression) {
|
public void visit(LambdaExpression lambdaExpression) {
|
||||||
this.lamCounter++;
|
this.lamCounter++;
|
||||||
|
|
||||||
String typeErasure = "(";
|
String typeErasure = "(";
|
||||||
Iterator<FormalParameter> itr = lambdaExpression.params.iterator();
|
Iterator<FormalParameter> itr = lambdaExpression.params.iterator();
|
||||||
while (itr.hasNext()) {
|
while (itr.hasNext()) {
|
||||||
@ -560,16 +566,31 @@ public class BytecodeGenMethod implements StatementVisitor {
|
|||||||
this.kindOfLambda = new KindOfLambda(lambdaExpression);
|
this.kindOfLambda = new KindOfLambda(lambdaExpression);
|
||||||
|
|
||||||
if (kindOfLambda.isInstanceCapturingLambda()) {
|
if (kindOfLambda.isInstanceCapturingLambda()) {
|
||||||
|
// if(!kindOfLambda.getArgumentList().contains(BytecodeGen.THISTYPE))
|
||||||
|
// kindOfLambda.getArgumentList().add(0, BytecodeGen.THISTYPE);
|
||||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
for(String v : kindOfLambda.getUsedVars()) {
|
||||||
|
mv.visitVarInsn(Opcodes.ALOAD, paramsAndLocals.get(v));
|
||||||
|
}
|
||||||
staticOrSpecial = Opcodes.H_INVOKESPECIAL;
|
staticOrSpecial = Opcodes.H_INVOKESPECIAL;
|
||||||
indexOfFirstParamLam = 1;
|
indexOfFirstParamLam = 1;
|
||||||
} else {
|
} else {
|
||||||
staticOrSpecial = Opcodes.H_INVOKESTATIC;
|
staticOrSpecial = Opcodes.H_INVOKESTATIC;
|
||||||
staticOrInstance = Opcodes.ACC_STATIC;
|
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
|
// 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
|
// Descriptor of functional interface methode
|
||||||
SamMethod samMethod = new SamMethod(kindOfLambda.getArgumentList(), lambdaExpression.getType());
|
SamMethod samMethod = new SamMethod(kindOfLambda.getArgumentList(), lambdaExpression.getType());
|
||||||
// Desc: (this/nothing)TargetType
|
// Desc: (this/nothing)TargetType
|
||||||
@ -577,9 +598,11 @@ public class BytecodeGenMethod implements StatementVisitor {
|
|||||||
mv.visitInvokeDynamicInsn("apply", fiMethodDesc, bootstrap, arg1, arg2, arg3);
|
mv.visitInvokeDynamicInsn("apply", fiMethodDesc, bootstrap, arg1, arg2, arg3);
|
||||||
|
|
||||||
MethodVisitor mvLambdaBody = cw.visitMethod(Opcodes.ACC_PRIVATE + staticOrInstance + Opcodes.ACC_SYNTHETIC,
|
MethodVisitor mvLambdaBody = cw.visitMethod(Opcodes.ACC_PRIVATE + staticOrInstance + Opcodes.ACC_SYNTHETIC,
|
||||||
methodName, arg3.toString(), null, null);
|
methodName, newDesc, null, null);
|
||||||
|
|
||||||
new BytecodeGenMethod(lambdaExpression, this.resultSet, mvLambdaBody, indexOfFirstParamLam, isInterface,
|
ArrayList<String> usedVars = kindOfLambda.getUsedVars();
|
||||||
|
|
||||||
|
new BytecodeGenMethod(lambdaExpression, usedVars,this.resultSet, mvLambdaBody, indexOfFirstParamLam, isInterface,
|
||||||
classFiles,this.path, lamCounter, sf);
|
classFiles,this.path, lamCounter, sf);
|
||||||
|
|
||||||
mvLambdaBody.visitMaxs(0, 0);
|
mvLambdaBody.visitMaxs(0, 0);
|
||||||
@ -654,7 +677,10 @@ public class BytecodeGenMethod implements StatementVisitor {
|
|||||||
if (!fieldVar.receiver.getClass().equals(StaticClassName.class)) {
|
if (!fieldVar.receiver.getClass().equals(StaticClassName.class)) {
|
||||||
mv.visitFieldInsn(Opcodes.GETFIELD, getResolvedType(fieldVar.receiver.getType()), fieldName, fieldDesc);
|
mv.visitFieldInsn(Opcodes.GETFIELD, getResolvedType(fieldVar.receiver.getType()), fieldName, fieldDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isBinaryExp) {
|
||||||
|
doUnboxing(getResolvedType(fieldVar.getType()));
|
||||||
|
}
|
||||||
// mv.visitFieldInsn(Opcodes.GETSTATIC,
|
// mv.visitFieldInsn(Opcodes.GETSTATIC,
|
||||||
// fieldVar.receiver.getType().toString().replace(".", "/"),
|
// fieldVar.receiver.getType().toString().replace(".", "/"),
|
||||||
// fieldVar.fieldVarName, fieldVar.getType().toString());
|
// fieldVar.fieldVarName, fieldVar.getType().toString());
|
||||||
@ -1081,6 +1107,10 @@ public class BytecodeGenMethod implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(This aThis) {
|
public void visit(This aThis) {
|
||||||
|
|
||||||
|
if(BytecodeGen.THISTYPE == null)
|
||||||
|
BytecodeGen.THISTYPE = aThis.getType();
|
||||||
|
|
||||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,22 +4,33 @@ import de.dhbwstuttgart.exceptions.NotImplementedException;
|
|||||||
import de.dhbwstuttgart.syntaxtree.statement.*;
|
import de.dhbwstuttgart.syntaxtree.statement.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
|
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.StatementVisitor;
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.Literal;
|
import de.dhbwstuttgart.syntaxtree.statement.Literal;
|
||||||
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
||||||
|
|
||||||
public class KindOfLambda implements StatementVisitor{
|
public class KindOfLambda implements StatementVisitor{
|
||||||
|
private ParameterList params;
|
||||||
private boolean isInstanceCapturingLambda = false;
|
private boolean isInstanceCapturingLambda = false;
|
||||||
private List<RefTypeOrTPHOrWildcardOrGeneric> argumentList = new ArrayList<>();
|
private List<RefTypeOrTPHOrWildcardOrGeneric> argumentList = new ArrayList<>();
|
||||||
|
private ArrayList<String> usedVars = new ArrayList<>();
|
||||||
|
private boolean hasThis = false;
|
||||||
|
|
||||||
public KindOfLambda(LambdaExpression lambdaExpression) {
|
public KindOfLambda(LambdaExpression lambdaExpression) {
|
||||||
|
this.params = lambdaExpression.params;
|
||||||
lambdaExpression.methodBody.accept(this);
|
lambdaExpression.methodBody.accept(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArrayList<String> getUsedVars() {
|
||||||
|
return usedVars;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isInstanceCapturingLambda() {
|
public boolean isInstanceCapturingLambda() {
|
||||||
return this.isInstanceCapturingLambda;
|
return this.isInstanceCapturingLambda;
|
||||||
}
|
}
|
||||||
@ -28,6 +39,10 @@ public class KindOfLambda implements StatementVisitor{
|
|||||||
return argumentList;
|
return argumentList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHasThis() {
|
||||||
|
return hasThis;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ArgumentList argumentList) {
|
public void visit(ArgumentList argumentList) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
@ -95,8 +110,26 @@ public class KindOfLambda implements StatementVisitor{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(LocalVar localVar) {
|
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
|
@Override
|
||||||
@ -157,9 +190,13 @@ public class KindOfLambda implements StatementVisitor{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(This aThis) {
|
public void visit(This aThis) {
|
||||||
|
if(!hasThis) {
|
||||||
|
hasThis = true;
|
||||||
|
this.argumentList.add(0,aThis.getType());
|
||||||
|
}
|
||||||
if(!isInstanceCapturingLambda) {
|
if(!isInstanceCapturingLambda) {
|
||||||
this.isInstanceCapturingLambda = true;
|
this.isInstanceCapturingLambda = true;
|
||||||
this.argumentList.add(aThis.getType());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
43
test/bytecode/LambdaCapturetest.java
Normal file
43
test/bytecode/LambdaCapturetest.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
test/bytecode/javFiles/LambdaCapture.jav
Normal file
12
test/bytecode/javFiles/LambdaCapture.jav
Normal 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;};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user