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:
Alexander Udalov
2015-06-10 03:20:37 +03:00
parent bc168c0cba
commit ab297a4da0
9 changed files with 148 additions and 20 deletions
@@ -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");
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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();
}
}