Optimize PropertyMetadataImpl instance creation

Create all property metadatas in <clinit> and save them to a static array
$propertyMetadata. Getter/setter of each delegated property will then just
obtain the corresponding instance from that array

 #KT-4232 Fixed
This commit is contained in:
Alexander Udalov
2014-04-22 22:59:48 +04:00
parent 925b27238c
commit 61675de305
10 changed files with 183 additions and 30 deletions
@@ -93,7 +93,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
private final Type returnType;
private final BindingContext bindingContext;
final MethodContext context;
private final MethodContext context;
private final CodegenStatementVisitor statementVisitor;
private final Stack<BlockStackElement> blockStackElements = new Stack<BlockStackElement>();
@@ -105,8 +105,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> 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<JetElement, StackValue.Local> tempVariables = Maps.newHashMap();
@NotNull
final Map<JetElement, StackValue> tempVariables = Maps.newHashMap();
private final TailRecursionCodegen tailRecursionCodegen;
public final CallGenerator defaultCallGenerator;
@@ -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<FunctionDescriptor> functions = descriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier("toArray"));
for (FunctionDescriptor function : functions) {
@@ -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<JetProperty> delegatedProperties = new ArrayList<JetProperty>();
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(), "<init>", "(Ljava/lang/String;)V");
iv.astore(PROPERTY_METADATA_IMPL_TYPE);
}
iv.putstatic(thisAsmType.getInternalName(), JvmAbi.PROPERTY_METADATA_ARRAY_NAME, "[" + PROPERTY_METADATA_TYPE);
}
}
@@ -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);
@@ -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<PropertyAccessorDescriptor> {
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<PropertyAccessorDescriptor> {
public DefaultPropertyWithDelegateAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) {
private static class DelegatedPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased<PropertyAccessorDescriptor> {
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<? extends ClassOrPackageFragmentDescriptor> 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();
@@ -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<T extends DeclarationDescriptor> {
@@ -103,17 +103,15 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
}
@NotNull
public ClassOrPackageFragmentDescriptor getClassOrPackageDescriptor() {
CodegenContext c = this;
@SuppressWarnings("unchecked")
public CodegenContext<? extends ClassOrPackageFragmentDescriptor> 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<T extends DeclarationDescriptor> {
}
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
@@ -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");
@@ -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<PropertyMetadata, Unit>()
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"
}
@@ -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");
@@ -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$";