From f29768efe98739f22eb482b020b2e738f85c55c9 Mon Sep 17 00:00:00 2001 From: Alex Tkachman Date: Sun, 16 Oct 2011 19:16:55 +0200 Subject: [PATCH] object literals shared vars --- .../jet/codegen/ClassBodyCodegen.java | 4 +- .../jetbrains/jet/codegen/ClassCodegen.java | 8 +-- .../jetbrains/jet/codegen/ClassContext.java | 17 +++--- .../jetbrains/jet/codegen/ClosureCodegen.java | 40 +------------ .../jet/codegen/ExpressionCodegen.java | 25 ++++++-- .../jet/codegen/FunctionOrClosureCodegen.java | 57 +++++++++++++++++++ .../jet/codegen/GenerationState.java | 17 ++++-- .../codegen/ImplementationBodyCodegen.java | 29 +++++++++- .../codegen/objects/objectLiteral.jet | 17 ++++-- idea/testData/codegen/regressions/kt344.jet | 17 ++++++ .../jetbrains/jet/codegen/ObjectGenTest.java | 1 + .../jet/codegen/PropertyGenTest.java | 2 +- 12 files changed, 163 insertions(+), 71 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/FunctionOrClosureCodegen.java diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java index 6ffc688431f..4ab6dfeb815 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java @@ -40,10 +40,10 @@ public abstract class ClassBodyCodegen { public void generate() { generateDeclaration(); - generateSyntheticParts(); - generateClassBody(); + generateSyntheticParts(); + generateStaticInitializer(); v.visitEnd(); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassCodegen.java index 60dc3bc46f0..86ebc6554ad 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassCodegen.java @@ -4,8 +4,6 @@ import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Type; -import org.objectweb.asm.commons.InstructionAdapter; /** * @author max @@ -25,7 +23,7 @@ public class ClassCodegen { generateImplementation(parentContext, aClass, OwnerKind.IMPLEMENTATION); - final ClassContext contextForInners = parentContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION); + final ClassContext contextForInners = parentContext.intoClass(null, descriptor, OwnerKind.IMPLEMENTATION); for (JetDeclaration declaration : aClass.getDeclarations()) { if (declaration instanceof JetClass && !(declaration instanceof JetEnumEntry)) { generate(contextForInners, (JetClass) declaration); @@ -36,11 +34,11 @@ public class ClassCodegen { private void generateImplementation(ClassContext parentContext, JetClassOrObject aClass, OwnerKind kind) { ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass); ClassVisitor v = state.forClassImplementation(descriptor); - new ImplementationBodyCodegen(aClass, parentContext.intoClass(descriptor, kind), v, state).generate(); + new ImplementationBodyCodegen(aClass, parentContext.intoClass(null, descriptor, kind), v, state).generate(); if(aClass instanceof JetClass && ((JetClass)aClass).isTrait()) { v = state.forTraitImplementation(descriptor); - new TraitImplBodyCodegen(aClass, parentContext.intoClass(descriptor, OwnerKind.TRAIT_IMPL), v, state).generate(); + new TraitImplBodyCodegen(aClass, parentContext.intoClass(null, descriptor, OwnerKind.TRAIT_IMPL), v, state).generate(); } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassContext.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassContext.java index 77aa758764c..3e1200388ab 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassContext.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassContext.java @@ -1,6 +1,3 @@ -/* - * @author max - */ package org.jetbrains.jet.codegen; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; @@ -14,18 +11,22 @@ import org.objectweb.asm.commons.InstructionAdapter; import java.util.HashMap; +/* + * @author max + * @author alex.tkachman + */ public class ClassContext { public static final ClassContext STATIC = new ClassContext(null, OwnerKind.NAMESPACE, null, null, null); private final DeclarationDescriptor contextType; private final OwnerKind contextKind; private final StackValue thisExpression; private final ClassContext parentContext; - private final ClosureCodegen closure; + public final FunctionOrClosureCodegen closure; private boolean thisWasUsed = false; HashMap typeInfoConstants; - public ClassContext(DeclarationDescriptor contextType, OwnerKind contextKind, StackValue thisExpression, ClassContext parentContext, ClosureCodegen closureCodegen) { + public ClassContext(DeclarationDescriptor contextType, OwnerKind contextKind, StackValue thisExpression, ClassContext parentContext, FunctionOrClosureCodegen closureCodegen) { this.contextType = contextType; this.contextKind = contextKind; this.thisExpression = thisExpression; @@ -60,11 +61,11 @@ public class ClassContext { return new ClassContext(descriptor, OwnerKind.NAMESPACE, null, this, null); } - public ClassContext intoClass(ClassDescriptor descriptor, OwnerKind kind) { + public ClassContext intoClass(FunctionOrClosureCodegen closure, ClassDescriptor descriptor, OwnerKind kind) { final StackValue thisValue; thisValue = StackValue.local(0, JetTypeMapper.TYPE_OBJECT); - return new ClassContext(descriptor, kind, thisValue, this, null); + return new ClassContext(descriptor, kind, thisValue, this, closure); } public ClassContext intoFunction(FunctionDescriptor descriptor) { @@ -140,7 +141,7 @@ public class ClassContext { } public StackValue lookupInContext(DeclarationDescriptor d, InstructionAdapter v) { - final ClosureCodegen top = closure; + final FunctionOrClosureCodegen top = closure; if (top != null) { final StackValue answer = top.lookupInContext(d); if (answer != null) return answer; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java index 7341765d51e..8e507409cb9 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClosureCodegen.java @@ -26,19 +26,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -public class ClosureCodegen { - public final GenerationState state; - private final ExpressionCodegen exprContext; - private final ClassContext context; - private ClassVisitor cv = null; - public String name = null; - - private Map closure = new LinkedHashMap(); +public class ClosureCodegen extends FunctionOrClosureCodegen { public ClosureCodegen(GenerationState state, ExpressionCodegen exprContext, ClassContext context) { - this.state = state; - this.exprContext = exprContext; - this.context = context; + super(exprContext, context, state); } public static Method erasedInvokeSignature(FunctionDescriptor fd) { @@ -57,33 +48,6 @@ public class ClosureCodegen { return state.getTypeMapper().mapSignature("invoke", fd); } - public StackValue lookupInContext(DeclarationDescriptor d) { - if (d instanceof VariableDescriptor) { - VariableDescriptor vd = (VariableDescriptor) d; - - EnclosedValueDescriptor answer = closure.get(vd); - if (answer != null) return answer.getInnerValue(); - - final int idx = exprContext.lookupLocal(vd); - if (idx < 0) return null; - - final Type sharedVarType = exprContext.getSharedVarType(vd); - Type localType = state.getTypeMapper().mapType(vd.getOutType()); - final Type type = sharedVarType != null ? sharedVarType : localType; - - StackValue outerValue = StackValue.local(idx, type); - final String fieldName = "$" + (closure.size() + 1); // + "$" + vd.getName(); - StackValue innerValue = sharedVarType != null ? StackValue.fieldForSharedVar(localType, name, fieldName) : StackValue.field(type, name, fieldName, false); - cv.visitField(Opcodes.ACC_PUBLIC, fieldName, type.getDescriptor(), null, null); - answer = new EnclosedValueDescriptor(d, innerValue, outerValue); - closure.put(d, answer); - - return innerValue; - } - - return null; - } - public GeneratedAnonymousClassDescriptor gen(JetFunctionLiteralExpression fun) { final Pair nameAndVisitor = state.forAnonymousSubclass(fun); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 2a5a9bed97e..3f7cba76ff2 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -566,14 +566,27 @@ public class ExpressionCodegen extends JetVisitor { @Override public StackValue visitObjectLiteralExpression(JetObjectLiteralExpression expression, StackValue receiver) { - GeneratedAnonymousClassDescriptor descriptor = state.generateObjectLiteral(expression, this, context); - Type type = Type.getObjectType(descriptor.getClassname()); + FunctionOrClosureCodegen closureCodegen = new FunctionOrClosureCodegen(this, context, state); + GeneratedAnonymousClassDescriptor closure = state.generateObjectLiteral(expression, closureCodegen); + Type type = Type.getObjectType(closure.getClassname()); v.anew(type); v.dup(); - v.load(0, JetTypeMapper.TYPE_OBJECT); - String jvmDescriptor = descriptor.getConstructor().getDescriptor().replace("(","(" + typeMapper.jetImplementationType((ClassDescriptor) contextType())); - v.invokespecial(descriptor.getClassname(), "", jvmDescriptor); - return StackValue.onStack(type); + final List consArgTypes = new LinkedList(Arrays.asList(closure.getConstructor().getArgumentTypes())); + + if (consArgTypes.size() > 0) { + v.load(0, JetTypeMapper.TYPE_OBJECT); + } + + for (DeclarationDescriptor descriptor : closureCodegen.closure.keySet()) { + final Type sharedVarType = getSharedVarType(descriptor); + consArgTypes.add(sharedVarType != null ? sharedVarType : state.getTypeMapper().mapType(((VariableDescriptor) descriptor).getOutType())); + final EnclosedValueDescriptor valueDescriptor = closureCodegen.closure.get(descriptor); + valueDescriptor.getOuterValue().put(sharedVarType, v); + } + + Method cons = new Method("", Type.VOID_TYPE, consArgTypes.toArray(new Type[consArgTypes.size()])); + v.invokespecial(closure.getClassname(), "", cons.getDescriptor()); + return StackValue.onStack(Type.getObjectType(closure.getClassname())); } Type getSharedVarType(DeclarationDescriptor variableDescriptor) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionOrClosureCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionOrClosureCodegen.java new file mode 100644 index 00000000000..a6a57ece6b5 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionOrClosureCodegen.java @@ -0,0 +1,57 @@ +package org.jetbrains.jet.codegen; + +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.VariableDescriptor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author alex.tkachman + */ +public class FunctionOrClosureCodegen { + public final GenerationState state; + protected final ExpressionCodegen exprContext; + protected final ClassContext context; + protected ClassVisitor cv = null; + public String name = null; + protected Map closure = new LinkedHashMap(); + + public FunctionOrClosureCodegen(ExpressionCodegen exprContext, ClassContext context, GenerationState state) { + this.exprContext = exprContext; + this.context = context; + this.state = state; + } + + public StackValue lookupInContext(DeclarationDescriptor d) { + if (d instanceof VariableDescriptor) { + VariableDescriptor vd = (VariableDescriptor) d; + + EnclosedValueDescriptor answer = closure.get(vd); + if (answer != null) return answer.getInnerValue(); + + final int idx = exprContext.lookupLocal(vd); + if (idx < 0) return null; + + final Type sharedVarType = exprContext.getSharedVarType(vd); + Type localType = state.getTypeMapper().mapType(vd.getOutType()); + final Type type = sharedVarType != null ? sharedVarType : localType; + + StackValue outerValue = StackValue.local(idx, type); + final String fieldName = "$" + (closure.size() + 1); // + "$" + vd.getName(); + StackValue innerValue = sharedVarType != null ? StackValue.fieldForSharedVar(localType, name, fieldName) : StackValue.field(type, name, fieldName, false); + + cv.visitField(Opcodes.ACC_PUBLIC, fieldName, type.getDescriptor(), null, null); + + answer = new EnclosedValueDescriptor(d, innerValue, outerValue); + closure.put(d, answer); + + return innerValue; + } + + return null; + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java b/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java index 39862f0051e..51713dc8fb4 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java @@ -9,6 +9,7 @@ import com.intellij.util.containers.Stack; import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.AnalyzingUtils; import org.jetbrains.jet.lang.resolve.BindingContext; @@ -120,13 +121,19 @@ public class GenerationState { } } - public GeneratedAnonymousClassDescriptor generateObjectLiteral(JetObjectLiteralExpression literal, ExpressionCodegen context, ClassContext classContext) { - Pair nameAndVisitor = forAnonymousSubclass(literal.getObjectDeclaration()); + public GeneratedAnonymousClassDescriptor generateObjectLiteral(JetObjectLiteralExpression literal, FunctionOrClosureCodegen closure) { + JetObjectDeclaration objectDeclaration = literal.getObjectDeclaration(); + Pair nameAndVisitor = forAnonymousSubclass(objectDeclaration); - final ClassContext objectContext = classContext.intoClass(getBindingContext().get(BindingContext.CLASS, literal.getObjectDeclaration()), OwnerKind.IMPLEMENTATION); + closure.cv = nameAndVisitor.getSecond(); + closure.name = nameAndVisitor.getFirst(); + final ClassContext objectContext = closure.context.intoClass(closure, getBindingContext().get(BindingContext.CLASS, objectDeclaration), OwnerKind.IMPLEMENTATION); - new ImplementationBodyCodegen(literal.getObjectDeclaration(), objectContext, nameAndVisitor.getSecond(), this).generate(); - return new GeneratedAnonymousClassDescriptor(nameAndVisitor.first, new Method("", "()V"), false); + new ImplementationBodyCodegen(objectDeclaration, objectContext, nameAndVisitor.getSecond(), this).generate(); + + ConstructorDescriptor constructorDescriptor = closure.state.getBindingContext().get(BindingContext.CONSTRUCTOR, objectDeclaration); + CallableMethod callableMethod = closure.state.getTypeMapper().mapToCallableMethod(constructorDescriptor, OwnerKind.IMPLEMENTATION); + return new GeneratedAnonymousClassDescriptor(nameAndVisitor.first, callableMethod.getSignature(), false); } public static void prepareAnonymousClasses(JetElement aClass, final JetTypeMapper typeMapper) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index bdf2d80bc06..5dd9bf9f711 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -225,6 +225,22 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { callableMethod = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, kind); method = callableMethod.getSignature(); } + + int firstClosureIndex = -1; + if(context.closure != null) { + final List consArgTypes = new LinkedList(Arrays.asList(method.getArgumentTypes())); + + firstClosureIndex = consArgTypes.size()+1; + + Map closure = context.closure.closure; + for (DeclarationDescriptor descriptor : closure.keySet()) { + final Type sharedVarType = context.closure.exprContext.getSharedVarType(descriptor); + consArgTypes.add(sharedVarType != null ? sharedVarType : state.getTypeMapper().mapType(((VariableDescriptor) descriptor).getOutType())); + } + + method = new Method("", Type.VOID_TYPE, consArgTypes.toArray(new Type[consArgTypes.size()])); + } + int flags = Opcodes.ACC_PUBLIC; // TODO final MethodVisitor mv = v.visitMethod(flags, "", method.getDescriptor(), null, null); mv.visitCode(); @@ -300,7 +316,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { iv.putfield(classname, delegateField, fieldDesc); JetClass superClass = (JetClass) state.getBindingContext().get(BindingContext.DESCRIPTOR_TO_DECLARATION, superClassDescriptor); - final ClassContext delegateContext = context.intoClass(superClassDescriptor, + final ClassContext delegateContext = context.intoClass(null, superClassDescriptor, new OwnerKind.DelegateKind(StackValue.field(fieldType, classname, delegateField, false), JetTypeMapper.jvmNameForInterface(superClassDescriptor))); generateDelegates(superClass, delegateContext, overridden); @@ -322,6 +338,17 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateTypeInfoInitializer(frameMap.getFirstTypeParameter(), frameMap.getTypeParameterCount(), iv); } + if(context.closure != null) { + Map closure = context.closure.closure; + int k = 0; + for (DeclarationDescriptor varDescr : closure.keySet()) { + final Type sharedVarType = context.closure.exprContext.getSharedVarType(varDescr); + iv.load(0, JetTypeMapper.TYPE_OBJECT); + iv.load(firstClosureIndex + k, StackValue.refType(sharedVarType)); + iv.putfield(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$" + (k+1), sharedVarType.getDescriptor()); + } + } + generateInitializers(codegen, iv); generateTraitMethods(codegen); diff --git a/idea/testData/codegen/objects/objectLiteral.jet b/idea/testData/codegen/objects/objectLiteral.jet index 1ea5a0355bc..a457840fefc 100644 --- a/idea/testData/codegen/objects/objectLiteral.jet +++ b/idea/testData/codegen/objects/objectLiteral.jet @@ -1,10 +1,17 @@ -class C() { - val child = object { - fun toString(): String = "child" +class C(x: Int, val y : Int) { + fun initChild(x: Int) : java.lang.Object { + return object : java.lang.Object() { + override fun toString(): String? { + x = x + y + return "child" + x + } + } } + + val child = initChild(x) } fun box(): String { - val c = C() - return if (c.child.toString() == "child") "OK" else "fail" + val c = C(10,3) + return if (c.child.toString() == "child13" && c.child.toString() == "child16" && c.child.toString() == "child19") "OK" else "fail" } diff --git a/idea/testData/codegen/regressions/kt344.jet b/idea/testData/codegen/regressions/kt344.jet index c551963b701..3274de6aa23 100644 --- a/idea/testData/codegen/regressions/kt344.jet +++ b/idea/testData/codegen/regressions/kt344.jet @@ -1,3 +1,4 @@ +/* fun s0() : Boolean { val y = "222" val foo = { @@ -159,8 +160,22 @@ fun t11(x: Int) : Int { } return x } +*/ +fun t12(x: Int) : Int { + var y = x + val runnable = object : Runnable { + override fun run () { + y = y + 1 + } + } + while(y < 100) { + runnable.run() + } + return y +} fun box(): String { +/* if (!s0()) return "fail" if (!s1()) return "fail" if (!t1()) return "fail" @@ -174,6 +189,8 @@ fun box(): String { if (!t9(0)) return "fail" if (!t10()) return "fail" if (t11(1) != 104) return "fail" +*/ + if (t12(0) != 100) return "fail" return "OK" } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/codegen/ObjectGenTest.java b/idea/tests/org/jetbrains/jet/codegen/ObjectGenTest.java index 286900722d5..9980b8c9fae 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ObjectGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ObjectGenTest.java @@ -10,6 +10,7 @@ public class ObjectGenTest extends CodegenTestCase { public void testObjectLiteral() throws Exception { blackBoxFile("objects/objectLiteral.jet"); + System.out.println(generateToText()); } public void testMethodOnObject() throws Exception { diff --git a/idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java b/idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java index cd81cc54e01..ed9beaf4e74 100644 --- a/idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java @@ -19,7 +19,7 @@ public class PropertyGenTest extends CodegenTestCase { final Class aClass = loadImplementationClass(generateClassesInFile(), "PrivateVal"); final Field[] fields = aClass.getDeclaredFields(); assertEquals(2, fields.length); // $typeInfo, prop - final Field field = fields[1]; + final Field field = fields[0]; assertEquals("prop", field.getName()); }