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)) {
DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
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.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 {
@@ -1,3 +1,5 @@
package foo
import kotlin.jvm.*
class WithNative {
@@ -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"
}