Generate bytecode for delegated properties
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user