Make native class object members delegate to static methods in the outer class

This commit is contained in:
Andrey Breslav
2014-11-19 18:24:02 +03:00
parent d20651b881
commit 27d403ee23
4 changed files with 48 additions and 13 deletions
@@ -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)) { if (functionDescriptor.getModality() == Modality.FINAL && !(functionDescriptor instanceof ConstructorDescriptor)) {
DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
if (!(containingDeclaration instanceof ClassDescriptor) || if (!(containingDeclaration instanceof ClassDescriptor) ||
@@ -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.JvmMethodParameterKind;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; 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.resolve.name.FqName;
import org.jetbrains.jet.lang.types.Approximation; import org.jetbrains.jet.lang.types.Approximation;
import org.jetbrains.jet.lang.types.TypesPackage; import org.jetbrains.jet.lang.types.TypesPackage;
@@ -129,6 +130,8 @@ public class FunctionCodegen extends ParentCodegenAware {
Method asmMethod = jvmSignature.getAsmMethod(); Method asmMethod = jvmSignature.getAsmMethod();
int flags = getMethodAsmFlags(functionDescriptor, methodContextKind); int flags = getMethodAsmFlags(functionDescriptor, methodContextKind);
boolean isNative = NativeDeclarationsPackage.hasNativeAnnotation(functionDescriptor);
MethodVisitor mv = v.newMethod(origin, MethodVisitor mv = v.newMethod(origin,
flags, flags,
asmMethod.getName(), asmMethod.getName(),
@@ -154,8 +157,9 @@ public class FunctionCodegen extends ParentCodegenAware {
generateBridges(functionDescriptor); generateBridges(functionDescriptor);
boolean staticInClassObject = AnnotationsPackage.isPlatformStaticInClassObject(functionDescriptor);
if (AnnotationsPackage.isPlatformStaticInClassObject(functionDescriptor)) { if (staticInClassObject) {
MemberCodegen<?> codegen = getParentCodegen().getParentCodegen(); MemberCodegen<?> codegen = getParentCodegen().getParentCodegen();
((ImplementationBodyCodegen) codegen).addAdditionalTask(new PlatformStaticGenerator(functionDescriptor, origin, state)); ((ImplementationBodyCodegen) codegen).addAdditionalTask(new PlatformStaticGenerator(functionDescriptor, origin, state));
} }
@@ -175,9 +179,17 @@ public class FunctionCodegen extends ParentCodegenAware {
return; return;
} }
if ((flags & Opcodes.ACC_NATIVE) == 0) { if (!isNative) {
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, getParentCodegen()); 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()); endVisit(mv, null, origin.getElement());
@@ -304,7 +316,7 @@ public class FunctionCodegen extends ParentCodegenAware {
JetTypeMapper typeMapper = parentCodegen.typeMapper; JetTypeMapper typeMapper = parentCodegen.typeMapper;
if (context.getParentContext() instanceof PackageFacadeContext) { if (context.getParentContext() instanceof PackageFacadeContext) {
generateStaticDelegateMethodBody(mv, signature.getAsmMethod(), (PackageFacadeContext) context.getParentContext()); generatePackageDelegateMethodBody(mv, signature.getAsmMethod(), (PackageFacadeContext) context.getParentContext());
} }
else { else {
FrameMap frameMap = createFrameMap(parentCodegen.state, functionDescriptor, signature, isStaticMethod(context.getContextKind(), 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 MethodVisitor mv,
@NotNull Method asmMethod, @NotNull Method asmMethod,
@NotNull PackageFacadeContext context @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); InstructionAdapter iv = new InstructionAdapter(mv);
Type[] argTypes = asmMethod.getArgumentTypes(); Type[] argTypes = asmMethod.getArgumentTypes();
@@ -388,12 +409,12 @@ public class FunctionCodegen extends ParentCodegenAware {
iv.visitLabel(label); iv.visitLabel(label);
iv.visitLineNumber(1, label); iv.visitLineNumber(1, label);
int k = 0; int k = isStatic ? 0 : 1;
for (Type argType : argTypes) { for (Type argType : argTypes) {
iv.load(k, argType); iv.load(k, argType);
k += argType.getSize(); 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()); iv.areturn(asmMethod.getReturnType());
} }
@@ -593,7 +614,7 @@ public class FunctionCodegen extends ParentCodegenAware {
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
if (this.owner instanceof PackageFacadeContext) { if (this.owner instanceof PackageFacadeContext) {
mv.visitCode(); mv.visitCode();
generateStaticDelegateMethodBody(mv, defaultMethod, (PackageFacadeContext) this.owner); generatePackageDelegateMethodBody(mv, defaultMethod, (PackageFacadeContext) this.owner);
endVisit(mv, "default method delegation", callableDescriptorToDeclaration(functionDescriptor)); endVisit(mv, "default method delegation", callableDescriptorToDeclaration(functionDescriptor));
} }
else { else {
@@ -1,3 +1,5 @@
package foo
import kotlin.jvm.* import kotlin.jvm.*
class WithNative { class WithNative {
@@ -1,27 +1,34 @@
package foo
import kotlin.jvm.* import kotlin.jvm.*
import kotlin.platform.* import kotlin.platform.*
class WithNative { class WithNative {
class object { class object {
platformStatic native fun bar() {} platformStatic native fun bar(l: Long, s: String): Double = null!!
} }
} }
object ObjWithNative { object ObjWithNative {
platformStatic native fun bar() {} platformStatic native fun bar(l: Long, s: String): Double = null!!
} }
fun box(): String { fun box(): String {
var d = 0.0
try { try {
WithNative.bar() d = WithNative.bar(1, "")
return "Link error expected" 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 { try {
ObjWithNative.bar() d = ObjWithNative.bar(1, "")
return "Link error expected on object" 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" return "OK"
} }