Write outer class info for closures

This commit is contained in:
Mikhael Bogdanov
2014-05-26 13:05:36 +04:00
parent 9773015548
commit 69ef648005
20 changed files with 413 additions and 28 deletions
@@ -23,9 +23,12 @@ import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.java.*;
@@ -39,6 +42,7 @@ import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
import java.util.ArrayList;
import java.util.List;
@@ -707,4 +711,43 @@ public class AsmUtil {
public static Type asmTypeByFqNameWithoutInnerClasses(@NotNull FqName fqName) {
return Type.getObjectType(JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName());
}
public static void writeOuterClassAndEnclosingMethod(
@NotNull ClassDescriptor descriptor,
@NotNull DeclarationDescriptor originalDescriptor,
@NotNull JetTypeMapper typeMapper,
@NotNull ClassBuilder v
) {
String outerClassName = getOuterClassName(descriptor, originalDescriptor, typeMapper);
FunctionDescriptor function = DescriptorUtils.getParentOfType(descriptor, FunctionDescriptor.class);
if (function != null) {
Method method = typeMapper.mapSignature(function).getAsmMethod();
v.visitOuterClass(outerClassName, method.getName(), method.getDescriptor());
}
else {
v.visitOuterClass(outerClassName, null, null);
}
}
@NotNull
private static String getOuterClassName(@NotNull ClassDescriptor classDescriptor, @NotNull DeclarationDescriptor originalDescriptor, @NotNull JetTypeMapper typeMapper) {
DeclarationDescriptor container = classDescriptor.getContainingDeclaration();
while (container != null) {
if (container instanceof ClassDescriptor) {
return typeMapper.mapClass((ClassDescriptor)container).getInternalName();
} else if (CodegenBinding.isLocalFunOrLambda(container)) {
ClassDescriptor descriptor =
CodegenBinding.anonymousClassForFunction(typeMapper.getBindingContext(), (FunctionDescriptor) container);
return typeMapper.mapClass(descriptor).getInternalName();
}
container = container.getContainingDeclaration();
}
JetFile containingFile = BindingContextUtils.getContainingFile(typeMapper.getBindingContext(), originalDescriptor);
assert containingFile != null : "Containing file should be present for " + classDescriptor;
return PackageCodegen.getPackagePartInternalName(containingFile);
}
}
@@ -179,6 +179,7 @@ public class ClosureCodegen extends ParentCodegenAwareImpl {
null);
AsmUtil.writeOuterClassAndEnclosingMethod(anonymousClassForFunction(bindingContext, funDescriptor), funDescriptor, typeMapper, cv);
cv.done();
}
@@ -251,33 +251,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
!(parentDescriptor instanceof PackageFragmentDescriptor || parentDescriptor instanceof ClassDescriptor);
// Do not emit enclosing method in "light-classes mode" since currently we generate local light classes as if they're top level
if (isLocalOrAnonymousClass && state.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) {
String outerClassName = getOuterClassName(descriptor, typeMapper);
FunctionDescriptor function = AsmUtil.isDeclarationInsideInlineFunction(descriptor)
? null
: DescriptorUtils.getParentOfType(descriptor, FunctionDescriptor.class);
if (function != null) {
Method method = typeMapper.mapSignature(function).getAsmMethod();
v.visitOuterClass(outerClassName, method.getName(), method.getDescriptor());
}
else {
v.visitOuterClass(outerClassName, null, null);
}
writeOuterClassAndEnclosingMethod(descriptor, descriptor, typeMapper, v);
}
}
@NotNull
private static String getOuterClassName(@NotNull ClassDescriptor classDescriptor, @NotNull JetTypeMapper typeMapper) {
ClassDescriptor container = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class);
if (container != null) {
return typeMapper.mapClass(container).getInternalName();
}
JetFile containingFile = BindingContextUtils.getContainingFile(typeMapper.getBindingContext(), classDescriptor);
assert containingFile != null : "Containing file should be present for " + classDescriptor;
return PackageCodegen.getPackagePartInternalName(containingFile);
}
private void writeInnerClasses() {
Collection<ClassDescriptor> result = bindingContext.get(INNER_CLASSES, descriptor);
if (result != null) {
@@ -267,10 +267,15 @@ public class CodegenBinding {
return sortedAnswer;
}
public static boolean isLocalNamedFun(DeclarationDescriptor fd) {
public static boolean isLocalNamedFun(@Nullable DeclarationDescriptor fd) {
return isLocalFunOrLambda(fd) && !fd.getName().isSpecial();
}
/*named or not*/
public static boolean isLocalFunOrLambda(@Nullable DeclarationDescriptor fd) {
if (fd instanceof FunctionDescriptor) {
FunctionDescriptor descriptor = (FunctionDescriptor) fd;
return descriptor.getVisibility() == Visibilities.LOCAL && !descriptor.getName().isSpecial();
return descriptor.getVisibility() == Visibilities.LOCAL;
}
return false;
}
@@ -0,0 +1,19 @@
fun box(): String {
val classInLambda = {
class Z {}
Z()
}()
val enclosingMethod = classInLambda.javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "invoke") return "method: $enclosingMethod"
val enclosingClass = classInLambda.javaClass.getEnclosingClass()
if (enclosingClass?.getName() != "_DefaultPackage\$box\$classInLambda\$1") return "enclosing class: $enclosingClass"
//KT-5092
//val declaringClass = classInLambda.javaClass.getDeclaringClass()
//if (declaringClass == null) return "class hasn't a declaring class"
return "OK"
}
@@ -0,0 +1,18 @@
fun box(): String {
val objectInLambda = {
object : Any () {}
}()
val enclosingMethod = objectInLambda.javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "invoke") return "method: $enclosingMethod"
val enclosingClass = objectInLambda.javaClass.getEnclosingClass()
if (enclosingClass?.getName() != "_DefaultPackage\$box\$objectInLambda\$1") return "enclosing class: $enclosingClass"
//KT-5092
//val declaringClass = objectInLambda.javaClass.getDeclaringClass()
//if (declaringClass == null) return "anonymous object hasn't a declaring class"
return "OK"
}
@@ -0,0 +1,17 @@
class C {
val l: Any = {}
}
fun box(): String {
val javaClass = C().l.javaClass
val enclosingConstructor = javaClass.getEnclosingConstructor()
if (enclosingConstructor?.getDeclaringClass()?.getName() != "C") return "ctor: $enclosingConstructor"
val enclosingClass = javaClass.getEnclosingClass()
if (enclosingClass?.getName() != "C") return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,15 @@
fun box(): String {
val l: Any = {}
val javaClass = l.javaClass
val enclosingMethod = javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "box") return "method: $enclosingMethod"
val enclosingClass = javaClass.getEnclosingClass()
if (!enclosingClass!!.getName().startsWith("_DefaultPackage-lambdaInFunction-")) return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,17 @@
fun box(): String {
val l = {
{}
}
val javaClass = l().javaClass
val enclosingMethod = javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "invoke") return "method: $enclosingMethod"
val enclosingClass = javaClass.getEnclosingClass()
if (enclosingClass!!.getName() != "_DefaultPackage\$box\$l$1") return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,19 @@
open class C(val a: Any)
fun box(): String {
class L : C({}) {
}
val l = L()
val javaClass = l.a.javaClass
val enclosingMethod = javaClass.getEnclosingConstructor()
if (enclosingMethod?.getName() != "_DefaultPackage\$box\$L") return "ctor: $enclosingMethod"
val enclosingClass = javaClass.getEnclosingClass()
if (!enclosingClass!!.getName().startsWith("_DefaultPackage\$box\$L")) return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,17 @@
fun box(): String {
fun foo(): Any {
return {}
}
val javaClass = foo().javaClass
val enclosingMethod = javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "invoke") return "method: $enclosingMethod"
val enclosingClass = javaClass.getEnclosingClass()
if (enclosingClass!!.getName() != "_DefaultPackage\$box$1") return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,20 @@
class C {
fun foo(): Any {
return {}
}
}
fun box(): String {
val javaClass = C().foo().javaClass
val enclosingMethod = javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "foo") return "method: $enclosingMethod"
val enclosingClass = javaClass.getEnclosingClass()
if (enclosingClass?.getName() != "C") return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,19 @@
fun box(): String {
class C {
fun foo(): Any {
return {}
}
}
val javaClass = C().foo().javaClass
val enclosingMethod = javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "foo") return "method: $enclosingMethod"
val enclosingClass = javaClass.getEnclosingClass()
if (enclosingClass?.getName() != "_DefaultPackage\$box\$C") return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,21 @@
class C {
class D {
fun foo(): Any {
return {}
}
}
}
fun box(): String {
val javaClass = C.D().foo().javaClass
val enclosingMethod = javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "foo") return "method: $enclosingMethod"
val enclosingClass = javaClass.getEnclosingClass()
if (enclosingClass?.getSimpleName() != "D") return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,18 @@
open class C(val a: Any)
fun box(): String {
val l = object : C({}) {
}
val javaClass = l.a.javaClass
val enclosingMethod = javaClass.getEnclosingConstructor()
if (enclosingMethod?.getName() != "_DefaultPackage\$box\$l\$1") return "ctor: $enclosingMethod"
val enclosingClass = javaClass.getEnclosingClass()
if (!enclosingClass!!.getName().startsWith("_DefaultPackage\$box\$l\$1")) return "enclosing class: $enclosingClass"
val declaringClass = javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,17 @@
val l: Any = {}
fun box(): String {
val enclosingClass = l.javaClass.getEnclosingClass()
if (!enclosingClass!!.getName().startsWith("_DefaultPackage-lambdaInPackage-")) return "enclosing class: $enclosingClass"
val enclosingConstructor = l.javaClass.getEnclosingConstructor()
if (enclosingConstructor != null) return "enclosing constructor found: $enclosingConstructor"
val enclosingMethod = l.javaClass.getEnclosingMethod()
if (enclosingMethod != null) return "enclosing method found: $enclosingMethod"
val declaringClass = l.javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,16 @@
val l: Any
get() = {}
fun box(): String {
val enclosingMethod = l.javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "getL") return "method: $enclosingMethod"
val enclosingClass = l.javaClass.getEnclosingClass()
if (!enclosingClass!!.getName().startsWith("_DefaultPackage-lambdaInPropertyGetter-")) return "enclosing class: $enclosingClass"
val declaringClass = l.javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -0,0 +1,23 @@
var _l: Any = ""
var l: Any
get() = _l
set(v) {
_l = {}
}
fun box(): String {
l = "" // to invoke the setter
val enclosingMethod = l.javaClass.getEnclosingMethod()
if (enclosingMethod?.getName() != "setL") return "method: $enclosingMethod"
val enclosingClass = l.javaClass.getEnclosingClass()
if (!enclosingClass!!.getName().startsWith("_DefaultPackage-lambdaInPropertySetter-")) return "enclosing class: $enclosingClass"
val declaringClass = l.javaClass.getDeclaringClass()
if (declaringClass != null) return "anonymous function has a declaring class: $declaringClass"
return "OK"
}
@@ -1,4 +1,4 @@
class A {
dclass A {
fun foo() {
inlineFun { "test" }
}
@@ -31,7 +31,7 @@ import org.jetbrains.jet.codegen.generated.AbstractBlackBoxCodegenTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/boxWithStdlib")
@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Arrays.class, BlackBoxWithStdlibCodegenTestGenerated.CallableReference.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.Evaluate.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class})
@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Arrays.class, BlackBoxWithStdlibCodegenTestGenerated.CallableReference.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.Evaluate.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Reflection.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class})
public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInBoxWithStdlib() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxWithStdlib"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -1215,6 +1215,108 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection")
@InnerTestClasses({Reflection.InsideLambda.class, Reflection.Lambda.class})
public static class Reflection extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInReflection() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxWithStdlib/reflection"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/insideLambda")
public static class InsideLambda extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInInsideLambda() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxWithStdlib/reflection/insideLambda"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("classInLambda.kt")
public void testClassInLambda() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/insideLambda/classInLambda.kt");
}
@TestMetadata("objectInLambda.kt")
public void testObjectInLambda() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/insideLambda/objectInLambda.kt");
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/lambda")
public static class Lambda extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInLambda() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxWithStdlib/reflection/lambda"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("lambdaInConstructor.kt")
public void testLambdaInConstructor() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInConstructor.kt");
}
@TestMetadata("lambdaInFunction.kt")
public void testLambdaInFunction() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInFunction.kt");
}
@TestMetadata("lambdaInLambda.kt")
public void testLambdaInLambda() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInLambda.kt");
}
@TestMetadata("lambdaInLocalClass.kt")
public void testLambdaInLocalClass() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInLocalClass.kt");
}
@TestMetadata("lambdaInLocalFunction.kt")
public void testLambdaInLocalFunction() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInLocalFunction.kt");
}
@TestMetadata("lambdaInMemberFunction.kt")
public void testLambdaInMemberFunction() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInMemberFunction.kt");
}
@TestMetadata("lambdaInMemberFunctionInLocalClass.kt")
public void testLambdaInMemberFunctionInLocalClass() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInMemberFunctionInLocalClass.kt");
}
@TestMetadata("lambdaInMemberFunctionInNestedClass.kt")
public void testLambdaInMemberFunctionInNestedClass() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInMemberFunctionInNestedClass.kt");
}
@TestMetadata("lambdaInObjectExpression.kt")
public void testLambdaInObjectExpression() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInObjectExpression.kt");
}
@TestMetadata("lambdaInPackage.kt")
public void testLambdaInPackage() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInPackage.kt");
}
@TestMetadata("lambdaInPropertyGetter.kt")
public void testLambdaInPropertyGetter() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInPropertyGetter.kt");
}
@TestMetadata("lambdaInPropertySetter.kt")
public void testLambdaInPropertySetter() throws Exception {
doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/lambda/lambdaInPropertySetter.kt");
}
}
public static Test innerSuite() {
TestSuite suite = new TestSuite("Reflection");
suite.addTestSuite(Reflection.class);
suite.addTestSuite(InsideLambda.class);
suite.addTestSuite(Lambda.class);
return suite;
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/regressions")
public static class Regressions extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInRegressions() throws Exception {
@@ -1472,6 +1574,7 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
suite.addTestSuite(FullJdk.class);
suite.addTestSuite(JdkAnnotations.class);
suite.addTest(Ranges.innerSuite());
suite.addTest(Reflection.innerSuite());
suite.addTestSuite(Regressions.class);
suite.addTestSuite(Strings.class);
suite.addTestSuite(ToArray.class);