diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index edfe00eb1ee..6d2dc5c2557 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -93,7 +93,7 @@ public class ExpressionCodegen extends JetVisitor implem private final Type returnType; private final BindingContext bindingContext; - final MethodContext context; + private final MethodContext context; private final CodegenStatementVisitor statementVisitor; private final Stack blockStackElements = new Stack(); @@ -105,8 +105,8 @@ public class ExpressionCodegen extends JetVisitor implem * When we create a temporary variable to hold some value not to compute it many times * we put it into this map to emit access to that variable instead of evaluating the whole expression */ - private final Map tempVariables = Maps.newHashMap(); - @NotNull + final Map tempVariables = Maps.newHashMap(); + private final TailRecursionCodegen tailRecursionCodegen; public final CallGenerator defaultCallGenerator; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 71acad3ba6a..3ca1e8b57fd 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -442,6 +442,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { @Override protected void generateSyntheticParts() { + generateDelegatedPropertyMetadataArray(); + generateFieldForSingleton(); generateClassObjectBackingFieldCopies(); @@ -474,6 +476,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { genClosureFields(context.closure, v, state.getTypeMapper()); } + private void generateDelegatedPropertyMetadataArray() { + if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { + generatePropertyMetadataArrayFieldIfNeeded(classAsmType, myClass); + } + } + private boolean isGenericToArrayPresent() { Collection functions = descriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier("toArray")); for (FunctionDescriptor function : functions) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/MemberCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/MemberCodegen.java index 82b9b83de3d..8babb0e6deb 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/MemberCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/MemberCodegen.java @@ -31,7 +31,9 @@ import org.jetbrains.jet.lang.descriptors.annotations.Annotations; import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.name.SpecialNames; import org.jetbrains.jet.lang.types.ErrorUtils; @@ -40,15 +42,18 @@ import org.jetbrains.jet.storage.LockBasedStorageManager; import org.jetbrains.jet.storage.NotNullLazyValue; import org.jetbrains.org.objectweb.asm.MethodVisitor; import org.jetbrains.org.objectweb.asm.Type; +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import static org.jetbrains.jet.codegen.AsmUtil.boxType; import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive; import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED; -import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; -import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.jetbrains.jet.lang.resolve.BindingContext.VARIABLE; +import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*; +import static org.jetbrains.org.objectweb.asm.Opcodes.*; public abstract class MemberCodegen extends ParentCodegenAwareImpl { protected final FieldOwnerContext context; @@ -193,7 +198,7 @@ public abstract class MemberCodegen extends ParentCodegenAwareImpl { } protected void initializeProperty(@NotNull ExpressionCodegen codegen, @NotNull JetProperty property) { - PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(BindingContext.VARIABLE, property); + PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property); assert propertyDescriptor != null; JetExpression initializer = property.getDelegateExpressionOrInitializer(); @@ -220,7 +225,7 @@ public abstract class MemberCodegen extends ParentCodegenAwareImpl { JetExpression initializer = property.getDelegateExpressionOrInitializer(); if (initializer == null) return false; - PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(BindingContext.VARIABLE, property); + PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(VARIABLE, property); assert propertyDescriptor != null; CompileTimeConstant compileTimeValue = propertyDescriptor.getCompileTimeInitializer(); @@ -281,4 +286,38 @@ public abstract class MemberCodegen extends ParentCodegenAwareImpl { } return false; } + + protected void generatePropertyMetadataArrayFieldIfNeeded(@NotNull Type thisAsmType, @NotNull JetDeclarationContainer container) { + List delegatedProperties = new ArrayList(); + for (JetDeclaration declaration : container.getDeclarations()) { + if (declaration instanceof JetProperty) { + JetProperty property = (JetProperty) declaration; + if (property.getDelegate() != null) { + delegatedProperties.add(property); + } + } + } + if (delegatedProperties.isEmpty()) return; + + v.newField(null, ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, JvmAbi.PROPERTY_METADATA_ARRAY_NAME, + "[" + PROPERTY_METADATA_TYPE, null, null); + + InstructionAdapter iv = createOrGetClInitCodegen().v; + iv.iconst(delegatedProperties.size()); + iv.newarray(PROPERTY_METADATA_TYPE); + + for (int i = 0, size = delegatedProperties.size(); i < size; i++) { + VariableDescriptor property = BindingContextUtils.getNotNull(bindingContext, VARIABLE, delegatedProperties.get(i)); + + iv.dup(); + iv.iconst(i); + iv.anew(PROPERTY_METADATA_IMPL_TYPE); + iv.dup(); + iv.visitLdcInsn(property.getName().asString()); + iv.invokespecial(PROPERTY_METADATA_IMPL_TYPE.getInternalName(), "", "(Ljava/lang/String;)V"); + iv.astore(PROPERTY_METADATA_IMPL_TYPE); + } + + iv.putstatic(thisAsmType.getInternalName(), JvmAbi.PROPERTY_METADATA_ARRAY_NAME, "[" + PROPERTY_METADATA_TYPE); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/PackagePartCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/PackagePartCodegen.java index 9efd357a491..cafb125db50 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/PackagePartCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/PackagePartCodegen.java @@ -74,6 +74,8 @@ public class PackagePartCodegen extends MemberCodegen { } }); + generatePropertyMetadataArrayFieldIfNeeded(packagePartType, jetFile); + if (clInit != null) { clInit.v.visitInsn(RETURN); FunctionCodegen.endVisit(clInit.v, "static initializer for package", jetFile); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java index 175c2fc1118..f021d119586 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java @@ -18,10 +18,10 @@ package org.jetbrains.jet.codegen; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.codegen.context.FieldOwnerContext; -import org.jetbrains.jet.codegen.context.PackageFacadeContext; +import org.jetbrains.jet.codegen.context.*; import org.jetbrains.jet.codegen.signature.JvmMethodSignature; import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.codegen.state.GenerationStateAware; @@ -49,6 +49,7 @@ import static org.jetbrains.jet.codegen.CodegenUtil.isInterface; import static org.jetbrains.jet.codegen.JvmSerializationBindings.*; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; +import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.PROPERTY_METADATA_TYPE; import static org.jetbrains.org.objectweb.asm.Opcodes.*; public class PropertyCodegen extends GenerationStateAware { @@ -291,8 +292,8 @@ public class PropertyCodegen extends GenerationStateAware { FunctionGenerationStrategy strategy; if (isDefaultAccessor) { - if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) { - strategy = new DefaultPropertyWithDelegateAccessorStrategy(state, accessorDescriptor); + if (p instanceof JetProperty && ((JetProperty) p).getDelegate() != null) { + strategy = new DelegatedPropertyAccessorStrategy(state, accessorDescriptor, indexOfDelegatedProperty((JetProperty) p)); } else { strategy = new DefaultPropertyAccessorStrategy(state, accessorDescriptor); @@ -306,6 +307,32 @@ public class PropertyCodegen extends GenerationStateAware { functionCodegen.generateMethod(accessor != null ? accessor : p, signature, accessorDescriptor, strategy); } + private static int indexOfDelegatedProperty(@NotNull JetProperty property) { + PsiElement parent = property.getParent(); + JetDeclarationContainer container; + if (parent instanceof JetClassBody) { + container = ((JetClassOrObject) parent.getParent()); + } + else if (parent instanceof JetFile) { + container = (JetFile) parent; + } + else { + throw new UnsupportedOperationException("Unknown delegated property container: " + parent); + } + + int index = 0; + for (JetDeclaration declaration : container.getDeclarations()) { + if (declaration instanceof JetProperty && ((JetProperty) declaration).getDelegate() != null) { + if (declaration == property) { + return index; + } + index++; + } + } + + throw new IllegalStateException("Delegated property not found in its parent: " + JetPsiUtil.getElementTextWithContext(property)); + } + private static class DefaultPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased { public DefaultPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) { @@ -318,7 +345,7 @@ public class PropertyCodegen extends GenerationStateAware { PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty(); int paramCode = 0; - if (codegen.context.getContextKind() != OwnerKind.PACKAGE) { + if (codegen.getContext().getContextKind() != OwnerKind.PACKAGE) { v.load(0, OBJECT_TYPE); paramCode = 1; } @@ -345,9 +372,12 @@ public class PropertyCodegen extends GenerationStateAware { } } - private static class DefaultPropertyWithDelegateAccessorStrategy extends FunctionGenerationStrategy.CodegenBased { - public DefaultPropertyWithDelegateAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) { + private static class DelegatedPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased { + private final int index; + + public DelegatedPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor, int index) { super(state, descriptor); + this.index = index; } @Override @@ -361,13 +391,35 @@ public class PropertyCodegen extends GenerationStateAware { Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, callableDescriptor); assert call != null : "Call should be recorded for delegate call " + signature.toString(); - if (codegen.context.getContextKind() != OwnerKind.PACKAGE) { + if (codegen.getContext().getContextKind() != OwnerKind.PACKAGE) { v.load(0, OBJECT_TYPE); } - PropertyDescriptor propertyDescriptor = callableDescriptor.getCorrespondingProperty(); + CodegenContext ownerContext = codegen.getContext().getClassOrPackageParentContext(); + final Type owner; + if (ownerContext instanceof ClassContext) { + owner = state.getTypeMapper().mapClass(((ClassContext) ownerContext).getContextDescriptor()); + } + else if (ownerContext instanceof PackageContext) { + owner = ((PackageContext) ownerContext).getPackagePartType(); + } + else { + throw new UnsupportedOperationException("Unknown context: " + ownerContext); + } - StackValue delegatedProperty = codegen.intermediateValueForProperty(propertyDescriptor, true, null); + codegen.tempVariables.put( + call.getValueArguments().get(1).asElement(), + new StackValue(PROPERTY_METADATA_TYPE) { + @Override + public void put(Type type, InstructionAdapter v) { + v.getstatic(owner.getInternalName(), JvmAbi.PROPERTY_METADATA_ARRAY_NAME, "[" + PROPERTY_METADATA_TYPE); + v.iconst(index); + StackValue.arrayElement(PROPERTY_METADATA_TYPE).put(type, v); + } + } + ); + + StackValue delegatedProperty = codegen.intermediateValueForProperty(callableDescriptor.getCorrespondingProperty(), true, null); StackValue lastValue = codegen.invokeFunction(call, delegatedProperty, resolvedCall); Type asmType = signature.getReturnType(); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/context/CodegenContext.java b/compiler/backend/src/org/jetbrains/jet/codegen/context/CodegenContext.java index 0f00bbedf7b..9af831cd3be 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/context/CodegenContext.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/context/CodegenContext.java @@ -19,7 +19,6 @@ package org.jetbrains.jet.codegen.context; import kotlin.Function0; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.jet.codegen.*; import org.jetbrains.jet.codegen.binding.CodegenBinding; import org.jetbrains.jet.codegen.binding.MutableClosure; @@ -33,17 +32,18 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.storage.LockBasedStorageManager; import org.jetbrains.jet.storage.NullableLazyValue; +import org.jetbrains.org.objectweb.asm.Type; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE; -import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PROTECTED; import static org.jetbrains.jet.codegen.AsmUtil.CAPTURED_THIS_FIELD; import static org.jetbrains.jet.codegen.AsmUtil.getVisibilityAccessFlag; import static org.jetbrains.jet.codegen.binding.CodegenBinding.*; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; +import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_PROTECTED; public abstract class CodegenContext { @@ -103,17 +103,15 @@ public abstract class CodegenContext { } @NotNull - public ClassOrPackageFragmentDescriptor getClassOrPackageDescriptor() { - CodegenContext c = this; + @SuppressWarnings("unchecked") + public CodegenContext getClassOrPackageParentContext() { + CodegenContext context = this; while (true) { - assert c != null; - DeclarationDescriptor contextDescriptor = c.getContextDescriptor(); - if (contextDescriptor instanceof ClassOrPackageFragmentDescriptor) { - return (ClassOrPackageFragmentDescriptor) contextDescriptor; - } - else { - c = c.getParentContext(); + if (context.getContextDescriptor() instanceof ClassOrPackageFragmentDescriptor) { + return (CodegenContext) context; } + context = context.getParentContext(); + assert context != null : "Context which is not top-level has no parent: " + this; } } @@ -413,7 +411,7 @@ public abstract class CodegenContext { } CodegenContext descriptorContext = null; - if (!fromOutsideContext || getClassOrPackageDescriptor() != descriptor.getContainingDeclaration()) { + if (!fromOutsideContext || getClassOrPackageParentContext().getContextDescriptor() != descriptor.getContainingDeclaration()) { DeclarationDescriptor enclosed = descriptor.getContainingDeclaration(); boolean isClassObjectMember = DescriptorUtils.isClassObject(enclosed); //go upper diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AsmTypeConstants.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AsmTypeConstants.java index 2930952026c..873dff87312 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AsmTypeConstants.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AsmTypeConstants.java @@ -35,6 +35,8 @@ public class AsmTypeConstants { public static final Type FUNCTION0_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/Function0"); public static final Type FUNCTION1_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/Function1"); public static final Type INT_RANGE_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/IntRange"); + public static final Type PROPERTY_METADATA_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/PropertyMetadata"); + public static final Type PROPERTY_METADATA_IMPL_TYPE = Type.getObjectType(BUILT_INS_PACKAGE_FQ_NAME + "/PropertyMetadataImpl"); public static final Type OBJECT_REF_TYPE = Type.getObjectType("kotlin/jvm/internal/Ref$ObjectRef"); diff --git a/compiler/testData/codegen/box/delegatedProperty/propertyMetadataShouldBeCached.kt b/compiler/testData/codegen/box/delegatedProperty/propertyMetadataShouldBeCached.kt new file mode 100644 index 00000000000..de5710ed11b --- /dev/null +++ b/compiler/testData/codegen/box/delegatedProperty/propertyMetadataShouldBeCached.kt @@ -0,0 +1,46 @@ +import java.util.IdentityHashMap + +class A { + var foo: Int by IntHandler + + class object { + var bar: Any? by AnyHandler + } +} + +val baz: String by StringHandler + + + +val metadatas = IdentityHashMap() + +fun record(p: PropertyMetadata) = metadatas.put(p, Unit.VALUE) + +object IntHandler { + fun get(t: Any?, p: PropertyMetadata): Int { record(p); return 42 } + fun set(t: Any?, p: PropertyMetadata, value: Int) { record(p) } +} + +object AnyHandler { + fun get(t: Any?, p: PropertyMetadata): Any? { record(p); return 3.14 } + fun set(t: Any?, p: PropertyMetadata, value: Any?) { record(p) } +} + +object StringHandler { + fun get(t: Any?, p: PropertyMetadata): String { record(p); return p.name } + fun set(t: Any?, p: PropertyMetadata, value: String) { record(p) } +} + +fun box(): String { + val a = A() + a.foo = 42 + a.foo = a.foo + baz.length + a.foo = 239 + A.bar = baz + a.foo + baz + A.bar + + if (metadatas.keySet().size() != 3) + return "Fail: only three instances of PropertyMetadata should have been created\n${metadatas.keySet()}" + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index 1b7c1bd6eb5..339aab09911 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -2299,6 +2299,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest("compiler/testData/codegen/box/delegatedProperty/privateVar.kt"); } + @TestMetadata("propertyMetadataShouldBeCached.kt") + public void testPropertyMetadataShouldBeCached() throws Exception { + doTest("compiler/testData/codegen/box/delegatedProperty/propertyMetadataShouldBeCached.kt"); + } + @TestMetadata("setAsExtensionFun.kt") public void testSetAsExtensionFun() throws Exception { doTest("compiler/testData/codegen/box/delegatedProperty/setAsExtensionFun.kt"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/JvmAbi.java b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/JvmAbi.java index 273ab3cdf04..965b0bd175c 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/JvmAbi.java +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/JvmAbi.java @@ -38,6 +38,7 @@ public final class JvmAbi { public static final String CLASS_OBJECT_SUFFIX = "$" + CLASS_OBJECT_CLASS_NAME; public static final String DELEGATED_PROPERTY_NAME_SUFFIX = "$delegate"; + public static final String PROPERTY_METADATA_ARRAY_NAME = "$propertyMetadata"; public static final String ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX = "$annotations"; public static final String INSTANCE_FIELD = "instance$";