Capture this in closures
This commit is contained in:
@@ -16,6 +16,7 @@ public class ClassContext {
|
||||
private final OwnerKind contextKind;
|
||||
private final StackValue thisExpression;
|
||||
private final ClassContext parentContext;
|
||||
private boolean thisWasUsed = false;
|
||||
|
||||
public ClassContext(DeclarationDescriptor contextType, OwnerKind contextKind, StackValue thisExpression, ClassContext parentContext) {
|
||||
this.contextType = contextType;
|
||||
@@ -33,6 +34,7 @@ public class ClassContext {
|
||||
}
|
||||
|
||||
public StackValue getThisExpression() {
|
||||
thisWasUsed = true;
|
||||
if (thisExpression != null) return thisExpression;
|
||||
return parentContext != null ? parentContext.getThisExpression() : null;
|
||||
}
|
||||
@@ -72,7 +74,7 @@ public class ClassContext {
|
||||
public ClassContext intoClosure(String internalClassName) {
|
||||
final DeclarationDescriptor contextDescriptor = getContextClass();
|
||||
StackValue outerClass = contextDescriptor instanceof ClassDescriptor
|
||||
? StackValue.instanceField(JetTypeMapper.jetImplementationType((ClassDescriptor) contextDescriptor), internalClassName, "this$0")
|
||||
? StackValue.instanceField(JetTypeMapper.jetInterfaceType((ClassDescriptor) contextDescriptor), internalClassName, "this$0")
|
||||
: StackValue.local(0, JetTypeMapper.TYPE_OBJECT);
|
||||
return new ClassContext(null, OwnerKind.IMPLEMENTATION, outerClass, this);
|
||||
}
|
||||
@@ -108,7 +110,7 @@ public class ClassContext {
|
||||
return mapper.jvmType((ClassDescriptor) contextType, contextKind);
|
||||
}
|
||||
else {
|
||||
return JetTypeMapper.TYPE_OBJECT; // TODO?
|
||||
return parentContext != null ? parentContext.jvmType(mapper) : JetTypeMapper.TYPE_OBJECT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,4 +121,8 @@ public class ClassContext {
|
||||
final ClassContext parent = getParentContext();
|
||||
return parent != null ? parent.getContextClass() : null;
|
||||
}
|
||||
|
||||
public boolean isThisWasUsed() {
|
||||
return thisWasUsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteral;
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
@@ -110,13 +107,17 @@ public class ClosureCodegen {
|
||||
|
||||
|
||||
generateBridge(name, funDescriptor, cv);
|
||||
generateBody(funDescriptor, cv, fun.getFunctionLiteral());
|
||||
final boolean captureThis = generateBody(funDescriptor, cv, fun.getFunctionLiteral());
|
||||
|
||||
final Method constructor = generateConstructor(funClass);
|
||||
final Method constructor = generateConstructor(funClass, captureThis);
|
||||
|
||||
if (captureThis) {
|
||||
cv.visitField(Opcodes.ACC_PRIVATE, "this$0", enclosingClassType().getDescriptor(), null, null);
|
||||
}
|
||||
|
||||
cv.visitEnd();
|
||||
|
||||
final GeneratedAnonymousClassDescriptor answer = new GeneratedAnonymousClassDescriptor(name, constructor);
|
||||
final GeneratedAnonymousClassDescriptor answer = new GeneratedAnonymousClassDescriptor(name, constructor, captureThis);
|
||||
for (DeclarationDescriptor descriptor : closure.keySet()) {
|
||||
final EnclosedValueDescriptor valueDescriptor = closure.get(descriptor);
|
||||
answer.addArg(valueDescriptor.getOuterValue());
|
||||
@@ -124,9 +125,15 @@ public class ClosureCodegen {
|
||||
return answer;
|
||||
}
|
||||
|
||||
private void generateBody(FunctionDescriptor funDescriptor, ClassVisitor cv, JetFunctionLiteral body) {
|
||||
FunctionCodegen fc = new FunctionCodegen(context.intoClosure(name), cv, state);
|
||||
private Type enclosingClassType() {
|
||||
return Type.getObjectType(state.getTypeMapper().jvmName((ClassDescriptor) context.getContextClass(), OwnerKind.INTERFACE));
|
||||
}
|
||||
|
||||
private boolean generateBody(FunctionDescriptor funDescriptor, ClassVisitor cv, JetFunctionLiteral body) {
|
||||
final ClassContext closureContext = context.intoClosure(name);
|
||||
FunctionCodegen fc = new FunctionCodegen(closureContext, cv, state);
|
||||
fc.generateMethod(body, invokeSignature(funDescriptor), funDescriptor);
|
||||
return closureContext.isThisWasUsed();
|
||||
}
|
||||
|
||||
private void generateBridge(String className, FunctionDescriptor funDescriptor, ClassVisitor cv) {
|
||||
@@ -164,9 +171,22 @@ public class ClosureCodegen {
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private Method generateConstructor(String funClass) {
|
||||
Type[] argTypes = new Type[closure.size()];
|
||||
private Method generateConstructor(String funClass, boolean captureThis) {
|
||||
int argCount = closure.size();
|
||||
|
||||
if (captureThis) {
|
||||
argCount++;
|
||||
}
|
||||
|
||||
Type[] argTypes = new Type[argCount];
|
||||
|
||||
|
||||
int i = 0;
|
||||
if (captureThis) {
|
||||
i = 1;
|
||||
argTypes[0] = enclosingClassType();
|
||||
}
|
||||
|
||||
for (DeclarationDescriptor descriptor : closure.keySet()) {
|
||||
argTypes[i++] = state.getTypeMapper().mapType(((VariableDescriptor) descriptor).getOutType());
|
||||
}
|
||||
@@ -179,11 +199,21 @@ public class ClosureCodegen {
|
||||
iv.load(0, Type.getObjectType(funClass));
|
||||
iv.invokespecial(funClass, "<init>", "()V");
|
||||
|
||||
for (int j = 0; j < argTypes.length; j++) {
|
||||
Type type = argTypes[j];
|
||||
i = 1;
|
||||
for (Type type : argTypes) {
|
||||
StackValue.local(0, JetTypeMapper.TYPE_OBJECT).put(JetTypeMapper.TYPE_OBJECT, iv);
|
||||
StackValue.local(j + 1, type).put(type, iv);
|
||||
StackValue.field(type, name, "$" + (j + 1), false).store(iv);
|
||||
StackValue.local(i, type).put(type, iv);
|
||||
final String fieldName;
|
||||
if (captureThis && i == 1) {
|
||||
fieldName = "this$0";
|
||||
captureThis = false;
|
||||
}
|
||||
else {
|
||||
fieldName = "$" + (i);
|
||||
i++;
|
||||
}
|
||||
|
||||
StackValue.field(type, name, fieldName, false).store(iv);
|
||||
}
|
||||
|
||||
iv.visitInsn(Opcodes.RETURN);
|
||||
|
||||
@@ -508,6 +508,10 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
|
||||
final Method cons = closure.getConstructor();
|
||||
|
||||
if (closure.isCaptureThis()) {
|
||||
thisToStack();
|
||||
}
|
||||
|
||||
for (int i = 0; i < closure.getArgs().size(); i++) {
|
||||
StackValue arg = closure.getArgs().get(i);
|
||||
arg.put(cons.getArgumentTypes()[i], v);
|
||||
|
||||
@@ -11,11 +11,13 @@ import java.util.List;
|
||||
public class GeneratedAnonymousClassDescriptor {
|
||||
private final String classname;
|
||||
private Method constructor;
|
||||
private final boolean captureThis;
|
||||
private List<StackValue> args = new ArrayList<StackValue>();
|
||||
|
||||
public GeneratedAnonymousClassDescriptor(String classname, Method constructor) {
|
||||
public GeneratedAnonymousClassDescriptor(String classname, Method constructor, boolean captureThis) {
|
||||
this.classname = classname;
|
||||
this.constructor = constructor;
|
||||
this.captureThis = captureThis;
|
||||
}
|
||||
|
||||
public String getClassname() {
|
||||
@@ -33,4 +35,8 @@ public class GeneratedAnonymousClassDescriptor {
|
||||
public List<StackValue> getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public boolean isCaptureThis() {
|
||||
return captureThis;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class GenerationState {
|
||||
final ClassContext objectContext = classContext.intoClass(getBindingContext().getClassDescriptor(literal.getObjectDeclaration()), OwnerKind.IMPLEMENTATION);
|
||||
|
||||
new ImplementationBodyCodegen(literal.getObjectDeclaration(), objectContext, nameAndVisitor.getSecond(), this).generate();
|
||||
return new GeneratedAnonymousClassDescriptor(nameAndVisitor.first, new Method("<init>", "()V"));
|
||||
return new GeneratedAnonymousClassDescriptor(nameAndVisitor.first, new Method("<init>", "()V"), false);
|
||||
}
|
||||
|
||||
public StackValue lookupInContext(DeclarationDescriptor d) {
|
||||
|
||||
Reference in New Issue
Block a user