From 27d403ee2390403a874419a5d2c983a1b7e16210 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 19 Nov 2014 18:24:02 +0300 Subject: [PATCH] Make native class object members delegate to static methods in the outer class --- .../org/jetbrains/jet/codegen/AsmUtil.java | 5 +++ .../jet/codegen/FunctionCodegen.java | 35 +++++++++++++++---- .../boxWithStdlib/native/simpleNative.kt | 2 ++ .../boxWithStdlib/native/staticNative.kt | 19 ++++++---- 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java index ab3d7e7ebdb..0bab49faf80 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java @@ -189,6 +189,11 @@ public class AsmUtil { } } + if (AnnotationsPackage.isPlatformStaticInClassObject(functionDescriptor)) { + // Native method will be a member of the class, the class object method will be delegated to it + flags &= ~Opcodes.ACC_NATIVE; + } + if (functionDescriptor.getModality() == Modality.FINAL && !(functionDescriptor instanceof ConstructorDescriptor)) { DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); if (!(containingDeclaration instanceof ClassDescriptor) || diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 68a73dd9f63..63f7f615c36 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -49,6 +49,7 @@ import org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin; import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterKind; import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; +import org.jetbrains.jet.lang.resolve.kotlin.nativeDeclarations.NativeDeclarationsPackage; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.types.Approximation; import org.jetbrains.jet.lang.types.TypesPackage; @@ -129,6 +130,8 @@ public class FunctionCodegen extends ParentCodegenAware { Method asmMethod = jvmSignature.getAsmMethod(); int flags = getMethodAsmFlags(functionDescriptor, methodContextKind); + boolean isNative = NativeDeclarationsPackage.hasNativeAnnotation(functionDescriptor); + MethodVisitor mv = v.newMethod(origin, flags, asmMethod.getName(), @@ -154,8 +157,9 @@ public class FunctionCodegen extends ParentCodegenAware { generateBridges(functionDescriptor); + boolean staticInClassObject = AnnotationsPackage.isPlatformStaticInClassObject(functionDescriptor); - if (AnnotationsPackage.isPlatformStaticInClassObject(functionDescriptor)) { + if (staticInClassObject) { MemberCodegen codegen = getParentCodegen().getParentCodegen(); ((ImplementationBodyCodegen) codegen).addAdditionalTask(new PlatformStaticGenerator(functionDescriptor, origin, state)); } @@ -175,9 +179,17 @@ public class FunctionCodegen extends ParentCodegenAware { return; } - if ((flags & Opcodes.ACC_NATIVE) == 0) { + if (!isNative) { generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, getParentCodegen()); } + else if (staticInClassObject) { + // native platformStatic foo() in class object should delegate to the static native function moved to the outer class + mv.visitCode(); + ClassifierDescriptor outerClassDescriptor = + (ClassifierDescriptor) functionDescriptor.getContainingDeclaration().getContainingDeclaration(); + assert outerClassDescriptor != null : "Class object has no outer class: " + functionDescriptor.getContainingDeclaration(); + generateDelegateToMethodBody(false, mv, asmMethod, typeMapper.mapClass(outerClassDescriptor).getInternalName()); + } endVisit(mv, null, origin.getElement()); @@ -304,7 +316,7 @@ public class FunctionCodegen extends ParentCodegenAware { JetTypeMapper typeMapper = parentCodegen.typeMapper; if (context.getParentContext() instanceof PackageFacadeContext) { - generateStaticDelegateMethodBody(mv, signature.getAsmMethod(), (PackageFacadeContext) context.getParentContext()); + generatePackageDelegateMethodBody(mv, signature.getAsmMethod(), (PackageFacadeContext) context.getParentContext()); } else { FrameMap frameMap = createFrameMap(parentCodegen.state, functionDescriptor, signature, isStaticMethod(context.getContextKind(), @@ -374,10 +386,19 @@ public class FunctionCodegen extends ParentCodegenAware { } } - private static void generateStaticDelegateMethodBody( + private static void generatePackageDelegateMethodBody( @NotNull MethodVisitor mv, @NotNull Method asmMethod, @NotNull PackageFacadeContext context + ) { + generateDelegateToMethodBody(true, mv, asmMethod, context.getDelegateToClassType().getInternalName()); + } + + private static void generateDelegateToMethodBody( + boolean isStatic, + @NotNull MethodVisitor mv, + @NotNull Method asmMethod, + @NotNull String classToDelegateTo ) { InstructionAdapter iv = new InstructionAdapter(mv); Type[] argTypes = asmMethod.getArgumentTypes(); @@ -388,12 +409,12 @@ public class FunctionCodegen extends ParentCodegenAware { iv.visitLabel(label); iv.visitLineNumber(1, label); - int k = 0; + int k = isStatic ? 0 : 1; for (Type argType : argTypes) { iv.load(k, argType); k += argType.getSize(); } - iv.invokestatic(context.getDelegateToClassType().getInternalName(), asmMethod.getName(), asmMethod.getDescriptor(), false); + iv.invokestatic(classToDelegateTo, asmMethod.getName(), asmMethod.getDescriptor(), false); iv.areturn(asmMethod.getReturnType()); } @@ -593,7 +614,7 @@ public class FunctionCodegen extends ParentCodegenAware { if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { if (this.owner instanceof PackageFacadeContext) { mv.visitCode(); - generateStaticDelegateMethodBody(mv, defaultMethod, (PackageFacadeContext) this.owner); + generatePackageDelegateMethodBody(mv, defaultMethod, (PackageFacadeContext) this.owner); endVisit(mv, "default method delegation", callableDescriptorToDeclaration(functionDescriptor)); } else { diff --git a/compiler/testData/codegen/boxWithStdlib/native/simpleNative.kt b/compiler/testData/codegen/boxWithStdlib/native/simpleNative.kt index 9203ce37630..eddc3553c70 100644 --- a/compiler/testData/codegen/boxWithStdlib/native/simpleNative.kt +++ b/compiler/testData/codegen/boxWithStdlib/native/simpleNative.kt @@ -1,3 +1,5 @@ +package foo + import kotlin.jvm.* class WithNative { diff --git a/compiler/testData/codegen/boxWithStdlib/native/staticNative.kt b/compiler/testData/codegen/boxWithStdlib/native/staticNative.kt index 09b952fc782..f95864575bb 100644 --- a/compiler/testData/codegen/boxWithStdlib/native/staticNative.kt +++ b/compiler/testData/codegen/boxWithStdlib/native/staticNative.kt @@ -1,27 +1,34 @@ +package foo + import kotlin.jvm.* import kotlin.platform.* class WithNative { class object { - platformStatic native fun bar() {} + platformStatic native fun bar(l: Long, s: String): Double = null!! } } object ObjWithNative { - platformStatic native fun bar() {} + platformStatic native fun bar(l: Long, s: String): Double = null!! } fun box(): String { + var d = 0.0 try { - WithNative.bar() + d = WithNative.bar(1, "") return "Link error expected" } - catch (e: java.lang.UnsatisfiedLinkError) {} + catch (e: java.lang.UnsatisfiedLinkError) { + if (e.getMessage() != "foo.WithNative.bar(JLjava/lang/String;)D") return "Fail 1: " + e.getMessage() + } try { - ObjWithNative.bar() + d = ObjWithNative.bar(1, "") return "Link error expected on object" } - catch (e: java.lang.UnsatisfiedLinkError) {} + catch (e: java.lang.UnsatisfiedLinkError) { + if (e.getMessage() != "foo.ObjWithNative.bar(JLjava/lang/String;)D") return "Fail 2: " + e.getMessage() + } return "OK" } \ No newline at end of file