Generate bytecode for delegated properties

This commit is contained in:
Natalia.Ukhorskaya
2013-04-22 13:59:37 +04:00
parent 3297c0e9d3
commit 6e2584d0de
33 changed files with 731 additions and 50 deletions
@@ -263,10 +263,11 @@ public class CodegenUtil {
return false;
}
public static boolean couldUseDirectAccessToProperty(PropertyDescriptor propertyDescriptor, boolean forGetter, boolean isInsideClass) {
public static boolean couldUseDirectAccessToProperty(PropertyDescriptor propertyDescriptor, boolean forGetter, boolean isInsideClass, boolean isDelegated) {
PropertyAccessorDescriptor accessorDescriptor = forGetter ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
boolean isExtensionProperty = propertyDescriptor.getReceiverParameter() != null;
return isInsideClass &&
!isDelegated &&
!isExtensionProperty &&
(accessorDescriptor == null ||
accessorDescriptor.isDefault() &&
@@ -1660,6 +1660,16 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return myFrameMap.getIndex(descriptor);
}
@Nullable
private static JetType getPropertyDelegateType(@NotNull PropertyDescriptor descriptor, @NotNull BindingContext bindingContext) {
PropertyGetterDescriptor getter = descriptor.getGetter();
if (getter != null) {
Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, getter);
return call != null ? call.getExplicitReceiver().getType() : null;
}
return null;
}
@NotNull
public StackValue.Property intermediateValueForProperty(
PropertyDescriptor propertyDescriptor,
@@ -1688,12 +1698,15 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
boolean isInsideClass = isCallInsideSameClassAsDeclared(propertyDescriptor, context);
boolean isInsideModule = isCallInsideSameModuleAsDeclared(propertyDescriptor, context);
JetType delegateType = getPropertyDelegateType(propertyDescriptor, state.getBindingContext());
boolean isDelegatedProperty = delegateType != null;
CallableMethod callableGetter = null;
CallableMethod callableSetter = null;
if (!forceField) {
//noinspection ConstantConditions
if (couldUseDirectAccessToProperty(propertyDescriptor, true, isInsideClass)) {
if (couldUseDirectAccessToProperty(propertyDescriptor, true, isInsideClass, isDelegatedProperty)) {
callableGetter = null;
}
else {
@@ -1715,7 +1728,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
if (propertyDescriptor.isVar()) {
if (propertyDescriptor.getSetter() != null) {
if (couldUseDirectAccessToProperty(propertyDescriptor, false, isInsideClass)) {
if (couldUseDirectAccessToProperty(propertyDescriptor, false, isInsideClass, isDelegatedProperty)) {
callableSetter = null;
}
else {
@@ -1736,8 +1749,14 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
owner = callableMethod.getOwner();
}
return StackValue.property(propertyDescriptor, owner, typeMapper.mapType(propertyDescriptor.getOriginal().getType()),
isStatic, callableGetter, callableSetter, state);
if (isDelegatedProperty && forceField) {
return StackValue.property(propertyDescriptor, owner, typeMapper.mapType(delegateType),
isStatic, true, callableGetter, callableSetter, state);
}
else {
return StackValue.property(propertyDescriptor, owner, typeMapper.mapType(propertyDescriptor.getOriginal().getType()),
isStatic, false, callableGetter, callableSetter, state);
}
}
@Override
@@ -1864,7 +1883,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return (MemberDescriptor) descriptor;
}
private StackValue invokeFunction(
public StackValue invokeFunction(
Call call,
StackValue receiver,
ResolvedCall<? extends CallableDescriptor> resolvedCall
@@ -1531,10 +1531,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) bindingContext.get(BindingContext.VARIABLE, property);
assert propertyDescriptor != null;
JetExpression initializer = property.getInitializer();
JetExpression initializer = property.getDelegateExpressionOrInitializer();
assert initializer != null : "shouldInitializeProperty must return false if initializer is null";
JetType jetType = propertyDescriptor.getType();
JetType jetType = getPropertyOrDelegateType(bindingContext, property, propertyDescriptor);
Type type = codegen.expressionType(initializer);
if (jetType.isNullable()) {
type = boxType(type);
@@ -1542,14 +1542,14 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
codegen.gen(initializer, type);
StackValue.Property propValue = codegen.intermediateValueForProperty(propertyDescriptor, true, null);
propValue.store(propValue.type, iv);
propValue.store(type, iv);
}
public static boolean shouldInitializeProperty(
@NotNull JetProperty property,
@NotNull JetTypeMapper typeMapper
) {
JetExpression initializer = property.getInitializer();
JetExpression initializer = property.getDelegateExpressionOrInitializer();
if (initializer == null) return false;
CompileTimeConstant<?> compileTimeValue = typeMapper.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, initializer);
@@ -1559,10 +1559,22 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
assert propertyDescriptor != null;
Object value = compileTimeValue.getValue();
Type type = typeMapper.mapType(propertyDescriptor.getType());
JetType jetType = getPropertyOrDelegateType(typeMapper.getBindingContext(), property, propertyDescriptor);
Type type = typeMapper.mapType(jetType);
return !skipDefaultValue(propertyDescriptor, value, type);
}
@NotNull
private static JetType getPropertyOrDelegateType(@NotNull BindingContext bindingContext, @NotNull JetProperty property, @NotNull PropertyDescriptor descriptor) {
JetExpression delegateExpression = property.getDelegateExpression();
if (delegateExpression != null) {
JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, delegateExpression);
assert delegateType != null : "Type of delegate expression should be recorded";
return delegateType;
}
return descriptor.getType();
}
private static boolean skipDefaultValue(PropertyDescriptor propertyDescriptor, Object value, Type type) {
if (isPrimitive(type)) {
if (!propertyDescriptor.getType().isNullable() && value instanceof Number) {
@@ -34,11 +34,13 @@ import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.DescriptorResolver;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames;
import org.jetbrains.jet.lang.resolve.java.kt.DescriptorKindUtils;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import static org.jetbrains.asm4.Opcodes.*;
@@ -86,38 +88,63 @@ public class PropertyCodegen extends GenerationStateAware {
private void generateBackingField(PsiElement p, PropertyDescriptor propertyDescriptor) {
//noinspection ConstantConditions
if (bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
boolean hasBackingField = bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
boolean isDelegated = p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null;
if (hasBackingField || isDelegated) {
DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
if (isInterface(containingDeclaration)) {
return;
}
Object value = null;
JetExpression initializer = p instanceof JetProperty ? ((JetProperty) p).getInitializer() : null;
if (initializer != null) {
if (initializer instanceof JetConstantExpression && !propertyDescriptor.getType().isNullable()) {
CompileTimeConstant<?> compileTimeValue = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, initializer);
value = compileTimeValue != null ? compileTimeValue.getValue() : null;
}
}
int modifiers;
if (kind == OwnerKind.NAMESPACE) {
modifiers = ACC_STATIC;
}
else {
modifiers = ACC_PRIVATE;
}
if (!propertyDescriptor.isVar()) {
modifiers |= ACC_FINAL;
}
modifiers |= getDeprecatedAccessFlag(propertyDescriptor);
if (KotlinBuiltIns.getInstance().isVolatile(propertyDescriptor)) {
modifiers |= ACC_VOLATILE;
}
Type type = typeMapper.mapType(propertyDescriptor);
FieldVisitor fieldVisitor = v.newField(p, modifiers, propertyDescriptor.getName().getName(), type.getDescriptor(), null, value);
FieldVisitor fieldVisitor = hasBackingField
? generateBackingFieldAccess(p, propertyDescriptor)
: generatePropertyDelegateAccess((JetProperty) p, propertyDescriptor);
AnnotationCodegen.forField(fieldVisitor, typeMapper).genAnnotations(propertyDescriptor);
}
}
private FieldVisitor generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) {
int modifiers = ACC_PRIVATE | ACC_FINAL | getDeprecatedAccessFlag(propertyDescriptor);
if (kind == OwnerKind.NAMESPACE) {
modifiers |= ACC_STATIC;
}
if (KotlinBuiltIns.getInstance().isVolatile(propertyDescriptor)) {
modifiers |= ACC_VOLATILE;
}
JetType delegateType = bindingContext.get(BindingContext.EXPRESSION_TYPE, p.getDelegateExpression());
assert delegateType != null : "Type of delegate should be recorded: " + p.getText();
Type type = typeMapper.mapType(delegateType);
return v.newField(p, modifiers, JvmAbi.getPropertyDelegateName(propertyDescriptor.getName()), type.getDescriptor(),
null, null);
}
private FieldVisitor generateBackingFieldAccess(PsiElement p, PropertyDescriptor propertyDescriptor) {
Object value = null;
JetExpression initializer = p instanceof JetProperty ? ((JetProperty) p).getInitializer() : null;
if (initializer != null) {
if (initializer instanceof JetConstantExpression && !propertyDescriptor.getType().isNullable()) {
CompileTimeConstant<?> compileTimeValue = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, initializer);
value = compileTimeValue != null ? compileTimeValue.getValue() : null;
}
}
int modifiers;
if (kind == OwnerKind.NAMESPACE) {
modifiers = ACC_STATIC;
}
else {
modifiers = ACC_PRIVATE;
}
if (!propertyDescriptor.isVar()) {
modifiers |= ACC_FINAL;
}
modifiers |= getDeprecatedAccessFlag(propertyDescriptor);
if (KotlinBuiltIns.getInstance().isVolatile(propertyDescriptor)) {
modifiers |= ACC_VOLATILE;
}
Type type = typeMapper.mapType(propertyDescriptor);
return v.newField(p, modifiers, propertyDescriptor.getName().getName(), type.getDescriptor(), null, value);
}
private void generateGetter(JetNamedDeclaration p, PropertyDescriptor propertyDescriptor, JetPropertyAccessor getter) {
@@ -130,10 +157,18 @@ public class PropertyCodegen extends GenerationStateAware {
getterDescriptor = getterDescriptor != null ? getterDescriptor : DescriptorResolver.createDefaultGetter(propertyDescriptor);
if (kind != OwnerKind.TRAIT_IMPL || !defaultGetter) {
FunctionGenerationStrategy strategy =
defaultGetter
? new DefaultPropertyAccessorStrategy(getterDescriptor)
: new FunctionGenerationStrategy.FunctionDefault(state, getterDescriptor, getter);
FunctionGenerationStrategy strategy;
if (defaultGetter) {
if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) {
strategy = new DefaultPropertyWithDelegateAccessorStrategy(getterDescriptor);
}
else {
strategy = new DefaultPropertyAccessorStrategy(getterDescriptor);
}
}
else {
strategy = new FunctionGenerationStrategy.FunctionDefault(state, getterDescriptor, getter);
}
functionCodegen.generateMethod(getter != null ? getter : p,
signature,
true,
@@ -153,10 +188,18 @@ public class PropertyCodegen extends GenerationStateAware {
setterDescriptor = setterDescriptor != null ? setterDescriptor : DescriptorResolver.createDefaultSetter(propertyDescriptor);
if (kind != OwnerKind.TRAIT_IMPL || !defaultSetter) {
FunctionGenerationStrategy strategy =
defaultSetter
? new DefaultPropertyAccessorStrategy(setterDescriptor)
: new FunctionGenerationStrategy.FunctionDefault(state, setterDescriptor, setter);
FunctionGenerationStrategy strategy;
if (defaultSetter) {
if (p instanceof JetProperty && ((JetProperty) p).getDelegateExpression() != null) {
strategy = new DefaultPropertyWithDelegateAccessorStrategy(setterDescriptor);
}
else {
strategy = new DefaultPropertyAccessorStrategy(setterDescriptor);
}
}
else {
strategy = new FunctionGenerationStrategy.FunctionDefault(state, setterDescriptor, setter);
}
functionCodegen.generateMethod(setter != null ? setter : p,
signature,
true,
@@ -227,6 +270,49 @@ public class PropertyCodegen extends GenerationStateAware {
}
}
private class DefaultPropertyWithDelegateAccessorStrategy extends FunctionGenerationStrategy {
private final PropertyAccessorDescriptor descriptor;
public DefaultPropertyWithDelegateAccessorStrategy(@NotNull PropertyAccessorDescriptor descriptor) {
this.descriptor = descriptor;
}
@Override
public void generateBody(
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature signature,
@NotNull MethodContext context
) {
InstructionAdapter iv = new InstructionAdapter(mv);
ExpressionCodegen codegen = new ExpressionCodegen(
mv, getFrameMap(typeMapper, context), signature.getAsmMethod().getReturnType(), context, state);
ResolvedCall<FunctionDescriptor> resolvedCall =
bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, descriptor);
Call call = bindingContext.get(BindingContext.DELEGATED_PROPERTY_CALL, descriptor);
assert call != null : "Call should be recorded for delegate call " + signature.toString();
PropertyDescriptor property = descriptor.getCorrespondingProperty();
Type asmType = typeMapper.mapType(property);
if (kind != OwnerKind.NAMESPACE) {
iv.load(0, OBJECT_TYPE);
}
StackValue.Property delegatedProperty = codegen.intermediateValueForProperty(property, true, null);
StackValue lastValue = codegen.invokeFunction(call, delegatedProperty, resolvedCall);
if (lastValue.type != Type.VOID_TYPE) {
lastValue.put(asmType, iv);
iv.areturn(asmType);
}
else {
iv.areturn(Type.VOID_TYPE);
}
}
}
public static void generateJetPropertyAnnotation(
MethodVisitor mv, @NotNull JvmPropertyAccessorSignature propertyAccessorSignature,
@NotNull PropertyDescriptor propertyDescriptor, @NotNull Visibility visibility
@@ -30,6 +30,7 @@ import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
@@ -139,12 +140,12 @@ public abstract class StackValue {
JvmClassName methodOwner,
Type type,
boolean isStatic,
boolean isDelegated,
@Nullable CallableMethod getter,
@Nullable CallableMethod setter,
GenerationState state
) {
return new Property(descriptor, methodOwner, getter, setter, isStatic, type,
state);
return new Property(descriptor, methodOwner, getter, setter, isStatic, isDelegated, type, state);
}
public static StackValue expression(Type type, JetExpression expression, ExpressionCodegen generator) {
@@ -869,11 +870,12 @@ public abstract class StackValue {
private final PropertyDescriptor descriptor;
@NotNull
private final GenerationState state;
private final boolean isDelegated;
public Property(
@NotNull PropertyDescriptor descriptor, @NotNull JvmClassName methodOwner,
@Nullable CallableMethod getter, @Nullable CallableMethod setter, boolean isStatic,
@NotNull Type type, @NotNull GenerationState state
boolean isDelegated, @NotNull Type type, @NotNull GenerationState state
) {
super(type, isStatic);
this.methodOwner = methodOwner;
@@ -881,12 +883,13 @@ public abstract class StackValue {
this.setter = setter;
this.descriptor = descriptor;
this.state = state;
this.isDelegated = isDelegated;
}
@Override
public void put(Type type, InstructionAdapter v) {
if (getter == null) {
v.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, methodOwner.getInternalName(), descriptor.getName().getName(),
v.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, methodOwner.getInternalName(), getPropertyName(),
this.type.getDescriptor());
genNotNullAssertionForField(v, state, descriptor);
}
@@ -901,13 +904,17 @@ public abstract class StackValue {
public void store(Type topOfStackType, InstructionAdapter v) {
coerceFrom(topOfStackType, v);
if (setter == null) {
v.visitFieldInsn(isStatic ? PUTSTATIC : PUTFIELD, methodOwner.getInternalName(), descriptor.getName().getName(),
v.visitFieldInsn(isStatic ? PUTSTATIC : PUTFIELD, methodOwner.getInternalName(), getPropertyName(),
this.type.getDescriptor()); }
else {
Method method = setter.getSignature().getAsmMethod();
v.visitMethodInsn(setter.getInvokeOpcode(), setter.getOwner().getInternalName(), method.getName(), method.getDescriptor());
}
}
private String getPropertyName() {
return isDelegated ? JvmAbi.getPropertyDelegateName(descriptor.getName()) : descriptor.getName().getName();
}
}
private static class Expression extends StackValue {