Generate separate anonymous class for each property reference

Each property reference obtained by the '::' operator now causes back-end to
generate an anonymous subclass of the corresponding KProperty class, with the
customized behavior. This fixes a number of issues:

- get/set/name of property references now works without kotlin-reflect.jar in
  the classpath
- get/set/name methods are now overridden with statically-generated property
  access instead of the default KPropertyImpl's behavior of using Java
  reflection, which should be a lot faster
- references to private/protected properties now work without the need to set
  'accessible' flag, because corresponding synthetic accessors are generated at
  compile-time near the target property

 #KT-6870 Fixed
 #KT-6873 Fixed
 #KT-7033 Fixed
This commit is contained in:
Alexander Udalov
2015-06-30 16:07:51 +03:00
parent 30794060a9
commit 048a9b686e
40 changed files with 833 additions and 304 deletions
@@ -202,7 +202,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
this.constructor = generateConstructor();
if (isConst(closure)) {
generateConstInstance();
generateConstInstance(asmType);
}
genClosureFields(closure, v, typeMapper);
@@ -251,30 +251,12 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
);
}
private void generateConstInstance() {
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);
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
mv.visitCode();
iv.anew(asmType);
iv.dup();
iv.invokespecial(asmType.getInternalName(), "<init>", "()V", false);
iv.putstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor());
mv.visitInsn(RETURN);
FunctionCodegen.endVisit(mv, "<clinit>", element);
}
}
private void generateBridge(@NotNull Method bridge, @NotNull Method delegate) {
if (bridge.equals(delegate)) return;
MethodVisitor mv =
v.newMethod(OtherOrigin(element, funDescriptor), ACC_PUBLIC | ACC_BRIDGE, bridge.getName(), bridge.getDescriptor(), null, ArrayUtil.EMPTY_STRING_ARRAY);
v.newMethod(OtherOrigin(element, funDescriptor), ACC_PUBLIC | ACC_BRIDGE,
bridge.getName(), bridge.getDescriptor(), null, ArrayUtil.EMPTY_STRING_ARRAY);
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
@@ -306,6 +288,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
FunctionCodegen.endVisit(mv, "bridge", element);
}
// TODO: ImplementationBodyCodegen.markLineNumberForSyntheticFunction?
private void generateFunctionReferenceMethods(@NotNull FunctionDescriptor descriptor) {
int flags = ACC_PUBLIC | ACC_FINAL;
boolean generateBody = state.getClassBuilderMode() == ClassBuilderMode.FULL;
@@ -316,7 +299,7 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
if (generateBody) {
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
generateFunctionReferenceDeclarationContainer(iv, descriptor, typeMapper);
generateCallableReferenceDeclarationContainer(iv, descriptor, typeMapper);
iv.areturn(K_DECLARATION_CONTAINER_TYPE);
FunctionCodegen.endVisit(iv, "function reference getOwner", element);
}
@@ -347,9 +330,9 @@ public class ClosureCodegen extends MemberCodegen<JetElement> {
}
}
private static void generateFunctionReferenceDeclarationContainer(
public static void generateCallableReferenceDeclarationContainer(
@NotNull InstructionAdapter iv,
@NotNull FunctionDescriptor descriptor,
@NotNull CallableDescriptor descriptor,
@NotNull JetTypeMapper typeMapper
) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
@@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
import org.jetbrains.kotlin.codegen.inline.*;
@@ -51,7 +52,6 @@ import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor;
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
@@ -85,7 +85,6 @@ import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
import java.util.*;
@@ -2755,29 +2754,39 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
(FunctionDescriptor) resolvedCall.getResultingDescriptor());
}
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
if (variableDescriptor == null) {
throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText());
}
// TODO: this diagnostic should also be reported on function references once they obtain reflection
checkReflectionIsAvailable(expression);
VariableDescriptor descriptor = (VariableDescriptor) resolvedCall.getResultingDescriptor();
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
if (variableDescriptor != null) {
return generatePropertyReference(expression, variableDescriptor, resolvedCall);
}
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
if (containingDeclaration instanceof PackageFragmentDescriptor) {
return generateTopLevelPropertyReference(descriptor);
}
else if (containingDeclaration instanceof ClassDescriptor) {
return generateMemberPropertyReference(descriptor, (ClassDescriptor) containingDeclaration);
}
else if (containingDeclaration instanceof ScriptDescriptor) {
return generateMemberPropertyReference(descriptor, ((ScriptDescriptor) containingDeclaration).getClassDescriptor());
}
else {
throw new UnsupportedOperationException("Unsupported callable reference container: " + containingDeclaration);
}
throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText());
}
@NotNull
private StackValue generatePropertyReference(
@NotNull JetCallableReferenceExpression expression,
@NotNull VariableDescriptor variableDescriptor,
@NotNull ResolvedCall<?> resolvedCall
) {
ClassDescriptor classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor);
ClassBuilder classBuilder = state.getFactory().newVisitor(
OtherOrigin(expression),
typeMapper.mapClass(classDescriptor),
expression.getContainingFile()
);
@SuppressWarnings("unchecked")
PropertyReferenceCodegen codegen = new PropertyReferenceCodegen(
state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION),
expression, classBuilder, classDescriptor, (ResolvedCall<VariableDescriptor>) resolvedCall
);
codegen.generate();
return codegen.putInstanceOnStack();
}
private void checkReflectionIsAvailable(@NotNull JetExpression expression) {
@@ -2786,64 +2795,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
}
@NotNull
private StackValue generateTopLevelPropertyReference(@NotNull final VariableDescriptor descriptor) {
PackageFragmentDescriptor containingPackage = (PackageFragmentDescriptor) descriptor.getContainingDeclaration();
final String packageClassInternalName = PackageClassUtils.getPackageClassInternalName(containingPackage.getFqName());
final ReceiverParameterDescriptor receiverParameter = descriptor.getExtensionReceiverParameter();
final Method factoryMethod;
if (receiverParameter != null) {
Type[] parameterTypes = new Type[] {JAVA_STRING_TYPE, K_PACKAGE_TYPE, getType(Class.class)};
factoryMethod = descriptor.isVar()
? method("mutableTopLevelExtensionProperty", K_MUTABLE_PROPERTY1_TYPE, parameterTypes)
: method("topLevelExtensionProperty", K_PROPERTY1_TYPE, parameterTypes);
}
else {
Type[] parameterTypes = new Type[] {JAVA_STRING_TYPE, K_PACKAGE_TYPE};
factoryMethod = descriptor.isVar()
? method("mutableTopLevelVariable", K_MUTABLE_PROPERTY0_TYPE, parameterTypes)
: method("topLevelVariable", K_PROPERTY0_TYPE, parameterTypes);
}
return StackValue.operation(factoryMethod.getReturnType(), new Function1<InstructionAdapter, Unit>() {
@Override
public Unit invoke(InstructionAdapter v) {
v.visitLdcInsn(descriptor.getName().asString());
v.getstatic(packageClassInternalName, JvmAbi.KOTLIN_PACKAGE_FIELD_NAME, K_PACKAGE_TYPE.getDescriptor());
if (receiverParameter != null) {
putJavaLangClassInstance(v, typeMapper.mapType(receiverParameter));
}
v.invokestatic(REFLECTION, factoryMethod.getName(), factoryMethod.getDescriptor(), false);
return Unit.INSTANCE$;
}
});
}
@NotNull
private StackValue generateMemberPropertyReference(
@NotNull final VariableDescriptor descriptor,
@NotNull final ClassDescriptor containingClass
) {
final Method factoryMethod = descriptor.isVar()
? method("mutableMemberProperty", K_MUTABLE_PROPERTY1_TYPE, JAVA_STRING_TYPE, K_CLASS_TYPE)
: method("memberProperty", K_PROPERTY1_TYPE, JAVA_STRING_TYPE, K_CLASS_TYPE);
return StackValue.operation(factoryMethod.getReturnType(), new Function1<InstructionAdapter, Unit>() {
@Override
public Unit invoke(InstructionAdapter v) {
v.visitLdcInsn(descriptor.getName().asString());
StackValue receiverClass = generateClassLiteralReference(typeMapper, containingClass.getDefaultType());
receiverClass.put(receiverClass.type, v);
v.invokestatic(REFLECTION, factoryMethod.getName(), factoryMethod.getDescriptor(), false);
return Unit.INSTANCE$;
}
});
}
@NotNull
public static StackValue generateClassLiteralReference(@NotNull final JetTypeMapper typeMapper, @NotNull final JetType type) {
return StackValue.operation(K_CLASS_TYPE, new Function1<InstructionAdapter, Unit>() {
@@ -29,15 +29,15 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.*;
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns;
public class JvmRuntimeTypes {
private final ClassDescriptor lambda;
private final ClassDescriptor functionReference;
private final List<ClassDescriptor> propertyReferences;
private final List<ClassDescriptor> mutablePropertyReferences;
public JvmRuntimeTypes() {
ModuleDescriptorImpl module = new ModuleDescriptorImpl(
@@ -49,6 +49,13 @@ public class JvmRuntimeTypes {
this.lambda = createClass(kotlinJvmInternal, "Lambda");
this.functionReference = createClass(kotlinJvmInternal, "FunctionReference");
this.propertyReferences = new ArrayList<ClassDescriptor>(3);
this.mutablePropertyReferences = new ArrayList<ClassDescriptor>(3);
for (int i = 0; i <= 2; i++) {
propertyReferences.add(createClass(kotlinJvmInternal, "PropertyReference" + i));
mutablePropertyReferences.add(createClass(kotlinJvmInternal, "MutablePropertyReference" + i));
}
}
@NotNull
@@ -98,4 +105,11 @@ public class JvmRuntimeTypes {
return Arrays.asList(functionReference.getDefaultType(), functionType);
}
@NotNull
public JetType getSupertypeForPropertyReference(@NotNull PropertyDescriptor descriptor) {
int arity = (descriptor.getExtensionReceiverParameter() != null ? 1 : 0) +
(descriptor.getDispatchReceiverParameter() != null ? 1 : 0);
return (descriptor.isVar() ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType();
}
}
@@ -532,4 +532,16 @@ public abstract class MemberCodegen<T extends JetElement/* TODO: & JetDeclaratio
}
return sourceMapper;
}
protected void generateConstInstance(@NotNull Type asmType) {
v.newField(OtherOrigin(element), ACC_STATIC | ACC_FINAL | ACC_PUBLIC, JvmAbi.INSTANCE_FIELD, asmType.getDescriptor(), null, null);
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
InstructionAdapter iv = createOrGetClInitCodegen().v;
iv.anew(asmType);
iv.dup();
iv.invokespecial(asmType.getInternalName(), "<init>", "()V", false);
iv.putstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor());
}
}
}
@@ -0,0 +1,201 @@
/*
* 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 org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.AsmUtil.method
import org.jetbrains.kotlin.codegen.AsmUtil.writeKotlinSyntheticClassAnnotation
import org.jetbrains.kotlin.codegen.context.ClassContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinSyntheticClass
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.JetCallableReferenceExpression
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.scopes.receivers.ScriptReceiver
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_FINAL
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_SUPER
import org.jetbrains.org.objectweb.asm.Opcodes.V1_6
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.Method
public class PropertyReferenceCodegen(
state: GenerationState,
parentCodegen: MemberCodegen<*>,
context: ClassContext,
expression: JetCallableReferenceExpression,
classBuilder: ClassBuilder,
private val classDescriptor: ClassDescriptor,
private val resolvedCall: ResolvedCall<VariableDescriptor>
) : MemberCodegen<JetCallableReferenceExpression>(state, parentCodegen, context, expression, classBuilder) {
private val target = resolvedCall.getResultingDescriptor()
private val asmType = typeMapper.mapClass(classDescriptor)
private val superAsmType: Type
init {
val superClass = classDescriptor.getSuperClassNotAny().sure { "No super class for $classDescriptor" }
superAsmType = typeMapper.mapClass(superClass)
}
override fun generateDeclaration() {
v.defineClass(
element,
V1_6,
ACC_FINAL or ACC_SUPER or AsmUtil.getVisibilityAccessFlagForAnonymous(classDescriptor), // TODO: test inline
asmType.getInternalName(),
null,
superAsmType.getInternalName(),
emptyArray()
)
v.visitSource(element.getContainingFile().getName(), null)
}
// TODO: ImplementationBodyCodegen.markLineNumberForSyntheticFunction?
override fun generateBody() {
// TODO: instance should be already wrapped by Reflection
generateConstInstance(asmType)
generateMethod("property reference init", 0, method("<init>", Type.VOID_TYPE)) {
load(0, OBJECT_TYPE)
invokespecial(superAsmType.getInternalName(), "<init>", "()V", false)
}
generateMethod("property reference getOwner", ACC_PUBLIC, method("getOwner", K_DECLARATION_CONTAINER_TYPE)) {
ClosureCodegen.generateCallableReferenceDeclarationContainer(this, target, typeMapper)
}
generateMethod("property reference getName", ACC_PUBLIC, method("getName", JAVA_STRING_TYPE)) {
aconst(target.getName().asString())
}
generateMethod("property reference getSignature", ACC_PUBLIC, method("getSignature", JAVA_STRING_TYPE)) {
target as PropertyDescriptor
val getter = target.getGetter() ?: run {
val defaultGetter = DescriptorFactory.createDefaultGetter(target)
defaultGetter.initialize(target.getType())
defaultGetter
}
val method = typeMapper.mapSignature(getter.sure { "No getter: $target" }).getAsmMethod()
aconst(method.getName() + method.getDescriptor())
}
generateAccessors()
}
private fun generateAccessors() {
val dispatchReceiver = resolvedCall.getDispatchReceiver()
val extensionReceiver = resolvedCall.getExtensionReceiver()
val receiverType =
when {
dispatchReceiver is ScriptReceiver -> {
// TODO: fix receiver for scripts, see ScriptReceiver#getType
dispatchReceiver.getDeclarationDescriptor().getClassDescriptor().getDefaultType()
}
dispatchReceiver.exists() -> dispatchReceiver.getType()
extensionReceiver.exists() -> extensionReceiver.getType()
else -> null
}
fun generateAccessor(method: Method, accessorBody: InstructionAdapter.(StackValue) -> Unit) {
generateMethod("property reference $method", ACC_PUBLIC, method) {
// Note: this descriptor is an inaccurate representation of the get/set method. In particular, it has incorrect
// return type and value parameter types. However, it's created only to be able to use
// ExpressionCodegen#intermediateValueForProperty, which is poorly coupled with everything else.
val fakeDescriptor = SimpleFunctionDescriptorImpl.create(
classDescriptor, Annotations.EMPTY, Name.identifier(method.getName()), CallableMemberDescriptor.Kind.DECLARATION,
SourceElement.NO_SOURCE
)
fakeDescriptor.initialize(null, classDescriptor.getThisAsReceiverParameter(), emptyList(), emptyList(),
classDescriptor.builtIns.getAnyType(), Modality.OPEN, Visibilities.PUBLIC)
val fakeCodegen = ExpressionCodegen(
this, FrameMap(), OBJECT_TYPE, context.intoFunction(fakeDescriptor), state, this@PropertyReferenceCodegen
)
val receiver =
if (receiverType != null) StackValue.coercion(StackValue.local(1, OBJECT_TYPE), typeMapper.mapType(receiverType))
else StackValue.none()
val value = fakeCodegen.intermediateValueForProperty(target as PropertyDescriptor, false, null, receiver)
accessorBody(value)
}
}
val getterParameters = if (receiverType != null) arrayOf(OBJECT_TYPE) else emptyArray()
generateAccessor(method("get", OBJECT_TYPE, *getterParameters)) { value ->
value.put(OBJECT_TYPE, this)
}
if (!target.isVar()) return
val setterParameters = (getterParameters + arrayOf(OBJECT_TYPE)).toTypedArray()
generateAccessor(method("set", Type.VOID_TYPE, *setterParameters)) { value ->
// Hard-coded 1 or 2 is safe here because there's only java/lang/Object in the signature, no double/long parameters
value.store(StackValue.local(if (receiverType != null) 2 else 1, OBJECT_TYPE), this)
}
}
private fun generateMethod(debugString: String, access: Int, method: Method, generate: InstructionAdapter.() -> Unit) {
val mv = v.newMethod(JvmDeclarationOrigin.NO_ORIGIN, access, method.getName(), method.getDescriptor(), null, null)
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
val iv = InstructionAdapter(mv)
iv.visitCode()
iv.generate()
iv.areturn(method.getReturnType())
FunctionCodegen.endVisit(mv, debugString, element)
}
}
override fun generateKotlinAnnotation() {
writeKotlinSyntheticClassAnnotation(v, KotlinSyntheticClass.Kind.CALLABLE_REFERENCE_WRAPPER)
}
public fun putInstanceOnStack(): StackValue {
val hasReceiver = target.getDispatchReceiverParameter() != null || target.getExtensionReceiverParameter() != null
val method =
when {
hasReceiver -> when {
target.isVar() -> method("mutableProperty1", K_MUTABLE_PROPERTY1_TYPE, MUTABLE_PROPERTY_REFERENCE1)
else -> method("property1", K_PROPERTY1_TYPE, PROPERTY_REFERENCE1)
}
else -> when {
target.isVar() -> method("mutableProperty0", K_MUTABLE_PROPERTY0_TYPE, MUTABLE_PROPERTY_REFERENCE0)
else -> method("property0", K_PROPERTY0_TYPE, PROPERTY_REFERENCE0)
}
}
return StackValue.operation(method.getReturnType()) { iv ->
iv.getstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, asmType.getDescriptor())
iv.invokestatic(REFLECTION, method.getName(), method.getDescriptor(), false)
}
}
}
@@ -84,15 +84,15 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
}
@NotNull
private ClassDescriptor recordClassForFunction(
private ClassDescriptor recordClassForCallable(
@NotNull JetElement element,
@NotNull FunctionDescriptor funDescriptor,
@NotNull CallableDescriptor callableDescriptor,
@NotNull Collection<JetType> supertypes,
@NotNull String name
) {
String simpleName = name.substring(name.lastIndexOf('/') + 1);
ClassDescriptorImpl classDescriptor = new ClassDescriptorImpl(
correctContainerForLambda(funDescriptor, element),
correctContainerForLambda(callableDescriptor, element),
Name.special("<closure-" + simpleName + ">"),
Modality.FINAL,
supertypes,
@@ -100,13 +100,13 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
);
classDescriptor.initialize(JetScope.Empty.INSTANCE$, Collections.<ConstructorDescriptor>emptySet(), null);
bindingTrace.record(CLASS_FOR_FUNCTION, funDescriptor, classDescriptor);
bindingTrace.record(CLASS_FOR_CALLABLE, callableDescriptor, classDescriptor);
return classDescriptor;
}
@NotNull
@SuppressWarnings("ConstantConditions")
private DeclarationDescriptor correctContainerForLambda(@NotNull FunctionDescriptor descriptor, @NotNull JetElement function) {
private DeclarationDescriptor correctContainerForLambda(@NotNull CallableDescriptor descriptor, @NotNull JetElement function) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
// In almost all cases the function's direct container is the correct container to consider in JVM back-end
@@ -286,7 +286,7 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
String name = inventAnonymousClassName(expression);
Collection<JetType> supertypes = runtimeTypes.getSupertypesForClosure(functionDescriptor);
ClassDescriptor classDescriptor = recordClassForFunction(functionLiteral, functionDescriptor, supertypes, name);
ClassDescriptor classDescriptor = recordClassForCallable(functionLiteral, functionDescriptor, supertypes, name);
recordClosure(classDescriptor, name);
classStack.push(classDescriptor);
@@ -298,17 +298,31 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
@Override
public void visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression) {
FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
// working around a problem with shallow analysis
if (functionDescriptor == null) return;
ResolvedCall<?> referencedFunction = CallUtilPackage.getResolvedCall(expression.getCallableReference(), bindingContext);
if (referencedFunction == null) return;
Collection<JetType> supertypes =
runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) referencedFunction.getResultingDescriptor());
CallableDescriptor target = referencedFunction.getResultingDescriptor();
CallableDescriptor callableDescriptor;
Collection<JetType> supertypes;
if (target instanceof FunctionDescriptor) {
callableDescriptor = bindingContext.get(FUNCTION, expression);
if (callableDescriptor == null) return;
supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target);
}
else if (target instanceof PropertyDescriptor) {
callableDescriptor = bindingContext.get(VARIABLE, expression);
if (callableDescriptor == null) return;
supertypes = Collections.singleton(runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target));
}
else {
return;
}
String name = inventAnonymousClassName(expression);
ClassDescriptor classDescriptor = recordClassForFunction(expression, functionDescriptor, supertypes, name);
ClassDescriptor classDescriptor = recordClassForCallable(expression, callableDescriptor, supertypes, name);
recordClosure(classDescriptor, name);
classStack.push(classDescriptor);
@@ -354,7 +368,7 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
else {
String name = inventAnonymousClassName(function);
Collection<JetType> supertypes = runtimeTypes.getSupertypesForClosure(functionDescriptor);
ClassDescriptor classDescriptor = recordClassForFunction(function, functionDescriptor, supertypes, name);
ClassDescriptor classDescriptor = recordClassForCallable(function, functionDescriptor, supertypes, name);
recordClosure(classDescriptor, name);
classStack.push(classDescriptor);
@@ -40,16 +40,14 @@ import org.jetbrains.org.objectweb.asm.Type;
import java.util.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isInterface;
import static org.jetbrains.kotlin.resolve.BindingContext.*;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
import static org.jetbrains.kotlin.resolve.source.SourcePackage.toSourceElement;
public class CodegenBinding {
public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
public static final WritableSlice<FunctionDescriptor, ClassDescriptor> CLASS_FOR_FUNCTION = Slices.createSimpleSlice();
public static final WritableSlice<CallableDescriptor, ClassDescriptor> CLASS_FOR_CALLABLE = Slices.createSimpleSlice();
public static final WritableSlice<ScriptDescriptor, ClassDescriptor> CLASS_FOR_SCRIPT = Slices.createSimpleSlice();
@@ -110,34 +108,41 @@ public class CodegenBinding {
}
@NotNull
public static ClassDescriptor anonymousClassForFunction(
public static ClassDescriptor anonymousClassForCallable(
@NotNull BindingContext bindingContext,
@NotNull FunctionDescriptor descriptor
@NotNull CallableDescriptor descriptor
) {
//noinspection ConstantConditions
return bindingContext.get(CLASS_FOR_FUNCTION, descriptor);
return bindingContext.get(CLASS_FOR_CALLABLE, descriptor);
}
@NotNull
public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull JetElement expression) {
if (expression instanceof JetObjectLiteralExpression) {
JetObjectLiteralExpression jetObjectLiteralExpression = (JetObjectLiteralExpression) expression;
expression = jetObjectLiteralExpression.getObjectDeclaration();
expression = ((JetObjectLiteralExpression) expression).getObjectDeclaration();
}
ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
if (descriptor == null) {
SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
assert functionDescriptor != null : "Couldn't find function descriptor for " + PsiUtilPackage.getElementTextWithContext(expression);
if (descriptor != null) {
return getAsmType(bindingContext, descriptor);
}
SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
if (functionDescriptor != null) {
return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
}
return getAsmType(bindingContext, descriptor);
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
if (variableDescriptor != null) {
return asmTypeForAnonymousClass(bindingContext, variableDescriptor);
}
throw new IllegalStateException("Couldn't compute ASM type for " + PsiUtilPackage.getElementTextWithContext(expression));
}
@NotNull
public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) {
return getAsmType(bindingContext, anonymousClassForFunction(bindingContext, descriptor));
public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull CallableDescriptor descriptor) {
return getAsmType(bindingContext, anonymousClassForCallable(bindingContext, descriptor));
}
public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.codegen.OwnerKind;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.anonymousClassForFunction;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.anonymousClassForCallable;
public class ClosureContext extends ClassContext {
private final FunctionDescriptor functionDescriptor;
@@ -33,7 +33,7 @@ public class ClosureContext extends ClassContext {
@Nullable CodegenContext parentContext,
@NotNull LocalLookup localLookup
) {
super(typeMapper, anonymousClassForFunction(typeMapper.getBindingContext(), functionDescriptor),
super(typeMapper, anonymousClassForCallable(typeMapper.getBindingContext(), functionDescriptor),
OwnerKind.IMPLEMENTATION, parentContext, localLookup);
this.functionDescriptor = functionDescriptor;
@@ -103,7 +103,7 @@ public interface LocalLookup {
BindingContext bindingContext = state.getBindingContext();
Type localType = asmTypeForAnonymousClass(bindingContext, vd);
MutableClosure localFunClosure = bindingContext.get(CLOSURE, bindingContext.get(CLASS_FOR_FUNCTION, vd));
MutableClosure localFunClosure = bindingContext.get(CLOSURE, bindingContext.get(CLASS_FOR_CALLABLE, vd));
if (localFunClosure != null && JvmCodegenUtil.isConst(localFunClosure)) {
// This is an optimization: we can obtain an instance of a const closure simply by GETSTATIC ...$instance
// (instead of passing this instance to the constructor and storing as a field)
@@ -234,7 +234,7 @@ public class InlineCodegenUtil {
return type.getInternalName();
} else if (currentDescriptor instanceof FunctionDescriptor) {
ClassDescriptor descriptor =
typeMapper.getBindingContext().get(CodegenBinding.CLASS_FOR_FUNCTION, (FunctionDescriptor) currentDescriptor);
typeMapper.getBindingContext().get(CodegenBinding.CLASS_FOR_CALLABLE, (FunctionDescriptor) currentDescriptor);
if (descriptor != null) {
Type type = typeMapper.mapType(descriptor);
return type.getInternalName();
@@ -70,7 +70,7 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression);
assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText();
classDescriptor = anonymousClassForFunction(bindingContext, functionDescriptor);
classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor);
closureClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor);
closure = bindingContext.get(CLOSURE, classDescriptor);