From 4cf00a8548babaa5a16ff4d39373f1e9a03b51a7 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Wed, 6 Jul 2011 17:37:01 +0400 Subject: [PATCH] Capture this in closures --- .../jetbrains/jet/codegen/ClassContext.java | 10 +++- .../jetbrains/jet/codegen/ClosureCodegen.java | 60 ++++++++++++++----- .../jet/codegen/ExpressionCodegen.java | 4 ++ .../GeneratedAnonymousClassDescriptor.java | 8 ++- .../jet/codegen/GenerationState.java | 2 +- 5 files changed, 65 insertions(+), 19 deletions(-) diff --git a/idea/src/org/jetbrains/jet/codegen/ClassContext.java b/idea/src/org/jetbrains/jet/codegen/ClassContext.java index 78602b96547..2559082cd47 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClassContext.java +++ b/idea/src/org/jetbrains/jet/codegen/ClassContext.java @@ -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; + } } diff --git a/idea/src/org/jetbrains/jet/codegen/ClosureCodegen.java b/idea/src/org/jetbrains/jet/codegen/ClosureCodegen.java index 41be927b6a4..f21a5e12442 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClosureCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ClosureCodegen.java @@ -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, "", "()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); diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 07d5a41d460..967b2e841e8 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -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); diff --git a/idea/src/org/jetbrains/jet/codegen/GeneratedAnonymousClassDescriptor.java b/idea/src/org/jetbrains/jet/codegen/GeneratedAnonymousClassDescriptor.java index f02567b8802..14e70f0ef96 100644 --- a/idea/src/org/jetbrains/jet/codegen/GeneratedAnonymousClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/codegen/GeneratedAnonymousClassDescriptor.java @@ -11,11 +11,13 @@ import java.util.List; public class GeneratedAnonymousClassDescriptor { private final String classname; private Method constructor; + private final boolean captureThis; private List args = new ArrayList(); - 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 getArgs() { return args; } + + public boolean isCaptureThis() { + return captureThis; + } } diff --git a/idea/src/org/jetbrains/jet/codegen/GenerationState.java b/idea/src/org/jetbrains/jet/codegen/GenerationState.java index 3c2308ef79c..198f3b69d3f 100644 --- a/idea/src/org/jetbrains/jet/codegen/GenerationState.java +++ b/idea/src/org/jetbrains/jet/codegen/GenerationState.java @@ -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("", "()V")); + return new GeneratedAnonymousClassDescriptor(nameAndVisitor.first, new Method("", "()V"), false); } public StackValue lookupInContext(DeclarationDescriptor d) {