org.jetbrains.kotlin.codegen.StackValue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen;
import com.intellij.psi.tree.IElementType;
import kotlin.Unit;
import kotlin.collections.ArraysKt;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapperBase;
import org.jetbrains.kotlin.codegen.state.StaticTypeMapperForOldBackend;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
import org.jetbrains.kotlin.load.java.DescriptorsJvmAbiUtil;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.ValueArgument;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor;
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.SimpleType;
import org.jetbrains.kotlin.types.model.KotlinTypeMarker;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLASS_FOR_CALLABLE;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.RECURSIVE_SUSPEND_CALLABLE_REFERENCE;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public abstract class StackValue {
private static final String NULLABLE_BYTE_TYPE_NAME = "java/lang/Byte";
private static final String NULLABLE_SHORT_TYPE_NAME = "java/lang/Short";
private static final String NULLABLE_LONG_TYPE_NAME = "java/lang/Long";
public static final StackValue.Local LOCAL_0 = local(0, OBJECT_TYPE);
private static final StackValue UNIT = operation(UNIT_TYPE, v -> {
v.visitFieldInsn(GETSTATIC, UNIT_TYPE.getInternalName(), JvmAbi.INSTANCE_FIELD, UNIT_TYPE.getDescriptor());
return null;
});
@NotNull
public final Type type;
@Nullable
public final KotlinType kotlinType;
private final boolean canHaveSideEffects;
protected StackValue(@NotNull Type type) {
this(type, null, true);
}
protected StackValue(@NotNull Type type, boolean canHaveSideEffects) {
this(type, null, canHaveSideEffects);
}
protected StackValue(@NotNull Type type, @Nullable KotlinType kotlinType) {
this(type, kotlinType, true);
}
protected StackValue(@NotNull Type type, @Nullable KotlinType kotlinType, boolean canHaveSideEffects) {
this.type = type;
this.kotlinType = kotlinType;
this.canHaveSideEffects = canHaveSideEffects;
}
/**
* This method is called to put the value on the top of the JVM stack if depth
other values have been put on the
* JVM stack after this value was generated.
*
* @param type the type as which the value should be put
* @param v the visitor used to genClassOrObject the instructions
* @param depth the number of new values put onto the stack
*/
public void moveToTopOfStack(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v, int depth) {
put(type, kotlinType, v);
}
public void put(@NotNull InstructionAdapter v) {
put(type, null, v, false);
}
public void put(@NotNull Type type, @NotNull InstructionAdapter v) {
put(type, null, v, false);
}
public void put(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
put(type, kotlinType, v, false);
}
public void put(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v, boolean skipReceiver) {
if (!skipReceiver) {
putReceiver(v, true);
}
putSelector(type, kotlinType, v);
}
public abstract void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v);
public boolean isNonStaticAccess(boolean isRead) {
return false;
}
public void putReceiver(@NotNull InstructionAdapter v, boolean isRead) {
//by default there is no receiver
//if you have it inherit StackValueWithSimpleReceiver
}
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
if (!Type.VOID_TYPE.equals(type)) {
AsmUtil.dup(v, type);
}
}
public void store(@NotNull StackValue value, @NotNull InstructionAdapter v) {
store(value, v, false);
}
public boolean canHaveSideEffects() {
return canHaveSideEffects;
}
public void store(@NotNull StackValue value, @NotNull InstructionAdapter v, boolean skipReceiver) {
if (!skipReceiver) {
putReceiver(v, false);
}
value.put(value.type, value.kotlinType, v);
storeSelector(value.type, value.kotlinType, v);
}
protected void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
throw new UnsupportedOperationException("Cannot store to value " + this);
}
@NotNull
public static Local local(int index, @NotNull Type type) {
return new Local(index, type);
}
public static Local local(int index, @NotNull Type type, @Nullable KotlinType kotlinType) {
return new Local(index, type, kotlinType);
}
@NotNull
public static StackValue local(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor) {
return local(index, type, descriptor, null);
}
@NotNull
public static StackValue local(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor, @Nullable KotlinType delegateKotlinType) {
if (descriptor.isLateInit()) {
assert delegateKotlinType == null :
"Delegated property can't be lateinit: " + descriptor + ", delegate type: " + delegateKotlinType;
return new LateinitLocal(index, type, descriptor.getType(), descriptor.getName());
}
else {
return new Local(
index, type,
delegateKotlinType != null ? delegateKotlinType : descriptor.getType()
);
}
}
@NotNull
public static Delegate localDelegate(
@NotNull Type type,
@NotNull StackValue delegateValue,
@NotNull StackValue metadataValue,
@NotNull VariableDescriptorWithAccessors variableDescriptor,
@NotNull ExpressionCodegen codegen
) {
return new Delegate(type, delegateValue, metadataValue, variableDescriptor, codegen);
}
@NotNull
public static StackValue shared(int index, @NotNull Type type) {
return new Shared(index, type);
}
@NotNull
public static StackValue shared(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor) {
return shared(index, type, descriptor, null);
}
@NotNull
public static StackValue shared(int index, @NotNull Type type, @NotNull VariableDescriptor descriptor, @Nullable KotlinType delegateKotlinType) {
return new Shared(
index, type,
delegateKotlinType != null ? delegateKotlinType : descriptor.getType(),
descriptor.isLateInit(), descriptor.getName()
);
}
@NotNull
public static StackValue onStack(@NotNull Type type) {
return onStack(type, null);
}
@NotNull
public static StackValue onStack(@NotNull Type type, @Nullable KotlinType kotlinType) {
return type == Type.VOID_TYPE ? none() : new OnStack(type, kotlinType);
}
@NotNull
public static StackValue integerConstant(int value, @NotNull Type type) {
if (type == Type.LONG_TYPE) {
return constant(Long.valueOf(value), type);
}
else if (type == Type.BYTE_TYPE || type == Type.SHORT_TYPE || type == Type.INT_TYPE) {
return constant(Integer.valueOf(value), type);
}
else if (type == Type.CHAR_TYPE) {
return constant(Character.valueOf((char) value), type);
}
else {
throw new AssertionError("Unexpected integer type: " + type);
}
}
@NotNull
public static StackValue constant(int value) {
return constant(value, Type.INT_TYPE);
}
@NotNull
public static StackValue constant(@Nullable Object value, @NotNull Type type) {
return constant(value, type, null);
}
@NotNull
public static StackValue constant(@Nullable Object value, @NotNull Type type, @Nullable KotlinType kotlinType) {
if (type == Type.BOOLEAN_TYPE) {
assert value instanceof Boolean : "Value for boolean constant should have boolean type: " + value;
return BranchedValue.Companion.booleanConstant((Boolean) value);
}
else {
return new Constant(value, type, kotlinType);
}
}
public static StackValue createDefaultValue(@NotNull Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return constant(null, type);
}
else {
return createDefaultPrimitiveValue(type);
}
}
private static StackValue createDefaultPrimitiveValue(@NotNull Type type) {
assert Type.BOOLEAN <= type.getSort() && type.getSort() <= Type.DOUBLE :
"'createDefaultPrimitiveValue' method should be called only for primitive types, but " + type;
Object value = 0;
if (type.getSort() == Type.BOOLEAN) {
value = Boolean.FALSE;
}
else if (type.getSort() == Type.FLOAT) {
value = new Float(0.0);
}
else if (type.getSort() == Type.DOUBLE) {
value = new Double(0.0);
}
else if (type.getSort() == Type.LONG) {
value = new Long(0);
}
return constant(value, type);
}
@NotNull
public static StackValue cmp(@NotNull IElementType opToken, @NotNull Type type, StackValue left, StackValue right) {
return BranchedValue.Companion.cmp(opToken, type, left, right);
}
@NotNull
public static StackValue not(@NotNull StackValue stackValue) {
return BranchedValue.Companion.createInvertValue(stackValue);
}
public static StackValue or(@NotNull StackValue left, @NotNull StackValue right) {
return new Or(left, right);
}
public static StackValue and(@NotNull StackValue left, @NotNull StackValue right) {
return new And(left, right);
}
public static StackValue compareIntWithZero(@NotNull StackValue argument, int operation) {
return new BranchedValue(argument, null, Type.INT_TYPE, operation);
}
public static StackValue compareWithNull(@NotNull StackValue argument, int operation) {
return new BranchedValue(argument, null, AsmTypes.OBJECT_TYPE, operation);
}
@NotNull
public static StackValue arrayElement(@NotNull Type type, @Nullable KotlinType kotlinType, StackValue array, StackValue index) {
return new ArrayElement(type, kotlinType, array, index);
}
@NotNull
public static StackValue collectionElement(
CollectionElementReceiver collectionElementReceiver,
Type type,
KotlinType kotlinType,
ResolvedCall getter,
ResolvedCall setter,
ExpressionCodegen codegen
) {
return new CollectionElement(collectionElementReceiver, type, kotlinType, getter, setter, codegen);
}
public static UnderlyingValueOfInlineClass underlyingValueOfInlineClass(
@NotNull Type type,
@Nullable KotlinType kotlinType,
@NotNull StackValue receiver
) {
return new UnderlyingValueOfInlineClass(type, kotlinType, receiver);
}
@NotNull
public static Field field(@NotNull Type type, @NotNull Type owner, @NotNull String name, boolean isStatic, @NotNull StackValue receiver) {
return field(type, null, owner, name, isStatic, receiver);
}
@NotNull
public static Field field(
@NotNull Type type,
@Nullable KotlinType kotlinType,
@NotNull Type owner,
@NotNull String name,
boolean isStatic,
@NotNull StackValue receiver
) {
return field(type, kotlinType, owner, name, isStatic, receiver, null);
}
@NotNull
public static Field field(
@NotNull Type type,
@Nullable KotlinType kotlinType,
@NotNull Type owner,
@NotNull String name,
boolean isStatic,
@NotNull StackValue receiver,
@Nullable DeclarationDescriptor descriptor
) {
return new Field(type, kotlinType, owner, name, isStatic, receiver, descriptor);
}
@NotNull
public static Field field(@NotNull StackValue.Field field, @NotNull StackValue newReceiver) {
return field(field.type, field.kotlinType, field.owner, field.name, field.isStaticPut, newReceiver, field.descriptor);
}
@NotNull
public static Field field(@NotNull FieldInfo info, @NotNull StackValue receiver) {
return field(
info.getFieldType(),
info.getFieldKotlinType(),
Type.getObjectType(info.getOwnerInternalName()),
info.getFieldName(),
info.isStatic(),
receiver
);
}
@NotNull
public static StackValue changeReceiverForFieldAndSharedVar(@NotNull StackValueWithSimpleReceiver stackValue, @Nullable StackValue newReceiver) {
//TODO static check
if (newReceiver == null || stackValue.isStaticPut) return stackValue;
return stackValue.changeReceiver(newReceiver);
}
@NotNull
public static Property property(
@NotNull PropertyDescriptor descriptor,
@Nullable Type backingFieldOwner,
@NotNull Type type,
boolean isStaticBackingField,
@Nullable String fieldName,
@Nullable CallableMethod getter,
@Nullable CallableMethod setter,
@NotNull StackValue receiver,
@NotNull ExpressionCodegen codegen,
@Nullable ResolvedCall resolvedCall,
boolean skipLateinitAssertion,
@Nullable KotlinType delegateKotlinType
) {
return new Property(descriptor, backingFieldOwner, getter, setter, isStaticBackingField, fieldName, type, receiver, codegen,
resolvedCall, skipLateinitAssertion, delegateKotlinType);
}
@NotNull
public static StackValue expression(Type type, KtExpression expression, ExpressionCodegen generator) {
return new Expression(type, expression, generator);
}
private static void box(Type type, Type toType, InstructionAdapter v) {
if (type == Type.INT_TYPE) {
if (toType.getInternalName().equals(NULLABLE_BYTE_TYPE_NAME)) {
type = Type.BYTE_TYPE;
}
else if (toType.getInternalName().equals(NULLABLE_SHORT_TYPE_NAME)) {
type = Type.SHORT_TYPE;
}
else if (toType.getInternalName().equals(NULLABLE_LONG_TYPE_NAME)) {
type = Type.LONG_TYPE;
}
v.cast(Type.INT_TYPE, type);
}
Type boxedType = AsmUtil.boxType(type);
if (boxedType == type) return;
v.invokestatic(boxedType.getInternalName(), "valueOf", Type.getMethodDescriptor(boxedType, type), false);
coerce(boxedType, toType, v);
}
private static void unbox(Type methodOwner, Type type, InstructionAdapter v) {
assert isPrimitive(type) : "Unboxing should be performed to primitive type, but " + type.getClassName();
v.invokevirtual(methodOwner.getInternalName(), type.getClassName() + "Value", "()" + type.getDescriptor(), false);
}
public static void boxInlineClass(
@NotNull KotlinTypeMarker kotlinType, @NotNull InstructionAdapter v, @NotNull KotlinTypeMapperBase typeMapper
) {
Type boxed = typeMapper.mapTypeCommon(kotlinType, TypeMappingMode.CLASS_DECLARATION);
Type unboxed = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType, typeMapper);
boolean isNullable = typeMapper.getTypeSystem().isNullableType(kotlinType) && !isPrimitive(unboxed);
boxInlineClass(unboxed, boxed, isNullable, v);
}
public static void boxInlineClass(
@NotNull Type unboxed, @NotNull Type boxed, boolean isNullable, @NotNull InstructionAdapter v
) {
if (isNullable) {
boxOrUnboxWithNullCheck(v, vv -> invokeBoxMethod(vv, boxed, unboxed));
} else {
invokeBoxMethod(v, boxed, unboxed);
}
}
private static void invokeBoxMethod(
@NotNull InstructionAdapter v,
@NotNull Type boxedType,
@NotNull Type underlyingType
) {
v.invokestatic(
boxedType.getInternalName(),
KotlinTypeMapper.BOX_JVM_METHOD_NAME,
Type.getMethodDescriptor(boxedType, underlyingType),
false
);
}
public static void unboxInlineClass(
@NotNull Type type,
@NotNull KotlinTypeMarker targetInlineClassType,
@NotNull InstructionAdapter v,
@NotNull KotlinTypeMapperBase typeMapper
) {
Type boxed = typeMapper.mapTypeCommon(targetInlineClassType, TypeMappingMode.CLASS_DECLARATION);
Type unboxed = KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(targetInlineClassType, typeMapper);
boolean isNullable = typeMapper.getTypeSystem().isNullableType(targetInlineClassType) && !isPrimitive(unboxed);
unboxInlineClass(type, boxed, unboxed, isNullable, v);
}
public static void unboxInlineClass(
@NotNull Type type, @NotNull Type boxed, @NotNull Type unboxed, boolean isNullable, @NotNull InstructionAdapter v
) {
coerce(type, boxed, v);
if (isNullable) {
boxOrUnboxWithNullCheck(v, vv -> invokeUnboxMethod(vv, boxed, unboxed));
} else {
invokeUnboxMethod(v, boxed, unboxed);
}
}
private static void invokeUnboxMethod(
@NotNull InstructionAdapter v,
@NotNull Type owner,
@NotNull Type resultType
) {
v.invokevirtual(
owner.getInternalName(),
KotlinTypeMapper.UNBOX_JVM_METHOD_NAME,
"()" + resultType.getDescriptor(),
false
);
}
private static void boxOrUnboxWithNullCheck(@NotNull InstructionAdapter v, @NotNull Consumer body) {
Label lNull = new Label();
Label lDone = new Label();
// NB The following piece of code looks sub-optimal (we have a 'null' value on stack and could just keep it there),
// but it is required, because bytecode verifier doesn't take into account null checks,
// and sees null-checked value on the top of the stack as a value of the source type (e.g., Ljava/lang/String;),
// which is not assignable to the expected type (destination type, e.g., LStr;).
v.dup();
v.ifnull(lNull);
body.accept(v);
v.goTo(lDone);
v.mark(lNull);
v.pop();
v.aconst(null);
v.mark(lDone);
}
protected void coerceTo(@NotNull Type toType, @Nullable KotlinType toKotlinType, @NotNull InstructionAdapter v) {
coerce(this.type, this.kotlinType, toType, toKotlinType, v);
}
protected void coerceFrom(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
coerce(topOfStackType, topOfStackKotlinType, this.type, this.kotlinType, v);
}
public static void coerce(
@NotNull Type fromType,
@Nullable KotlinType fromKotlinType,
@NotNull Type toType,
@Nullable KotlinType toKotlinType,
@NotNull InstructionAdapter v
) {
if (coerceInlineClasses(fromType, fromKotlinType, toType, toKotlinType, v, StaticTypeMapperForOldBackend.INSTANCE)) return;
coerce(fromType, toType, v);
}
public static boolean requiresInlineClassBoxingOrUnboxing(
@NotNull Type fromType,
@Nullable KotlinType fromKotlinType,
@NotNull Type toType,
@Nullable KotlinType toKotlinType
) {
// NB see also coerceInlineClasses below
if (fromKotlinType == null || toKotlinType == null) return false;
boolean isFromTypeInlineClass = InlineClassesUtilsKt.isInlineClassType(fromKotlinType);
boolean isToTypeInlineClass = InlineClassesUtilsKt.isInlineClassType(toKotlinType);
if (!isFromTypeInlineClass && !isToTypeInlineClass) return false;
boolean isFromTypeUnboxed = isFromTypeInlineClass && isUnboxedInlineClass(fromKotlinType, fromType);
boolean isToTypeUnboxed = isToTypeInlineClass && isUnboxedInlineClass(toKotlinType, toType);
if (isFromTypeInlineClass && isToTypeInlineClass) {
return isFromTypeUnboxed != isToTypeUnboxed;
}
else {
return isFromTypeInlineClass /* && !isToTypeInlineClass */ && isFromTypeUnboxed ||
isToTypeInlineClass /* && !isFromTypeInlineClass */ && isToTypeUnboxed;
}
}
private static boolean coerceInlineClasses(
@NotNull Type fromType,
@Nullable KotlinType fromKotlinType,
@NotNull Type toType,
@Nullable KotlinType toKotlinType,
@NotNull InstructionAdapter v,
@NotNull KotlinTypeMapperBase typeMapper
) {
// NB see also requiresInlineClassBoxingOrUnboxing above
if (fromKotlinType == null || toKotlinType == null) return false;
boolean isFromTypeInlineClass = InlineClassesUtilsKt.isInlineClassType(fromKotlinType);
boolean isToTypeInlineClass = InlineClassesUtilsKt.isInlineClassType(toKotlinType);
if (!isFromTypeInlineClass && !isToTypeInlineClass) return false;
if (fromKotlinType.equals(toKotlinType) && fromType.equals(toType)) return true;
/*
* Preconditions: one of the types is definitely inline class type and types are not equal
* Consider the following situations:
* - both types are inline class types: we do box/unbox only if they are not both boxed or unboxed
* - from type is inline class type: we should do box, because target type can be only "subtype" of inline class type (like Any)
* - target type is inline class type: we should do unbox, because from type can come from some 'is' check for object type
*
* "return true" means that types were coerced successfully and usual coercion shouldn't be evaluated
* */
if (isFromTypeInlineClass && isToTypeInlineClass) {
boolean isFromTypeUnboxed = isUnboxedInlineClass(fromKotlinType, fromType);
boolean isToTypeUnboxed = isUnboxedInlineClass(toKotlinType, toType);
if (isFromTypeUnboxed && !isToTypeUnboxed) {
boxInlineClass(fromKotlinType, v, typeMapper);
return true;
}
else if (!isFromTypeUnboxed && isToTypeUnboxed) {
unboxInlineClass(fromType, toKotlinType, v, typeMapper);
return true;
}
}
else if (isFromTypeInlineClass) {
if (isUnboxedInlineClass(fromKotlinType, fromType)) {
boxInlineClass(fromKotlinType, v, typeMapper);
return true;
}
}
else { // isToTypeInlineClass is `true`
if (isUnboxedInlineClass(toKotlinType, toType)) {
unboxInlineClass(fromType, toKotlinType, v, typeMapper);
return true;
}
}
return false;
}
public static boolean isUnboxedInlineClass(@NotNull KotlinType kotlinType, @NotNull Type actualType) {
return KotlinTypeMapper.mapUnderlyingTypeOfInlineClassType(kotlinType, StaticTypeMapperForOldBackend.INSTANCE).equals(actualType);
}
public static void coerce(@NotNull Type fromType, @NotNull Type toType, @NotNull InstructionAdapter v) {
coerce(fromType, toType, v, false);
}
public static void coerce(@NotNull Type fromType, @NotNull Type toType, @NotNull InstructionAdapter v, boolean forceSelfCast) {
if (toType.equals(fromType) && !forceSelfCast) return;
if (toType.getSort() == Type.VOID) {
pop(v, fromType);
}
else if (fromType.getSort() == Type.VOID) {
if (toType.equals(UNIT_TYPE) || toType.equals(OBJECT_TYPE)) {
putUnitInstance(v);
}
else {
pushDefaultValueOnStack(toType, v);
}
}
else if (toType.equals(UNIT_TYPE)) {
if (fromType.equals(getType(Object.class))) {
v.checkcast(UNIT_TYPE);
}
else if (!fromType.equals(getType(Void.class))) {
pop(v, fromType);
putUnitInstance(v);
}
}
else if (toType.getSort() == Type.ARRAY) {
if (fromType.getSort() != Type.ARRAY) {
v.checkcast(toType);
}
else if (toType.getDimensions() != fromType.getDimensions()) {
v.checkcast(toType);
}
else if (!toType.getElementType().equals(OBJECT_TYPE)) {
v.checkcast(toType);
}
}
else if (toType.getSort() == Type.OBJECT) {
if (fromType.getSort() == Type.OBJECT || fromType.getSort() == Type.ARRAY) {
if (!toType.equals(OBJECT_TYPE)) {
v.checkcast(toType);
}
}
else {
box(fromType, toType, v);
}
}
else if (fromType.getSort() == Type.OBJECT || fromType.getSort() == Type.ARRAY) {
// here toType is primitive and fromType is reference (object or array)
Type unboxedType = unboxPrimitiveTypeOrNull(fromType);
if (unboxedType != null) {
unbox(fromType, unboxedType, v);
coerce(unboxedType, toType, v);
}
else if (toType.getSort() == Type.BOOLEAN) {
coerce(fromType, BOOLEAN_WRAPPER_TYPE, v);
unbox(BOOLEAN_WRAPPER_TYPE, Type.BOOLEAN_TYPE, v);
}
else if (toType.getSort() == Type.CHAR) {
if (fromType.equals(NUMBER_TYPE)) {
unbox(NUMBER_TYPE, Type.INT_TYPE, v);
v.visitInsn(Opcodes.I2C);
}
else {
coerce(fromType, CHARACTER_WRAPPER_TYPE, v);
unbox(CHARACTER_WRAPPER_TYPE, Type.CHAR_TYPE, v);
}
}
else {
coerce(fromType, NUMBER_TYPE, v);
unbox(NUMBER_TYPE, toType, v);
}
}
else {
v.cast(fromType, toType);
}
}
public static void putUnitInstance(@NotNull InstructionAdapter v) {
unit().put(UNIT_TYPE, null, v);
}
public static StackValue unit() {
return UNIT;
}
public static StackValue none() {
return None.INSTANCE;
}
public static Field receiverWithRefWrapper(
@NotNull Type localType,
@NotNull Type classType,
@NotNull String fieldName,
@NotNull StackValue receiver,
@Nullable DeclarationDescriptor descriptor
) {
return field(sharedTypeForType(localType), null, classType, fieldName, false, receiver, descriptor);
}
public static FieldForSharedVar fieldForSharedVar(
@NotNull Type localType,
@NotNull Type classType,
@NotNull String fieldName,
@NotNull Field refWrapper,
@NotNull VariableDescriptor variableDescriptor
) {
return new FieldForSharedVar(
localType, variableDescriptor.getType(), classType, fieldName, refWrapper,
variableDescriptor.isLateInit(), variableDescriptor.getName()
);
}
@NotNull
public static FieldForSharedVar fieldForSharedVar(@NotNull FieldForSharedVar field, @NotNull StackValue newReceiver) {
Field oldReceiver = (Field) field.receiver;
Field newSharedVarReceiver = field(oldReceiver, newReceiver);
return new FieldForSharedVar(
field.type, field.kotlinType,
field.owner, field.name, newSharedVarReceiver, field.isLateinit, field.variableName
);
}
public static StackValue coercion(@NotNull StackValue value, @NotNull Type castType, @Nullable KotlinType castKotlinType) {
return coercionValueForArgumentOfInlineClassConstructor(value, castType, castKotlinType, null);
}
public static StackValue coercionValueForArgumentOfInlineClassConstructor(
@NotNull StackValue value,
@NotNull Type castType,
@Nullable KotlinType castKotlinType,
@Nullable KotlinType underlyingKotlinType
) {
boolean kotlinTypesAreEqual = value.kotlinType == null && castKotlinType == null ||
value.kotlinType != null && castKotlinType != null && castKotlinType.equals(value.kotlinType);
if (value.type.equals(castType) && kotlinTypesAreEqual) {
return value;
}
return new CoercionValue(value, castType, castKotlinType, underlyingKotlinType);
}
@NotNull
public static StackValue thisOrOuter(
@NotNull ExpressionCodegen codegen,
@NotNull ClassDescriptor descriptor,
boolean isSuper,
boolean castReceiver
) {
// Coerce 'this' for the case when it is smart cast.
// Do not coerce for other cases due to the 'protected' access issues (JVMS 7, 4.9.2 Structural Constraints).
boolean coerceType = descriptor.getKind() == ClassKind.INTERFACE || InlineClassesUtilsKt.isInlineClass(descriptor) ||
(castReceiver && !isSuper);
return new ThisOuter(codegen, descriptor, isSuper, coerceType);
}
public static StackValue postIncrement(int index, int increment) {
return new PostIncrement(index, increment);
}
public static StackValue preIncrementForLocalVar(int index, int increment, @Nullable KotlinType kotlinType) {
return new PreIncrementForLocalVar(index, increment, kotlinType);
}
public static StackValue preIncrement(
@NotNull Type type,
@NotNull StackValue stackValue,
int delta,
ResolvedCall resolvedCall,
@NotNull ExpressionCodegen codegen
) {
KotlinType kotlinType = stackValue.kotlinType;
if (stackValue instanceof StackValue.Local && Type.INT_TYPE == stackValue.type &&
kotlinType != null && KotlinBuiltIns.isPrimitiveType(kotlinType)
) {
return preIncrementForLocalVar(((StackValue.Local) stackValue).index, delta, kotlinType);
}
return new PrefixIncrement(type, stackValue, resolvedCall, codegen);
}
public static StackValue receiver(
ResolvedCall> resolvedCall,
StackValue receiver,
ExpressionCodegen codegen,
@Nullable Callable callableMethod
) {
ReceiverValue callDispatchReceiver = resolvedCall.getDispatchReceiver();
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
if (descriptor instanceof SyntheticFieldDescriptor) {
callDispatchReceiver = ((SyntheticFieldDescriptor) descriptor).getDispatchReceiverForBackend();
}
ReceiverValue callExtensionReceiver = resolvedCall.getExtensionReceiver();
boolean isImportedObjectMember = false;
if (descriptor instanceof ImportedFromObjectCallableDescriptor) {
isImportedObjectMember = true;
descriptor = ((ImportedFromObjectCallableDescriptor) descriptor).getCallableFromObject();
}
if (callDispatchReceiver != null || callExtensionReceiver != null
|| isLocalFunCall(callableMethod) || isImportedObjectMember) {
ReceiverParameterDescriptor dispatchReceiverParameter = descriptor.getDispatchReceiverParameter();
ReceiverParameterDescriptor extensionReceiverParameter = descriptor.getExtensionReceiverParameter();
if (descriptor instanceof SyntheticFieldDescriptor) {
dispatchReceiverParameter = ((SyntheticFieldDescriptor) descriptor).getDispatchReceiverParameterForBackend();
}
boolean hasExtensionReceiver = callExtensionReceiver != null;
StackValue dispatchReceiver = platformStaticCallIfPresent(
genReceiver(hasExtensionReceiver ? none() : receiver, codegen, descriptor, callableMethod, callDispatchReceiver, false),
descriptor
);
StackValue extensionReceiver = genReceiver(receiver, codegen, descriptor, callableMethod, callExtensionReceiver, true);
return CallReceiver.generateCallReceiver(
resolvedCall, codegen, callableMethod,
dispatchReceiverParameter, dispatchReceiver,
extensionReceiverParameter, extensionReceiver
);
}
return receiver;
}
private static StackValue genReceiver(
@NotNull StackValue receiver,
@NotNull ExpressionCodegen codegen,
@NotNull CallableDescriptor descriptor,
@Nullable Callable callableMethod,
@Nullable ReceiverValue receiverValue,
boolean isExtension
) {
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
if (receiver == none()) {
if (receiverValue != null) {
return codegen.generateReceiverValue(receiverValue, false);
}
else if (isLocalFunCall(callableMethod) && !isExtension) {
if (descriptor instanceof SimpleFunctionDescriptor) {
SimpleFunctionDescriptor initial =
CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction((SimpleFunctionDescriptor) descriptor);
if (initial != null && initial.isSuspend()) {
return putLocalSuspendFunctionOnStack(codegen, initial.getOriginal());
}
}
StackValue value = codegen.findLocalOrCapturedValue(descriptor.getOriginal());
assert value != null : "Local fun should be found in locals or in captured params: " + descriptor;
return value;
}
else if (!isExtension && DescriptorUtils.isObject(containingDeclaration)) {
// Object member could be imported by name, in which case it has no explicit dispatch receiver
return singleton((ClassDescriptor) containingDeclaration, codegen.typeMapper);
}
}
else if (receiverValue != null) {
return receiver;
}
return none();
}
private static StackValue putLocalSuspendFunctionOnStack(
@NotNull ExpressionCodegen codegen,
SimpleFunctionDescriptor callee
) {
// There can be three types of suspend local function calls:
// 1) normal call: we first define it as a closure and then call it
// 2) call using callable reference: in this case it is not local, but rather captured value
// 3) recursive call: we are in the middle of defining it, but, thankfully, we can simply call `this.invoke` to
// create new coroutine
// 4) Normal call, but the value is captured
// First, check whether this is a normal call
int index = codegen.lookupLocalIndex(callee);
if (index >= 0) {
// This is a normal local call
return local(index, OBJECT_TYPE);
}
// Then check for call inside a callable reference
BindingContext bindingContext = codegen.getBindingContext();
Type calleeType = CodegenBinding.asmTypeForAnonymousClass(bindingContext, callee);
if (codegen.context.hasThisDescriptor()) {
ClassDescriptor thisDescriptor = codegen.context.getThisDescriptor();
ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_CALLABLE, callee);
if (thisDescriptor instanceof SyntheticClassDescriptorForLambda &&
((SyntheticClassDescriptorForLambda) thisDescriptor).isCallableReference()) {
// Call is inside a callable reference
// if it is call to recursive local, just return this$0
Boolean isRecursive = bindingContext.get(RECURSIVE_SUSPEND_CALLABLE_REFERENCE, thisDescriptor);
if (isRecursive != null && isRecursive) {
assert classDescriptor != null : "No CLASS_FOR_CALLABLE" + callee;
return thisOrOuter(codegen, classDescriptor, false, false);
}
// Otherwise, just call constructor of the closure
return codegen.findCapturedValue(callee);
}
if (classDescriptor == thisDescriptor) {
// Recursive suspend local function, just call invoke on this, it will create new coroutine automatically
codegen.v.visitVarInsn(ALOAD, 0);
return onStack(calleeType);
}
}
// Otherwise, this is captured value
return codegen.findCapturedValue(callee);
}
private static StackValue platformStaticCallIfPresent(@NotNull StackValue resultReceiver, @NotNull CallableDescriptor descriptor) {
if (CodegenUtilKt.isJvmStaticInObjectOrClassOrInterface(descriptor)) {
if (resultReceiver.canHaveSideEffects()) {
return coercion(resultReceiver, Type.VOID_TYPE, null);
}
else {
return none();
}
}
return resultReceiver;
}
@Contract("null -> false")
static boolean isLocalFunCall(@Nullable Callable callableMethod) {
return callableMethod != null && callableMethod.getGenerateCalleeType() != null;
}
public static StackValue receiverWithoutReceiverArgument(StackValue receiverWithParameter) {
if (receiverWithParameter instanceof CallReceiver) {
return ((CallReceiver) receiverWithParameter).withoutReceiverArgument();
}
return receiverWithParameter;
}
@NotNull
public static Field enumEntry(@NotNull ClassDescriptor descriptor, @NotNull KotlinTypeMapper typeMapper) {
DeclarationDescriptor enumClass = descriptor.getContainingDeclaration();
assert DescriptorUtils.isEnumClass(enumClass) : "Enum entry should be declared in enum class: " + descriptor;
SimpleType enumType = ((ClassDescriptor) enumClass).getDefaultType();
Type type = typeMapper.mapType(enumType);
return field(type, enumType, type, descriptor.getName().asString(), true, none(), descriptor);
}
@NotNull
public static Field singleton(@NotNull ClassDescriptor classDescriptor, @NotNull KotlinTypeMapper typeMapper) {
return field(FieldInfo.createForSingleton(classDescriptor, typeMapper), none());
}
public static Field createSingletonViaInstance(@NotNull ClassDescriptor classDescriptor, @NotNull KotlinTypeMapper typeMapper, @NotNull String name) {
return field(FieldInfo.createSingletonViaInstance(classDescriptor, typeMapper, name), none());
}
public static StackValue operation(Type type, Function1 lambda) {
return operation(type, null, lambda);
}
public static StackValue operation(Type type, KotlinType kotlinType, Function1 lambda) {
return new OperationStackValue(type, kotlinType, lambda);
}
public static StackValue functionCall(Type type, KotlinType kotlinType, Function1 lambda) {
return new FunctionCallStackValue(type, kotlinType, lambda);
}
public static boolean couldSkipReceiverOnStaticCall(StackValue value) {
return value instanceof Local || value instanceof Constant;
}
private static class None extends StackValue {
public static final None INSTANCE = new None();
private None() {
super(Type.VOID_TYPE, false);
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
coerceTo(type, kotlinType, v);
}
}
public static class Local extends StackValue {
public final int index;
private Local(int index, Type type, KotlinType kotlinType) {
super(type, kotlinType, false);
if (index < 0) {
throw new IllegalStateException("local variable index must be non-negative");
}
this.index = index;
}
private Local(int index, Type type) {
this(index, type, null);
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
v.load(index, this.type);
coerceTo(type, kotlinType, v);
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
coerceFrom(topOfStackType, topOfStackKotlinType, v);
v.store(index, this.type);
}
}
public static class LateinitLocal extends StackValue {
public final int index;
private final Name name;
private LateinitLocal(int index, Type type, KotlinType kotlinType, Name name) {
super(type, kotlinType, false);
if (index < 0) {
throw new IllegalStateException("local variable index must be non-negative");
}
if (name == null) {
throw new IllegalArgumentException("Lateinit local variable should have name: #" + index + " " + type.getDescriptor());
}
this.index = index;
this.name = name;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
v.load(index, this.type);
StackValue.genNonNullAssertForLateinit(v, name.asString());
coerceTo(type, kotlinType, v);
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
coerceFrom(topOfStackType, topOfStackKotlinType, v);
v.store(index, this.type);
PseudoInsnsKt.storeNotNull(v);
}
}
public static class Delegate extends StackValue {
@NotNull
private final StackValue delegateValue;
@NotNull
private final StackValue metadataValue;
@NotNull
private final VariableDescriptorWithAccessors variableDescriptor;
@NotNull
private final ExpressionCodegen codegen;
private Delegate(
@NotNull Type type,
@NotNull StackValue delegateValue,
@NotNull StackValue metadataValue,
@NotNull VariableDescriptorWithAccessors variableDescriptor,
@NotNull ExpressionCodegen codegen
) {
super(type);
this.delegateValue = delegateValue;
this.metadataValue = metadataValue;
this.variableDescriptor = variableDescriptor;
this.codegen = codegen;
}
private ResolvedCall getResolvedCall(boolean isGetter) {
BindingContext bindingContext = codegen.getState().getBindingContext();
VariableAccessorDescriptor accessor = isGetter ? variableDescriptor.getGetter(): variableDescriptor.getSetter();
assert accessor != null : "Accessor descriptor for delegated local property should be present " + variableDescriptor;
ResolvedCall resolvedCall = bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor);
assert resolvedCall != null : "Resolve call should be recorded for delegate call " + variableDescriptor;
return resolvedCall;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
ResolvedCall resolvedCall = getResolvedCall(true);
List extends ValueArgument> arguments = resolvedCall.getCall().getValueArguments();
assert arguments.size() == 2 : "Resolved call for 'getValue' should have 2 arguments, but was " +
arguments.size() + ": " + resolvedCall;
codegen.tempVariables.put(arguments.get(0).asElement(), StackValue.constant(null, OBJECT_TYPE));
codegen.tempVariables.put(arguments.get(1).asElement(), metadataValue);
StackValue lastValue = codegen.invokeFunction(resolvedCall, delegateValue);
lastValue.put(type, kotlinType, v);
codegen.tempVariables.remove(arguments.get(0).asElement());
codegen.tempVariables.remove(arguments.get(1).asElement());
}
@Override
public void store(@NotNull StackValue rightSide, @NotNull InstructionAdapter v, boolean skipReceiver) {
ResolvedCall resolvedCall = getResolvedCall(false);
List extends ValueArgument> arguments = resolvedCall.getCall().getValueArguments();
assert arguments.size() == 3 : "Resolved call for 'setValue' should have 3 arguments, but was " +
arguments.size() + ": " + resolvedCall;
codegen.tempVariables.put(arguments.get(0).asElement(), StackValue.constant(null, OBJECT_TYPE));
codegen.tempVariables.put(arguments.get(1).asElement(), metadataValue);
codegen.tempVariables.put(arguments.get(2).asElement(), rightSide);
StackValue lastValue = codegen.invokeFunction(resolvedCall, delegateValue);
lastValue.put(Type.VOID_TYPE, null, v);
codegen.tempVariables.remove(arguments.get(0).asElement());
codegen.tempVariables.remove(arguments.get(1).asElement());
codegen.tempVariables.remove(arguments.get(2).asElement());
}
}
public static class OnStack extends StackValue {
public OnStack(Type type) {
this(type, null);
}
public OnStack(Type type, KotlinType kotlinType) {
super(type, kotlinType);
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
coerceTo(type, kotlinType, v);
}
@Override
public void moveToTopOfStack(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v, int depth) {
if (depth == 0) {
put(type, kotlinType, v);
}
else if (depth == 1) {
int size = this.type.getSize();
if (size == 1) {
v.swap();
}
else if (size == 2) {
v.dupX2();
v.pop();
}
else {
throw new UnsupportedOperationException("don't know how to move type " + type + " to top of stack");
}
coerceTo(type, kotlinType, v);
}
else if (depth == 2) {
int size = this.type.getSize();
if (size == 1) {
v.dup2X1();
v.pop2();
}
else if (size == 2) {
v.dup2X2();
v.pop2();
}
else {
throw new UnsupportedOperationException("don't know how to move type " + type + " to top of stack");
}
coerceTo(type, kotlinType, v);
}
else {
throw new UnsupportedOperationException("unsupported move-to-top depth " + depth);
}
}
}
public static class Constant extends StackValue {
@Nullable
public final Object value;
public Constant(@Nullable Object value, Type type, KotlinType kotlinType) {
super(type, kotlinType, false);
assert !Type.BOOLEAN_TYPE.equals(type) : "Boolean constants should be created via 'StackValue.constant'";
this.value = value;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
if (value instanceof Integer || value instanceof Byte || value instanceof Short) {
v.iconst(((Number) value).intValue());
}
else if (value instanceof Character) {
v.iconst(((Character) value).charValue());
}
else if (value instanceof Long) {
v.lconst((Long) value);
}
else if (value instanceof Float) {
v.fconst((Float) value);
}
else if (value instanceof Double) {
v.dconst((Double) value);
}
else {
v.aconst(value);
}
if (value != null || AsmUtil.isPrimitive(type)) {
coerceTo(type, kotlinType, v);
}
}
}
private static class ArrayElement extends StackValueWithSimpleReceiver {
private final Type type;
public ArrayElement(Type type, KotlinType kotlinType, StackValue array, StackValue index) {
super(type, kotlinType, false, false, new Receiver(Type.LONG_TYPE, array, index), true);
this.type = type;
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
coerceFrom(topOfStackType, topOfStackKotlinType, v);
v.astore(this.type);
}
@Override
public int receiverSize() {
return 2;
}
@Override
public void putSelector(
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
) {
v.aload(this.type); // assumes array and index are on the stack
coerceTo(type, kotlinType, v);
}
}
public static class UnderlyingValueOfInlineClass extends StackValueWithSimpleReceiver {
public UnderlyingValueOfInlineClass(
@NotNull Type type,
@Nullable KotlinType kotlinType,
@NotNull StackValue receiver
) {
super(type, kotlinType, false, false, receiver, true);
}
@Override
public void putSelector(
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
) {
coerceTo(type, kotlinType, v);
}
}
public static class CollectionElementReceiver extends StackValue {
private final Callable callable;
private final boolean isGetter;
private final ExpressionCodegen codegen;
private final List valueArguments;
private final FrameMap frame;
private final StackValue receiver;
private final ResolvedCall resolvedGetCall;
private final ResolvedCall resolvedSetCall;
private DefaultCallArgs defaultArgs;
private CallGenerator callGenerator;
boolean isComplexOperationWithDup;
public CollectionElementReceiver(
@NotNull Callable callable,
@NotNull StackValue receiver,
ResolvedCall resolvedGetCall,
ResolvedCall resolvedSetCall,
boolean isGetter,
@NotNull ExpressionCodegen codegen,
List valueArguments
) {
super(OBJECT_TYPE);
this.callable = callable;
this.isGetter = isGetter;
this.receiver = receiver;
this.resolvedGetCall = resolvedGetCall;
this.resolvedSetCall = resolvedSetCall;
this.valueArguments = valueArguments;
this.codegen = codegen;
this.frame = codegen.myFrameMap;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
ResolvedCall> call = isGetter ? resolvedGetCall : resolvedSetCall;
StackValue newReceiver = StackValue.receiver(call, receiver, codegen, callable);
ArgumentGenerator generator = createArgumentGenerator();
newReceiver.put(newReceiver.type, newReceiver.kotlinType, v);
callGenerator.processHiddenParameters();
callGenerator.putHiddenParamsIntoLocals();
defaultArgs = generator.generate(valueArguments, valueArguments, call.getResultingDescriptor());
}
private ArgumentGenerator createArgumentGenerator() {
assert callGenerator == null :
"'putSelector' and 'createArgumentGenerator' methods should be called once for CollectionElementReceiver: " + callable;
ResolvedCall resolvedCall = isGetter ? resolvedGetCall : resolvedSetCall;
assert resolvedCall != null : "Resolved call should be non-null: " + callable;
callGenerator =
!isComplexOperationWithDup ? codegen.getOrCreateCallGenerator(resolvedCall) : codegen.defaultCallGenerator;
return new CallBasedArgumentGenerator(
codegen,
callGenerator,
resolvedCall.getResultingDescriptor().getValueParameters(), callable.getValueParameterTypes()
);
}
@Override
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
dupReceiver(v);
}
public void dupReceiver(@NotNull InstructionAdapter v) {
if (CollectionElement.isStandardStack(codegen.typeMapper, resolvedGetCall, 1) &&
CollectionElement.isStandardStack(codegen.typeMapper, resolvedSetCall, 2)) {
v.dup2(); // collection and index
return;
}
FrameMap.Mark mark = frame.mark();
// indexes
List valueParameters = resolvedGetCall.getResultingDescriptor().getValueParameters();
int firstParamIndex = -1;
for (int i = valueParameters.size() - 1; i >= 0; --i) {
Type type = codegen.typeMapper.mapType(valueParameters.get(i).getType());
firstParamIndex = frame.enterTemp(type);
v.store(firstParamIndex, type);
}
ReceiverValue receiverParameter = resolvedGetCall.getExtensionReceiver();
int receiverIndex = -1;
if (receiverParameter != null) {
Type type = codegen.typeMapper.mapType(receiverParameter.getType());
receiverIndex = frame.enterTemp(type);
v.store(receiverIndex, type);
}
ReceiverValue dispatchReceiver = resolvedGetCall.getDispatchReceiver();
int thisIndex = -1;
if (dispatchReceiver != null) {
thisIndex = frame.enterTemp(OBJECT_TYPE);
v.store(thisIndex, OBJECT_TYPE);
}
// for setter
int realReceiverIndex;
Type realReceiverType;
if (receiverIndex != -1) {
realReceiverType = codegen.typeMapper.mapType(receiverParameter.getType());
realReceiverIndex = receiverIndex;
}
else if (thisIndex != -1) {
realReceiverType = OBJECT_TYPE;
realReceiverIndex = thisIndex;
}
else {
throw new UnsupportedOperationException();
}
if (resolvedSetCall.getDispatchReceiver() != null) {
if (resolvedSetCall.getExtensionReceiver() != null) {
codegen.generateReceiverValue(resolvedSetCall.getDispatchReceiver(), false).put(OBJECT_TYPE, null, v);
}
v.load(realReceiverIndex, realReceiverType);
}
else {
if (resolvedSetCall.getExtensionReceiver() != null) {
v.load(realReceiverIndex, realReceiverType);
}
else {
throw new UnsupportedOperationException();
}
}
int index = firstParamIndex;
for (ValueParameterDescriptor valueParameter : valueParameters) {
Type type = codegen.typeMapper.mapType(valueParameter.getType());
v.load(index, type);
index -= type.getSize();
}
// restoring original
if (thisIndex != -1) {
v.load(thisIndex, OBJECT_TYPE);
}
if (receiverIndex != -1) {
v.load(receiverIndex, realReceiverType);
}
index = firstParamIndex;
for (ValueParameterDescriptor valueParameter : valueParameters) {
Type type = codegen.typeMapper.mapType(valueParameter.getType());
v.load(index, type);
index -= type.getSize();
}
mark.dropTo();
}
}
public static class CollectionElement extends StackValueWithSimpleReceiver {
private final Callable getter;
private final Callable setter;
private final ExpressionCodegen codegen;
private final ResolvedCall resolvedGetCall;
private final ResolvedCall resolvedSetCall;
public CollectionElement(
@NotNull CollectionElementReceiver collectionElementReceiver,
@NotNull Type type,
@Nullable KotlinType kotlinType,
@Nullable ResolvedCall resolvedGetCall,
@Nullable ResolvedCall resolvedSetCall,
@NotNull ExpressionCodegen codegen
) {
super(type, kotlinType, false, false, collectionElementReceiver, true);
this.resolvedGetCall = resolvedGetCall;
this.resolvedSetCall = resolvedSetCall;
this.setter = resolvedSetCall == null ? null :
codegen.resolveToCallable(codegen.accessibleFunctionDescriptor(resolvedSetCall), false, resolvedSetCall);
this.getter = resolvedGetCall == null ? null :
codegen.resolveToCallable(codegen.accessibleFunctionDescriptor(resolvedGetCall), false, resolvedGetCall);
this.codegen = codegen;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
if (getter == null) {
throw new UnsupportedOperationException("no getter specified");
}
CallGenerator callGenerator = getCallGenerator();
callGenerator.genCall(getter, resolvedGetCall, genDefaultMaskIfPresent(callGenerator), codegen);
coerceTo(type, kotlinType, v);
}
private boolean genDefaultMaskIfPresent(CallGenerator callGenerator) {
DefaultCallArgs defaultArgs = ((CollectionElementReceiver) receiver).defaultArgs;
return defaultArgs.generateOnStackIfNeeded(callGenerator, true);
}
private CallGenerator getCallGenerator() {
CallGenerator generator = ((CollectionElementReceiver) receiver).callGenerator;
assert generator != null :
"CollectionElementReceiver should be putted on stack before CollectionElement:" +
" getCall = " + resolvedGetCall + ", setCall = " + resolvedSetCall;
return generator;
}
@Override
public int receiverSize() {
if (isStandardStack(codegen.typeMapper, resolvedGetCall, 1) && isStandardStack(codegen.typeMapper, resolvedSetCall, 2)) {
return 2;
}
else {
return -1;
}
}
public static boolean isStandardStack(@NotNull KotlinTypeMapper typeMapper, @Nullable ResolvedCall> call, int valueParamsSize) {
if (call == null) {
return true;
}
List valueParameters = call.getResultingDescriptor().getValueParameters();
if (valueParameters.size() != valueParamsSize) {
return false;
}
for (ValueParameterDescriptor valueParameter : valueParameters) {
if (typeMapper.mapType(valueParameter.getType()).getSize() != 1) {
return false;
}
}
if (call.getDispatchReceiver() != null) {
if (call.getExtensionReceiver() != null) {
return false;
}
}
else {
//noinspection ConstantConditions
if (typeMapper.mapType(call.getResultingDescriptor().getExtensionReceiverParameter().getType()).getSize() != 1) {
return false;
}
}
return true;
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
if (setter == null) {
throw new UnsupportedOperationException("no setter specified");
}
Type lastParameterType = ArraysKt.last(setter.getParameterTypes());
KotlinType lastParameterKotlinType =
CollectionsKt.last(resolvedSetCall.getResultingDescriptor().getOriginal().getValueParameters()).getType();
coerce(topOfStackType, topOfStackKotlinType, lastParameterType, lastParameterKotlinType, v);
CallGenerator callGenerator = getCallGenerator();
callGenerator.putValueIfNeeded(
new JvmKotlinType(lastParameterType, lastParameterKotlinType),
StackValue.onStack(lastParameterType, lastParameterKotlinType)
);
CollectionElementReceiver collectionElementReceiver = (CollectionElementReceiver) receiver;
boolean callDefault = false;
boolean properSetterCalls = codegen.getState().getLanguageVersionSettings().supportsFeature(LanguageFeature.ProperArrayConventionSetterWithDefaultCalls);
if (collectionElementReceiver.isGetter) {
//Convention setter/getter could have default parameters at the end of parameter list (in case of setter before last parameter)
//We should remove default parameters of getter from stack if they don't match setter ones and regenerate mask for setter
//Note that it works only for non-inline cases
//TODO: try to don't generate defaults at all in CollectionElementReceiver
List getterArguments = new ArrayList<>(collectionElementReceiver.valueArguments);
List getterDefaults = CollectionsKt.takeLastWhile(getterArguments,
argument -> argument instanceof DefaultValueArgument);
List setterArguments = resolvedSetCall.getValueArgumentsByIndex();
List setterDefaults = CollectionsKt.takeLastWhile(CollectionsKt.dropLast(setterArguments, 1),
argument -> argument instanceof DefaultValueArgument);
if (!getterDefaults.isEmpty() || !setterDefaults.isEmpty()) {
Local rhsValue = StackValue.local(codegen.myFrameMap.enterTemp(lastParameterType), lastParameterType);
rhsValue.store(StackValue.onStack(type), v);
List types = getter.getValueParameterTypes();
for (int i = collectionElementReceiver.valueArguments.size() - 1; i >= 0; i--) {
ResolvedValueArgument argument = collectionElementReceiver.valueArguments.get(i);
if (argument instanceof DefaultValueArgument) {
AsmUtil.pop(v, types.get(i));
}
}
if (properSetterCalls) {
DefaultCallArgs defaultArgs = new DefaultCallArgs(
CodegenUtilKt.unwrapFrontendVersion(resolvedSetCall.getResultingDescriptor()).getValueParameters().size());
if (!setterDefaults.isEmpty()) {
ArgumentGenerator setterArgumentGenerator = new CallBasedArgumentGenerator(
codegen,
callGenerator,
resolvedSetCall.getResultingDescriptor().getValueParameters(), setter.getValueParameterTypes()
);
int defaultIndex = CollectionsKt.getLastIndex(setterArguments) - 1/*rhs value*/ - setterDefaults.size();
for (ResolvedValueArgument aDefault : setterDefaults) {
defaultArgs.mark(++defaultIndex);
setterArgumentGenerator.generateDefault(defaultIndex, (DefaultValueArgument) aDefault);
}
callDefault = true;
}
rhsValue.put(v);
codegen.myFrameMap.leaveTemp(lastParameterType);
defaultArgs.generateOnStackIfNeeded(callGenerator, false);
}
else {
rhsValue.put(v);
codegen.myFrameMap.leaveTemp(lastParameterType);
}
}
} else {
callDefault = properSetterCalls && genDefaultMaskIfPresent(callGenerator);
}
callGenerator.genCall(setter, resolvedSetCall, callDefault, codegen);
Type returnType = setter.getReturnType();
if (returnType != Type.VOID_TYPE) {
pop(v, returnType);
}
}
}
public static class Field extends StackValueWithSimpleReceiver {
public final Type owner;
public final String name;
public final DeclarationDescriptor descriptor;
public Field(
@NotNull Type type,
@Nullable KotlinType kotlinType,
@NotNull Type owner,
@NotNull String name,
boolean isStatic,
@NotNull StackValue receiver,
@Nullable DeclarationDescriptor descriptor
) {
super(type, kotlinType, isStatic, isStatic, receiver, receiver.canHaveSideEffects());
this.owner = owner;
this.name = name;
this.descriptor = descriptor;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
v.visitFieldInsn(isStaticPut ? GETSTATIC : GETFIELD, owner.getInternalName(), name, this.type.getDescriptor());
coerceTo(type, kotlinType, v);
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
coerceFrom(topOfStackType, topOfStackKotlinType, v);
v.visitFieldInsn(isStaticStore ? PUTSTATIC : PUTFIELD, owner.getInternalName(), name, this.type.getDescriptor());
}
@Override
protected StackValueWithSimpleReceiver changeReceiver(@NotNull StackValue newReceiver) {
return field(this, newReceiver);
}
}
public static class Property extends StackValueWithSimpleReceiver {
private final CallableMethod getter;
private final CallableMethod setter;
private final Type backingFieldOwner;
private final PropertyDescriptor descriptor;
private final String fieldName;
private final ExpressionCodegen codegen;
private final ResolvedCall resolvedCall;
private final boolean skipLateinitAssertion;
private final KotlinType delegateKotlinType;
public Property(
@NotNull PropertyDescriptor descriptor, @Nullable Type backingFieldOwner, @Nullable CallableMethod getter,
@Nullable CallableMethod setter, boolean isStaticBackingField, @Nullable String fieldName, @NotNull Type type,
@NotNull StackValue receiver, @NotNull ExpressionCodegen codegen, @Nullable ResolvedCall resolvedCall,
boolean skipLateinitAssertion, @Nullable KotlinType delegateKotlinType
) {
super(type, descriptor.getType(), isStatic(isStaticBackingField, getter), isStatic(isStaticBackingField, setter), receiver, true);
this.backingFieldOwner = backingFieldOwner;
this.getter = getter;
this.setter = setter;
this.descriptor = descriptor;
this.fieldName = fieldName;
this.codegen = codegen;
this.resolvedCall = resolvedCall;
this.skipLateinitAssertion = skipLateinitAssertion;
this.delegateKotlinType = delegateKotlinType;
}
private static class DelegatePropertyConstructorMarker {
private DelegatePropertyConstructorMarker() {}
public static final DelegatePropertyConstructorMarker MARKER = new DelegatePropertyConstructorMarker();
}
/**
* Given a delegating property, create a "property" corresponding to the underlying delegate itself.
* This will take care of backing fields, accessors, and other such stuff.
* Note that we just replace kotlinType
with the delegateKotlinType
* (so that type coercion will work properly),
* and delegateKotlinType
with null
* (so that the resulting property has no underlying delegate of its own).
*
* @param delegating delegating property
* @param marker intent marker
*/
@SuppressWarnings("unused")
private Property(@NotNull Property delegating, @NotNull DelegatePropertyConstructorMarker marker) {
super(delegating.type, delegating.delegateKotlinType,
delegating.isStaticPut, delegating.isStaticStore, delegating.receiver, true);
this.backingFieldOwner = delegating.backingFieldOwner;
this.getter = delegating.getter;
this.setter = delegating.setter;
this.descriptor = delegating.descriptor;
this.fieldName = delegating.fieldName;
this.codegen = delegating.codegen;
this.resolvedCall = delegating.resolvedCall;
this.skipLateinitAssertion = delegating.skipLateinitAssertion;
this.delegateKotlinType = null;
}
public Property getDelegateOrNull() {
if (delegateKotlinType == null) return null;
return new Property(this, DelegatePropertyConstructorMarker.MARKER);
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
if (getter == null) {
assert fieldName != null : "Property should have either a getter or a field name: " + descriptor;
assert backingFieldOwner != null : "Property should have either a getter or a backingFieldOwner: " + descriptor;
if (inlineConstantIfNeeded(type, kotlinType, v)) return;
v.visitFieldInsn(isStaticPut ? GETSTATIC : GETFIELD,
backingFieldOwner.getInternalName(), fieldName, this.type.getDescriptor());
if (!skipLateinitAssertion && descriptor.isLateInit()) {
CallableMemberDescriptor contextDescriptor = codegen.context.getContextDescriptor();
boolean isCompanionAccessor =
contextDescriptor instanceof AccessorForPropertyBackingField &&
((AccessorForPropertyBackingField) contextDescriptor).getAccessorKind() == AccessorKind.IN_CLASS_COMPANION;
if (!isCompanionAccessor) {
genNonNullAssertForLateinit(v, this.descriptor.getName().asString());
}
}
coerceTo(type, kotlinType, v);
}
else {
PropertyGetterDescriptor getterDescriptor = descriptor.getGetter();
assert getterDescriptor != null : "Getter descriptor should be not null for " + descriptor;
if (resolvedCall != null && getterDescriptor.isInline()) {
CallGenerator callGenerator = codegen.getOrCreateCallGenerator(resolvedCall, ((PropertyDescriptor)resolvedCall.getResultingDescriptor()).getGetter());
callGenerator.processHiddenParameters();
callGenerator.putHiddenParamsIntoLocals();
callGenerator.genCall(getter, resolvedCall, false, codegen);
}
else {
getter.genInvokeInstruction(v);
}
Type typeOfValueOnStack = getter.getReturnType();
KotlinType kotlinTypeOfValueOnStack = getterDescriptor.getReturnType();
if (DescriptorUtils.isAnnotationClass(descriptor.getContainingDeclaration())) {
if (this.type.equals(K_CLASS_TYPE)) {
wrapJavaClassIntoKClass(v);
typeOfValueOnStack = K_CLASS_TYPE;
kotlinTypeOfValueOnStack = null;
}
else if (this.type.equals(K_CLASS_ARRAY_TYPE)) {
wrapJavaClassesIntoKClasses(v);
typeOfValueOnStack = K_CLASS_ARRAY_TYPE;
kotlinTypeOfValueOnStack = null;
}
}
coerce(typeOfValueOnStack, kotlinTypeOfValueOnStack, type, kotlinType, v);
// For non-private lateinit properties in companion object, the assertion is generated in the public getFoo method
// in the companion and _not_ in the synthetic accessor access$getFoo$cp in the outer class. The reason is that this way,
// the synthetic accessor can be reused for isInitialized checks, which require there to be no assertion.
// For lateinit properties that are accessed via the backing field directly (or via the synthetic accessor, if the access
// is from a different context), the assertion will be generated on each access, see KT-28331.
if (descriptor instanceof AccessorForPropertyBackingField) {
PropertyDescriptor property = ((AccessorForPropertyBackingField) descriptor).getCalleeDescriptor();
if (!skipLateinitAssertion && property.isLateInit() && DescriptorsJvmAbiUtil.isPropertyWithBackingFieldInOuterClass(property) &&
!JvmCodegenUtil.couldUseDirectAccessToProperty(property, true, false, codegen.context, false)) {
genNonNullAssertForLateinit(v, property.getName().asString());
}
}
KotlinType returnType = descriptor.getReturnType();
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
v.aconst(null);
v.athrow();
}
}
}
private boolean inlineConstantIfNeeded(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
if (JvmCodegenUtil.isInlinedJavaConstProperty(descriptor)) {
return inlineConstant(type, kotlinType, v);
}
if (descriptor.isConst() && codegen.getState().getConfig().getShouldInlineConstVals()) {
return inlineConstant(type, kotlinType, v);
}
return false;
}
private boolean inlineConstant(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
assert AsmUtil.isPrimitive(this.type) || AsmTypes.JAVA_STRING_TYPE.equals(this.type) :
"Const property should have primitive or string type: " + descriptor;
assert isStaticPut : "Const property should be static" + descriptor;
ConstantValue> constantValue = descriptor.getCompileTimeInitializer();
if (constantValue == null) return false;
Object value = constantValue.getValue();
if (this.type == Type.FLOAT_TYPE && value instanceof Double) {
value = ((Double) value).floatValue();
}
StackValue.constant(value, this.type, this.kotlinType).putSelector(type, kotlinType, v);
return true;
}
@Override
public void store(@NotNull StackValue rightSide, @NotNull InstructionAdapter v, boolean skipReceiver) {
PropertySetterDescriptor setterDescriptor = descriptor.getSetter();
if (resolvedCall != null && setterDescriptor != null && setterDescriptor.isInline()) {
assert setter != null : "Setter should be not null for " + descriptor;
CallGenerator callGenerator = codegen.getOrCreateCallGenerator(resolvedCall, ((PropertyDescriptor)resolvedCall.getResultingDescriptor()).getSetter());
if (!skipReceiver) {
putReceiver(v, false);
}
callGenerator.processHiddenParameters();
callGenerator.putValueIfNeeded(new JvmKotlinType(
CollectionsKt.last(setter.getValueParameters()).getAsmType(),
CollectionsKt.last(setterDescriptor.getValueParameters()).getType()),
rightSide);
callGenerator.putHiddenParamsIntoLocals();
callGenerator.genCall(setter, resolvedCall, false, codegen);
}
else {
super.store(rightSide, v, skipReceiver);
}
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
if (setter == null) {
coerceFrom(topOfStackType, topOfStackKotlinType, v);
assert fieldName != null : "Property should have either a setter or a field name: " + descriptor;
assert backingFieldOwner != null : "Property should have either a setter or a backingFieldOwner: " + descriptor;
v.visitFieldInsn(isStaticStore ? PUTSTATIC : PUTFIELD, backingFieldOwner.getInternalName(), fieldName, this.type.getDescriptor());
}
else {
PropertySetterDescriptor setterDescriptor = descriptor.getSetter();
KotlinType setterLastParameterType =
setterDescriptor != null ? CollectionsKt.last(setterDescriptor.getValueParameters()).getReturnType() : null;
coerce(topOfStackType, topOfStackKotlinType, ArraysKt.last(this.setter.getParameterTypes()), setterLastParameterType, v);
setter.genInvokeInstruction(v);
Type returnType = this.setter.getReturnType();
if (returnType != Type.VOID_TYPE) {
pop(v, returnType);
}
}
}
private static boolean isStatic(boolean isStaticBackingField, @Nullable CallableMethod callable) {
if (isStaticBackingField && callable == null) {
return true;
}
if (callable != null && callable.isStaticCall()) {
List parameters = callable.getValueParameters();
for (JvmMethodParameterSignature parameter : parameters) {
JvmMethodParameterKind kind = parameter.getKind();
if (kind == JvmMethodParameterKind.VALUE) {
break;
}
if (kind == JvmMethodParameterKind.CONTEXT_RECEIVER || kind == JvmMethodParameterKind.RECEIVER || kind == JvmMethodParameterKind.THIS) {
return false;
}
}
return true;
}
return false;
}
}
private static void genNonNullAssertForLateinit(@NotNull InstructionAdapter v, @NotNull String name) {
v.dup();
Label ok = new Label();
v.ifnonnull(ok);
v.visitLdcInsn(name);
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwUninitializedPropertyAccessException", "(Ljava/lang/String;)V", false);
v.mark(ok);
}
private static class Expression extends StackValue {
private final KtExpression expression;
private final ExpressionCodegen generator;
public Expression(Type type, KtExpression expression, ExpressionCodegen generator) {
super(type, generator.kotlinType(expression));
this.expression = expression;
this.generator = generator;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
generator.gen(expression, type, kotlinType);
}
}
public static class Shared extends StackValueWithSimpleReceiver {
private final int index;
private final boolean isLateinit;
private final Name name;
public Shared(int index, Type type, KotlinType kotlinType, boolean isLateinit, Name name) {
super(type, kotlinType, false, false, local(index, OBJECT_TYPE), false);
this.index = index;
if (isLateinit && name == null) {
throw new IllegalArgumentException("Lateinit shared local variable should have name: #" + index + " " + type.getDescriptor());
}
this.isLateinit = isLateinit;
this.name = name;
}
public Shared(int index, Type type) {
this(index, type, null, false, null);
}
public int getIndex() {
return index;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
Type refType = refType(this.type);
Type sharedType = sharedTypeForType(this.type);
v.visitFieldInsn(GETFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
if (isLateinit) {
StackValue.genNonNullAssertForLateinit(v, name.asString());
}
coerceFrom(refType, null, v);
coerceTo(type, kotlinType, v);
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
coerceFrom(topOfStackType, topOfStackKotlinType, v);
Type refType = refType(this.type);
Type sharedType = sharedTypeForType(this.type);
v.visitFieldInsn(PUTFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
}
}
@NotNull
public static Type sharedTypeForType(@NotNull Type type) {
switch (type.getSort()) {
case Type.OBJECT:
case Type.ARRAY:
return OBJECT_REF_TYPE;
default:
PrimitiveType primitiveType = AsmUtil.asmPrimitiveTypeToLangPrimitiveType(type);
if (primitiveType == null) throw new UnsupportedOperationException();
return sharedTypeForPrimitive(primitiveType);
}
}
public static Type refType(Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return OBJECT_TYPE;
}
return type;
}
public static class FieldForSharedVar extends StackValueWithSimpleReceiver {
final Type owner;
final String name;
final boolean isLateinit;
final Name variableName;
public FieldForSharedVar(
Type type, KotlinType kotlinType,
Type owner, String name, StackValue.Field receiver,
boolean isLateinit, Name variableName
) {
super(type, kotlinType, false, false, receiver, receiver.canHaveSideEffects());
if (isLateinit && variableName == null) {
throw new IllegalArgumentException("variableName should be non-null for captured lateinit variable " + name);
}
this.owner = owner;
this.name = name;
this.isLateinit = isLateinit;
this.variableName = variableName;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
Type sharedType = sharedTypeForType(this.type);
Type refType = refType(this.type);
v.visitFieldInsn(GETFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
if (isLateinit) {
StackValue.genNonNullAssertForLateinit(v, variableName.asString());
}
coerceFrom(refType, null, v);
coerceTo(type, kotlinType, v);
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @Nullable KotlinType topOfStackKotlinType, @NotNull InstructionAdapter v) {
coerceFrom(topOfStackType, topOfStackKotlinType, v);
v.visitFieldInsn(PUTFIELD, sharedTypeForType(type).getInternalName(), "element", refType(type).getDescriptor());
}
@Override
protected StackValueWithSimpleReceiver changeReceiver(@NotNull StackValue newReceiver) {
return fieldForSharedVar(this, newReceiver);
}
}
private static class ThisOuter extends StackValue {
private final ExpressionCodegen codegen;
private final ClassDescriptor descriptor;
private final boolean isSuper;
private final boolean coerceType;
public ThisOuter(ExpressionCodegen codegen, ClassDescriptor descriptor, boolean isSuper, boolean coerceType) {
super(OBJECT_TYPE, false);
this.codegen = codegen;
this.descriptor = descriptor;
this.isSuper = isSuper;
this.coerceType = coerceType;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
StackValue stackValue = codegen.generateThisOrOuter(descriptor, isSuper);
stackValue.put(
coerceType ? type : stackValue.type,
coerceType ? kotlinType : stackValue.kotlinType,
v
);
}
}
private static class PostIncrement extends StackValue {
private final int index;
private final int increment;
public PostIncrement(int index, int increment) {
super(Type.INT_TYPE);
this.index = index;
this.increment = increment;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
if (!type.equals(Type.VOID_TYPE)) {
v.load(index, Type.INT_TYPE);
coerceTo(type, kotlinType, v);
}
v.iinc(index, increment);
}
}
private static class PreIncrementForLocalVar extends StackValue {
private final int index;
private final int increment;
public PreIncrementForLocalVar(int index, int increment, @Nullable KotlinType kotlinType) {
super(Type.INT_TYPE, kotlinType);
this.index = index;
this.increment = increment;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
v.iinc(index, increment);
if (!type.equals(Type.VOID_TYPE)) {
v.load(index, Type.INT_TYPE);
coerceTo(type, kotlinType, v);
}
}
}
private static class PrefixIncrement extends StackValue {
private final ResolvedCall resolvedCall;
private final ExpressionCodegen codegen;
private StackValue value;
public PrefixIncrement(
@NotNull Type type,
@NotNull StackValue value,
ResolvedCall resolvedCall,
@NotNull ExpressionCodegen codegen
) {
super(type, value.kotlinType);
this.value = value;
this.resolvedCall = resolvedCall;
this.codegen = codegen;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
value = StackValue.complexReceiver(value, true, false, true);
value.put(this.type, this.kotlinType, v);
value.store(codegen.invokeFunction(resolvedCall, StackValue.onStack(this.type, this.kotlinType)), v, true);
value.put(this.type, this.kotlinType, v, true);
coerceTo(type, kotlinType, v);
}
}
public abstract static class StackValueWithSimpleReceiver extends StackValue {
public final boolean isStaticPut;
public final boolean isStaticStore;
@NotNull
public final StackValue receiver;
public StackValueWithSimpleReceiver(
@NotNull Type type,
@Nullable KotlinType kotlinType,
boolean isStaticPut,
boolean isStaticStore,
@NotNull StackValue receiver,
boolean canHaveSideEffects
) {
super(type, kotlinType, canHaveSideEffects);
this.receiver = receiver;
this.isStaticPut = isStaticPut;
this.isStaticStore = isStaticStore;
}
@Override
public void putReceiver(@NotNull InstructionAdapter v, boolean isRead) {
boolean hasReceiver = isNonStaticAccess(isRead);
if (hasReceiver || receiver.canHaveSideEffects()) {
receiver.put(
hasReceiver ? receiver.type : Type.VOID_TYPE,
hasReceiver ? receiver.kotlinType : null,
v
);
}
}
@Override
public boolean isNonStaticAccess(boolean isRead) {
return isRead ? !isStaticPut : !isStaticStore;
}
public int receiverSize() {
return receiver.type.getSize();
}
@Override
public void dup(@NotNull InstructionAdapter v, boolean withWriteReceiver) {
if (!withWriteReceiver) {
super.dup(v, false);
}
else {
int receiverSize = isNonStaticAccess(false) ? receiverSize() : 0;
switch (receiverSize) {
case 0:
AsmUtil.dup(v, type);
break;
case 1:
if (type.getSize() == 2) {
v.dup2X1();
}
else {
v.dupX1();
}
break;
case 2:
if (type.getSize() == 2) {
v.dup2X2();
}
else {
v.dupX2();
}
break;
case -1:
throw new UnsupportedOperationException();
}
}
}
@Override
public void store(
@NotNull StackValue rightSide, @NotNull InstructionAdapter v, boolean skipReceiver
) {
if (!skipReceiver) {
putReceiver(v, false);
}
rightSide.put(rightSide.type, rightSide.kotlinType, v);
storeSelector(rightSide.type, rightSide.kotlinType, v);
}
protected StackValueWithSimpleReceiver changeReceiver(@NotNull StackValue newReceiver) {
return this;
}
}
private static class ComplexReceiver extends StackValue {
private final StackValueWithSimpleReceiver originalValueWithReceiver;
private final boolean[] isReadOperations;
public ComplexReceiver(StackValueWithSimpleReceiver value, boolean[] isReadOperations) {
super(value.type, value.receiver.canHaveSideEffects());
this.originalValueWithReceiver = value;
this.isReadOperations = isReadOperations;
if (value instanceof CollectionElement) {
if (value.receiver instanceof CollectionElementReceiver) {
((CollectionElementReceiver) value.receiver).isComplexOperationWithDup = true;
}
}
}
@Override
public void putSelector(
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
) {
boolean wasPut = false;
StackValue receiver = originalValueWithReceiver.receiver;
for (boolean operation : isReadOperations) {
if (originalValueWithReceiver.isNonStaticAccess(operation)) {
if (!wasPut) {
receiver.put(receiver.type, receiver.kotlinType, v);
wasPut = true;
}
else {
receiver.dup(v, false);
}
}
}
if (!wasPut && receiver.canHaveSideEffects()) {
receiver.put(Type.VOID_TYPE, null, v);
}
}
}
public static class Receiver extends StackValue {
private final StackValue[] instructions;
protected Receiver(@NotNull Type type, StackValue... receiverInstructions) {
super(type);
instructions = receiverInstructions;
}
@Override
public void putSelector(
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
) {
for (StackValue instruction : instructions) {
instruction.put(instruction.type, instruction.kotlinType, v);
}
}
}
public static class DelegatedForComplexReceiver extends StackValueWithSimpleReceiver {
public final StackValueWithSimpleReceiver originalValue;
public DelegatedForComplexReceiver(
@NotNull Type type,
@NotNull StackValueWithSimpleReceiver originalValue,
@NotNull ComplexReceiver receiver
) {
super(type, null, bothReceiverStatic(originalValue), bothReceiverStatic(originalValue), receiver, originalValue.canHaveSideEffects());
this.originalValue = originalValue;
}
private static boolean bothReceiverStatic(StackValueWithSimpleReceiver originalValue) {
return !(originalValue.isNonStaticAccess(true) || originalValue.isNonStaticAccess(false));
}
@Override
public void putSelector(
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
) {
originalValue.putSelector(type, kotlinType, v);
}
@Override
public void store(@NotNull StackValue rightSide, @NotNull InstructionAdapter v, boolean skipReceiver) {
if (!skipReceiver) {
putReceiver(v, false);
}
originalValue.store(rightSide, v, true);
}
@Override
public void storeSelector(
@NotNull Type topOfStackType, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
) {
originalValue.storeSelector(topOfStackType, kotlinType, v);
}
@Override
public void dup(@NotNull InstructionAdapter v, boolean withWriteReceiver) {
originalValue.dup(v, withWriteReceiver);
}
}
public static StackValue complexWriteReadReceiver(StackValue stackValue) {
return complexReceiver(stackValue, false, true);
}
private static StackValue complexReceiver(StackValue stackValue, boolean... isReadOperations) {
if (stackValue instanceof Delegate) {
//TODO need to support
throwUnsupportedComplexOperation(((Delegate) stackValue).variableDescriptor);
}
if (stackValue instanceof StackValueWithSimpleReceiver) {
return new DelegatedForComplexReceiver(stackValue.type, (StackValueWithSimpleReceiver) stackValue,
new ComplexReceiver((StackValueWithSimpleReceiver) stackValue, isReadOperations));
}
else {
return stackValue;
}
}
static class SafeCall extends StackValue {
@NotNull private final Type type;
private final StackValue receiver;
@Nullable private final Label ifNull;
public SafeCall(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull StackValue value, @Nullable Label ifNull) {
super(type, kotlinType);
this.type = type;
this.receiver = value;
this.ifNull = ifNull;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
receiver.put(this.type, this.kotlinType, v);
if (ifNull != null) {
//not a primitive
v.dup();
v.ifnull(ifNull);
}
coerceTo(type, kotlinType, v);
}
}
static class SafeFallback extends StackValueWithSimpleReceiver {
@Nullable private final Label ifNull;
public SafeFallback(@NotNull Type type, @Nullable KotlinType kotlinType, @Nullable Label ifNull, StackValue receiver) {
super(type, kotlinType, false, false, receiver, true);
this.ifNull = ifNull;
}
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
Label end = new Label();
v.goTo(end);
v.mark(ifNull);
v.pop();
if (!this.type.equals(Type.VOID_TYPE)) {
v.aconst(null);
}
v.mark(end);
coerceTo(type, kotlinType, v);
}
@Override
public void store(
@NotNull StackValue rightSide, @NotNull InstructionAdapter v, boolean skipReceiver
) {
receiver.store(rightSide, v, skipReceiver);
Label end = new Label();
v.goTo(end);
v.mark(ifNull);
v.pop();
v.mark(end);
}
}
private static void throwUnsupportedComplexOperation(
@NotNull CallableDescriptor descriptor
) {
throw new RuntimeException(
"Augmented assignment and increment are not supported for local delegated properties and inline properties: " +
descriptor);
}
}