Capture this in closures

This commit is contained in:
Maxim Shafirov
2011-07-06 17:37:01 +04:00
parent 0c85c53c64
commit 4cf00a8548
5 changed files with 65 additions and 19 deletions
@@ -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) {