From baea660a0b27ebbdaf9cd6fe1311fa6c895b2cfc Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 16 Jan 2015 16:18:39 +0300 Subject: [PATCH] Rewrite hack in codegen for lambdas in anonymous object super calls Synthetic ClassDescriptor created in codegen for lambdas and such should have correct container, because it'll be used in generation of InnerClasses --- .../binding/CodegenAnnotatingVisitor.java | 149 +++++++++--------- .../lambda/lambdaInLocalClassConstructor.kt | 26 +++ ...lass.kt => lambdaInLocalClassSuperCall.kt} | 2 +- .../lambda/lambdaInObjectExpression.kt | 13 +- .../lambda/lambdaInObjectLiteralSuperCall.kt | 21 +++ ...lackBoxWithStdlibCodegenTestGenerated.java | 18 ++- 6 files changed, 144 insertions(+), 85 deletions(-) create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassConstructor.kt rename compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/{lambdaInLocalClass.kt => lambdaInLocalClassSuperCall.kt} (99%) create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectLiteralSuperCall.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 8181c3f2ecc..ff9a3d1fb9d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -63,34 +63,12 @@ import static org.jetbrains.kotlin.resolve.BindingContext.*; class CodegenAnnotatingVisitor extends JetVisitorVoid { private static final TokenSet BINARY_OPERATIONS = TokenSet.orSet( AUGMENTED_ASSIGNMENTS, - TokenSet.create(PLUS, MINUS, MUL, DIV, PERC, RANGE, LT, GT, LTEQ, GTEQ, IDENTIFIER)); - - private static class ClassDescriptorWithState { - - private boolean isDelegationToSuperCall; - - private final ClassDescriptor descriptor; - - public ClassDescriptorWithState(ClassDescriptor descriptor) { - this.descriptor = descriptor; - } - - public boolean isDelegationToSuperCall() { - return isDelegationToSuperCall; - } - - public void setDelegationToSuperCall(boolean isDelegationToSuperCall) { - this.isDelegationToSuperCall = isDelegationToSuperCall; - } - - public ClassDescriptor getDescriptor() { - return descriptor; - } - } + TokenSet.create(PLUS, MINUS, MUL, DIV, PERC, RANGE, LT, GT, LTEQ, GTEQ, IDENTIFIER) + ); private final Map anonymousSubclassesCount = new HashMap(); - private final Stack classStack = new Stack(); + private final Stack classStack = new Stack(); private final Stack nameStack = new Stack(); private final BindingTrace bindingTrace; @@ -114,7 +92,10 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { ) { String simpleName = name.substring(name.lastIndexOf('/') + 1); ClassDescriptorImpl classDescriptor = new ClassDescriptorImpl( - funDescriptor.getContainingDeclaration(), Name.special(""), Modality.FINAL, supertypes, + correctContainerForLambda(funDescriptor, element), + Name.special(""), + Modality.FINAL, + supertypes, SourcePackage.toSourceElement(element) ); classDescriptor.initialize(JetScope.Empty.INSTANCE$, Collections.emptySet(), null); @@ -123,6 +104,39 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { return classDescriptor; } + @NotNull + @SuppressWarnings("ConstantConditions") + private DeclarationDescriptor correctContainerForLambda(@NotNull FunctionDescriptor descriptor, @NotNull JetElement function) { + DeclarationDescriptor container = descriptor.getContainingDeclaration(); + + // In almost all cases the function's direct container is the correct container to consider in JVM back-end + // (and subsequently to write to EnclosingMethod and InnerClasses attributes). + // The only exceptional case is when a lambda is declared in the super call of an anonymous object: + // in this case it's constructed in the outer code, despite being located under the object PSI- and descriptor-wise + // TODO: consider the possibility of fixing this in the compiler front-end + + if (container instanceof ConstructorDescriptor && DescriptorUtils.isAnonymousObject(container.getContainingDeclaration())) { + PsiElement element = function; + while (element != null) { + PsiElement child = element; + element = element.getParent(); + + if (bindingContext.get(DECLARATION_TO_DESCRIPTOR, element) == container) return container; + + if (element instanceof JetObjectDeclaration && + element.getParent() instanceof JetObjectLiteralExpression && + child instanceof JetDelegationSpecifierList) { + // If we're passing an anonymous object's super call, it means "container" is ConstructorDescriptor of that object. + // To reach outer context, we should call getContainingDeclaration() twice + // TODO: this is probably not entirely correct, mostly because DECLARATION_TO_DESCRIPTOR can return null + container = container.getContainingDeclaration().getContainingDeclaration(); + } + } + } + + return container; + } + private String inventAnonymousClassName(JetElement declaration) { String top = peekFromStack(nameStack); Integer cnt = anonymousSubclassesCount.get(top); @@ -160,7 +174,7 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { // SCRIPT: should be replaced with VisitScript override //noinspection ConstantConditions ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, bindingContext.get(SCRIPT, file.getScript())); - pushClassDescriptor(classDescriptor); + classStack.push(classDescriptor); //noinspection ConstantConditions nameStack.push(asmTypeForScriptPsi(bindingContext, file.getScript()).getInternalName()); } @@ -170,7 +184,7 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { file.acceptChildren(this); nameStack.pop(); if (file.isScript()) { - popClassDescriptor(); + classStack.pop(); } } @@ -201,16 +215,16 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { String name = peekFromStack(nameStack) + JvmAbi.CLASS_OBJECT_SUFFIX; recordClosure(classDescriptor, name); - pushClassDescriptor(classDescriptor); + classStack.push(classDescriptor); nameStack.push(name); super.visitClassObject(classObject); nameStack.pop(); - popClassDescriptor(); + classStack.pop(); } @Override public void visitObjectDeclaration(@NotNull JetObjectDeclaration declaration) { - if (declaration.getParent() instanceof JetObjectLiteralExpression || declaration.getParent() instanceof JetClassObject) { + if (declaration.getParent() instanceof JetClassObject) { super.visitObjectDeclaration(declaration); } else { @@ -223,11 +237,11 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { String name = getName(classDescriptor); recordClosure(classDescriptor, name); - pushClassDescriptor(classDescriptor); + classStack.push(classDescriptor); nameStack.push(name); super.visitObjectDeclaration(declaration); nameStack.pop(); - popClassDescriptor(); + classStack.pop(); } } @@ -242,11 +256,11 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { String name = getName(classDescriptor); recordClosure(classDescriptor, name); - pushClassDescriptor(classDescriptor); + classStack.push(classDescriptor); nameStack.push(name); super.visitClass(klass); nameStack.pop(); - popClassDescriptor(); + classStack.pop(); } private String getName(ClassDescriptor classDescriptor) { @@ -258,21 +272,30 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { @Override public void visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression) { - ClassDescriptor classDescriptor = bindingContext.get(CLASS, expression.getObjectDeclaration()); + JetObjectDeclaration object = expression.getObjectDeclaration(); + ClassDescriptor classDescriptor = bindingContext.get(CLASS, object); if (classDescriptor == null) { // working around a problem with shallow analysis super.visitObjectLiteralExpression(expression); return; } - String name = inventAnonymousClassName(expression.getObjectDeclaration()); + String name = inventAnonymousClassName(object); recordClosure(classDescriptor, name); - pushClassDescriptor(classDescriptor); + JetDelegationSpecifierList delegationSpecifierList = object.getDelegationSpecifierList(); + if (delegationSpecifierList != null) { + delegationSpecifierList.accept(this); + } + + classStack.push(classDescriptor); nameStack.push(CodegenBinding.getAsmType(bindingContext, classDescriptor).getInternalName()); - super.visitObjectLiteralExpression(expression); + JetClassBody body = object.getBody(); + if (body != null) { + super.visitClassBody(body); + } nameStack.pop(); - popClassDescriptor(); + classStack.pop(); } @Override @@ -288,42 +311,13 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { ClassDescriptor classDescriptor = recordClassForFunction(functionLiteral, functionDescriptor, supertypes, name); recordClosure(classDescriptor, name); - pushClassDescriptor(classDescriptor); + classStack.push(classDescriptor); nameStack.push(name); super.visitFunctionLiteralExpression(expression); nameStack.pop(); - popClassDescriptor(); - } - - private void pushClassDescriptor(ClassDescriptor classDescriptor) { - classStack.push(new ClassDescriptorWithState(classDescriptor)); - } - - private void popClassDescriptor() { classStack.pop(); } - private ClassDescriptor getOuterClassDescriptor() { - ListIterator iterator = classStack.listIterator(classStack.size()); - while(iterator.hasPrevious()) { - ClassDescriptorWithState previous = iterator.previous(); - if (!previous.isDelegationToSuperCall()) {//find last not delegated to super call - return previous.getDescriptor(); - } - } - return null; - } - - @Override - public void visitDelegationToSuperCallSpecifier(@NotNull JetDelegatorToSuperCall call) { - ClassDescriptorWithState state = peekFromStack(classStack); - // working around a problem with shallow analysis - if (state == null) return; - state.setDelegationToSuperCall(true); - super.visitDelegationToSuperCallSpecifier(call); - state.setDelegationToSuperCall(false); - } - @Override public void visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression) { FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression); @@ -339,16 +333,15 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { ClassDescriptor classDescriptor = recordClassForFunction(expression, functionDescriptor, supertypes, name); recordClosure(classDescriptor, name); - pushClassDescriptor(classDescriptor); + classStack.push(classDescriptor); nameStack.push(name); super.visitCallableReferenceExpression(expression); nameStack.pop(); - popClassDescriptor(); + classStack.pop(); } - private void recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) { - CodegenBinding.recordClosure(bindingTrace, classDescriptor, getOuterClassDescriptor(), Type.getObjectType(name)); + CodegenBinding.recordClosure(bindingTrace, classDescriptor, peekFromStack(classStack), Type.getObjectType(name)); } @Override @@ -386,11 +379,11 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { ClassDescriptor classDescriptor = recordClassForFunction(function, functionDescriptor, supertypes, name); recordClosure(classDescriptor, name); - pushClassDescriptor(classDescriptor); + classStack.push(classDescriptor); nameStack.push(name); super.visitNamedFunction(function); nameStack.pop(); - popClassDescriptor(); + classStack.pop(); } } @@ -547,9 +540,9 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { @NotNull private String getCurrentTopLevelClassOrPackagePartInternalName(@NotNull JetFile file) { - ListIterator iterator = classStack.listIterator(classStack.size()); + ListIterator iterator = classStack.listIterator(classStack.size()); while (iterator.hasPrevious()) { - ClassDescriptor previous = iterator.previous().getDescriptor(); + ClassDescriptor previous = iterator.previous(); if (DescriptorUtils.isTopLevelOrInnerClass(previous)) { return CodegenBinding.getAsmType(bindingContext, previous).getInternalName(); } diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassConstructor.kt b/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassConstructor.kt new file mode 100644 index 00000000000..b67711c1198 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassConstructor.kt @@ -0,0 +1,26 @@ +open class C + +fun box(): String { + class L : C() { + val a: Any + + { + a = {} + } + } + val l = L() + + val javaClass = l.a.javaClass + val enclosingMethod = javaClass.getEnclosingConstructor()!!.getName() + if (!enclosingMethod.startsWith("_DefaultPackage\$") || !enclosingMethod.endsWith("\$box\$L")) return "ctor: $enclosingMethod" + + val enclosingClass = javaClass.getEnclosingClass()!!.getName() + if (!enclosingClass.startsWith("_DefaultPackage\$") || !enclosingClass.endsWith("\$box\$L")) return "enclosing class: $enclosingClass" + + if (enclosingMethod != enclosingClass) return "$enclosingClass != $enclosingMethod" + + val declaringClass = javaClass.getDeclaringClass() + if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass" + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClass.kt b/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassSuperCall.kt similarity index 99% rename from compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClass.kt rename to compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassSuperCall.kt index 458bdfe0b72..55c2373f93e 100644 --- a/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClass.kt +++ b/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassSuperCall.kt @@ -18,4 +18,4 @@ fun box(): String { if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass" return "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectExpression.kt b/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectExpression.kt index 116ffc4aa3b..f5ec547b225 100644 --- a/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectExpression.kt +++ b/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectExpression.kt @@ -1,7 +1,14 @@ -open class C(val a: Any) +trait C { + val a: Any +} fun box(): String { - val l = object : C({}) { + val l = object : C { + override val a: Any + + { + a = {} + } } val javaClass = l.a.javaClass @@ -17,4 +24,4 @@ fun box(): String { if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass" return "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectLiteralSuperCall.kt b/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectLiteralSuperCall.kt new file mode 100644 index 00000000000..b3a032c9572 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectLiteralSuperCall.kt @@ -0,0 +1,21 @@ +open class C(val a: Any) + +fun box(): String { + val l = object : C({}) { + } + + val javaClass = l.a.javaClass + if (javaClass.getEnclosingConstructor() != null) return "ctor should be null" + + val enclosingMethod = javaClass.getEnclosingMethod()!!.getName() + if (enclosingMethod != "box") return "method: $enclosingMethod" + + val enclosingClass = javaClass.getEnclosingClass()!!.getName() + if (!enclosingClass.startsWith("_DefaultPackage\$") || enclosingClass != l.javaClass.getEnclosingClass()!!.getName()) + return "enclosing class: $enclosingClass" + + val declaringClass = javaClass.getDeclaringClass() + if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass" + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index b4db5bc09b7..973672450b5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2504,9 +2504,15 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } - @TestMetadata("lambdaInLocalClass.kt") - public void testLambdaInLocalClass() throws Exception { - String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClass.kt"); + @TestMetadata("lambdaInLocalClassConstructor.kt") + public void testLambdaInLocalClassConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassConstructor.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("lambdaInLocalClassSuperCall.kt") + public void testLambdaInLocalClassSuperCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInLocalClassSuperCall.kt"); doTestWithStdlib(fileName); } @@ -2540,6 +2546,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("lambdaInObjectLiteralSuperCall.kt") + public void testLambdaInObjectLiteralSuperCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInObjectLiteralSuperCall.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("lambdaInPackage.kt") public void testLambdaInPackage() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/enclosing/lambda/lambdaInPackage.kt");