Generate reflection info to classes for function references
The information includes the owner (class, package, script, or null for local functions) and the JVM signature -- this information will be used by reflection to locate the symbol
This commit is contained in:
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils;
|
||||
import org.jetbrains.kotlin.psi.JetElement;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
@@ -46,6 +47,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.ExpressionCodegen.generateClassLiteralReference;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
|
||||
@@ -53,6 +55,7 @@ import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticC
|
||||
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.OtherOrigin;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
@@ -61,6 +64,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
private final SamType samType;
|
||||
private final JetType superClassType;
|
||||
private final List<JetType> superInterfaceTypes;
|
||||
private final FunctionDescriptor functionReferenceTarget;
|
||||
private final FunctionGenerationStrategy strategy;
|
||||
private final CalculatedClosure closure;
|
||||
private final Type asmType;
|
||||
@@ -76,6 +80,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
@Nullable SamType samType,
|
||||
@NotNull ClosureContext context,
|
||||
@NotNull KotlinSyntheticClass.Kind syntheticClassKind,
|
||||
@Nullable FunctionDescriptor functionReferenceTarget,
|
||||
@NotNull FunctionGenerationStrategy strategy,
|
||||
@NotNull MemberCodegen<?> parentCodegen,
|
||||
@NotNull ClassBuilder classBuilder
|
||||
@@ -86,6 +91,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
this.classDescriptor = context.getContextDescriptor();
|
||||
this.samType = samType;
|
||||
this.syntheticClassKind = syntheticClassKind;
|
||||
this.functionReferenceTarget = functionReferenceTarget;
|
||||
this.strategy = strategy;
|
||||
|
||||
if (samType == null) {
|
||||
@@ -189,7 +195,11 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
functionCodegen.generateBridges(descriptorForBridges);
|
||||
}
|
||||
|
||||
this.constructor = generateConstructor(superClassAsmType);
|
||||
if (functionReferenceTarget != null) {
|
||||
generateFunctionReferenceMethods(functionReferenceTarget);
|
||||
}
|
||||
|
||||
this.constructor = generateConstructor();
|
||||
|
||||
if (isConst(closure)) {
|
||||
generateConstInstance();
|
||||
@@ -214,10 +224,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public StackValue putInstanceOnStack(
|
||||
@NotNull final ExpressionCodegen codegen,
|
||||
@Nullable final FunctionDescriptor functionReferenceTarget
|
||||
) {
|
||||
public StackValue putInstanceOnStack(@NotNull final ExpressionCodegen codegen) {
|
||||
return StackValue.operation(asmType, new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter v) {
|
||||
@@ -267,7 +274,8 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
MethodVisitor mv = v.newMethod(OtherOrigin(element, funDescriptor), ACC_STATIC | ACC_SYNTHETIC, "<clinit>", "()V", null, ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
|
||||
v.newField(OtherOrigin(element, funDescriptor), ACC_STATIC | ACC_FINAL | ACC_PUBLIC, JvmAbi.INSTANCE_FIELD, asmType.getDescriptor(), null, null);
|
||||
v.newField(OtherOrigin(element, funDescriptor), ACC_STATIC | ACC_FINAL | ACC_PUBLIC, JvmAbi.INSTANCE_FIELD, asmType.getDescriptor(),
|
||||
null, null);
|
||||
|
||||
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
|
||||
mv.visitCode();
|
||||
@@ -316,8 +324,78 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
|
||||
FunctionCodegen.endVisit(mv, "bridge", element);
|
||||
}
|
||||
|
||||
private void generateFunctionReferenceMethods(@NotNull FunctionDescriptor descriptor) {
|
||||
int flags = ACC_PUBLIC | ACC_FINAL;
|
||||
boolean generateBody = state.getClassBuilderMode() == ClassBuilderMode.FULL;
|
||||
|
||||
{
|
||||
MethodVisitor mv =
|
||||
v.newMethod(NO_ORIGIN, flags, "getOwner", Type.getMethodDescriptor(K_DECLARATION_CONTAINER_TYPE), null, null);
|
||||
if (generateBody) {
|
||||
mv.visitCode();
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
generateFunctionReferenceDeclarationContainer(iv, descriptor, typeMapper);
|
||||
iv.areturn(K_DECLARATION_CONTAINER_TYPE);
|
||||
FunctionCodegen.endVisit(iv, "function reference getOwner", element);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
MethodVisitor mv =
|
||||
v.newMethod(NO_ORIGIN, flags, "getName", Type.getMethodDescriptor(JAVA_STRING_TYPE), null, null);
|
||||
if (generateBody) {
|
||||
mv.visitCode();
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
iv.aconst(descriptor.getName().asString());
|
||||
iv.areturn(JAVA_STRING_TYPE);
|
||||
FunctionCodegen.endVisit(iv, "function reference getName", element);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
MethodVisitor mv = v.newMethod(NO_ORIGIN, flags, "getSignature", Type.getMethodDescriptor(JAVA_STRING_TYPE), null, null);
|
||||
if (generateBody) {
|
||||
mv.visitCode();
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
Method method = typeMapper.mapSignature(descriptor).getAsmMethod();
|
||||
iv.aconst(method.getName() + method.getDescriptor());
|
||||
iv.areturn(JAVA_STRING_TYPE);
|
||||
FunctionCodegen.endVisit(iv, "function reference getSignature", element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateFunctionReferenceDeclarationContainer(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull JetTypeMapper typeMapper
|
||||
) {
|
||||
DeclarationDescriptor container = descriptor.getContainingDeclaration();
|
||||
if (container instanceof ClassDescriptor) {
|
||||
// TODO: getDefaultType() here is wrong and won't work for arrays
|
||||
StackValue value = generateClassLiteralReference(typeMapper, ((ClassDescriptor) container).getDefaultType());
|
||||
value.put(K_CLASS_TYPE, iv);
|
||||
}
|
||||
else if (container instanceof PackageFragmentDescriptor) {
|
||||
String packageClassInternalName = PackageClassUtils.getPackageClassInternalName(
|
||||
((PackageFragmentDescriptor) container).getFqName()
|
||||
);
|
||||
iv.getstatic(packageClassInternalName, JvmAbi.KOTLIN_PACKAGE_FIELD_NAME, K_PACKAGE_TYPE.getDescriptor());
|
||||
}
|
||||
else if (container instanceof ScriptDescriptor) {
|
||||
// TODO: correct container for scripts (KScript?)
|
||||
StackValue value = generateClassLiteralReference(
|
||||
typeMapper, ((ScriptDescriptor) container).getClassDescriptor().getDefaultType()
|
||||
);
|
||||
value.put(K_CLASS_TYPE, iv);
|
||||
}
|
||||
else {
|
||||
iv.aconst(null);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Method generateConstructor(@NotNull Type superClassAsmType) {
|
||||
private Method generateConstructor() {
|
||||
List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType);
|
||||
|
||||
Type[] argTypes = fieldListToTypeArray(args);
|
||||
|
||||
@@ -1429,7 +1429,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
);
|
||||
|
||||
ClosureCodegen closureCodegen = new ClosureCodegen(
|
||||
state, declaration, samType, context.intoClosure(descriptor, this, typeMapper), kind, strategy, parentCodegen, cv
|
||||
state, declaration, samType, context.intoClosure(descriptor, this, typeMapper), kind,
|
||||
functionReferenceTarget, strategy, parentCodegen, cv
|
||||
);
|
||||
|
||||
closureCodegen.generate();
|
||||
@@ -1439,7 +1440,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
propagateChildReifiedTypeParametersUsages(closureCodegen.getReifiedTypeParametersUsages());
|
||||
}
|
||||
|
||||
return closureCodegen.putInstanceOnStack(this, functionReferenceTarget);
|
||||
return closureCodegen.putInstanceOnStack(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2741,7 +2742,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
assert state.getReflectionTypes().getkClass().getTypeConstructor().equals(type.getConstructor())
|
||||
: "::class expression should be type checked to a KClass: " + type;
|
||||
|
||||
return generateClassLiteralReference(KotlinPackage.single(type.getArguments()).getType());
|
||||
return generateClassLiteralReference(typeMapper, KotlinPackage.single(type.getArguments()).getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2834,7 +2835,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter v) {
|
||||
v.visitLdcInsn(descriptor.getName().asString());
|
||||
StackValue receiverClass = generateClassLiteralReference(containingClass.getDefaultType());
|
||||
StackValue receiverClass = generateClassLiteralReference(typeMapper, containingClass.getDefaultType());
|
||||
receiverClass.put(receiverClass.type, v);
|
||||
v.invokestatic(REFLECTION, factoryMethod.getName(), factoryMethod.getDescriptor(), false);
|
||||
|
||||
@@ -2844,7 +2845,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private StackValue generateClassLiteralReference(@NotNull final JetType type) {
|
||||
public static StackValue generateClassLiteralReference(@NotNull final JetTypeMapper typeMapper, @NotNull final JetType type) {
|
||||
return StackValue.operation(K_CLASS_TYPE, new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter v) {
|
||||
|
||||
@@ -40,6 +40,7 @@ public class AsmTypes {
|
||||
public static final Type K_CLASS_TYPE = reflect("KClass");
|
||||
public static final Type K_CLASS_ARRAY_TYPE = Type.getObjectType("[" + K_CLASS_TYPE.getDescriptor());
|
||||
public static final Type K_PACKAGE_TYPE = reflect("KPackage");
|
||||
public static final Type K_DECLARATION_CONTAINER_TYPE = reflect("KDeclarationContainer");
|
||||
|
||||
public static final Type K_FUNCTION = reflect("KFunction");
|
||||
public static final Type K_TOP_LEVEL_FUNCTION = reflect("KTopLevelFunction");
|
||||
|
||||
@@ -24,7 +24,7 @@ package kotlin.reflect
|
||||
*
|
||||
* @param T the type of the class.
|
||||
*/
|
||||
public interface KClass<T> {
|
||||
public interface KClass<T> : KDeclarationContainer {
|
||||
/**
|
||||
* The simple name of the class as it was declared in the source code,
|
||||
* or `null` if the class has no name (if, for example, it is an anonymous object literal).
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package kotlin.reflect
|
||||
|
||||
/**
|
||||
* Represents an entity which may contain declarations of any other entities,
|
||||
* such as a class or a package.
|
||||
*/
|
||||
public interface KDeclarationContainer
|
||||
@@ -19,4 +19,4 @@ package kotlin.reflect
|
||||
/**
|
||||
* Represents a package and provides introspection capabilities.
|
||||
*/
|
||||
public interface KPackage
|
||||
public interface KPackage : KDeclarationContainer
|
||||
|
||||
@@ -30,8 +30,9 @@ import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.JvmType.PrimitiveType.
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.reflect.KotlinReflectionInternalError
|
||||
import kotlin.reflect.KDeclarationContainer
|
||||
|
||||
abstract class KCallableContainerImpl {
|
||||
abstract class KCallableContainerImpl : KDeclarationContainer {
|
||||
// Note: this is stored here on a soft reference to prevent GC from destroying the weak reference to it in the moduleByClassLoader cache
|
||||
val moduleData by ReflectProperties.lazySoft {
|
||||
jClass.getOrCreateModule()
|
||||
|
||||
@@ -18,7 +18,4 @@ package kotlin.reflect.jvm.internal
|
||||
|
||||
import kotlin.jvm.internal.FunctionImpl
|
||||
|
||||
/**
|
||||
* @suppress
|
||||
*/
|
||||
public abstract class KFunctionImpl<out R> : FunctionImpl()
|
||||
abstract class KFunctionImpl<out R> : FunctionImpl()
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package kotlin.jvm.internal;
|
||||
|
||||
import kotlin.jvm.KotlinReflectionNotSupportedError;
|
||||
import kotlin.reflect.*;
|
||||
|
||||
public abstract class FunctionReference
|
||||
public class FunctionReference
|
||||
extends FunctionImpl
|
||||
implements KTopLevelFunction,
|
||||
KMemberFunction,
|
||||
@@ -39,4 +40,30 @@ public abstract class FunctionReference
|
||||
public int getArity() {
|
||||
return arity;
|
||||
}
|
||||
|
||||
// The following methods provide the information identifying this function, which is used by the reflection implementation.
|
||||
// They are supposed to be overridden in each subclass (each anonymous class generated for a function reference).
|
||||
|
||||
public KDeclarationContainer getOwner() {
|
||||
throw error();
|
||||
}
|
||||
|
||||
// Kotlin name of the function, the one which was declared in the source code (@platformName can't change it)
|
||||
public String getName() {
|
||||
throw error();
|
||||
}
|
||||
|
||||
// JVM signature of the function, e.g. "println(Ljava/lang/Object;)V"
|
||||
public String getSignature() {
|
||||
throw error();
|
||||
}
|
||||
|
||||
// The following methods are the stub implementations of reflection functions.
|
||||
// They are called when you're using reflection on a function reference without the reflection implementation in the classpath.
|
||||
|
||||
// (nothing here yet)
|
||||
|
||||
private static Error error() {
|
||||
throw new KotlinReflectionNotSupportedError();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user