All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.jet.codegen.StackValue Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2013 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.jet.codegen;

import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethod;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.JetTypeMapper;
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.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;

import java.util.List;

import static org.jetbrains.jet.codegen.AsmUtil.*;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
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";

    @NotNull
    public final Type type;

    protected StackValue(@NotNull Type type) {
        this.type = type;
    }

    /**
     * Put this value to the top of the stack.
     */
    public abstract void put(Type type, InstructionAdapter v);

    /**
     * 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
     */
    protected void moveToTopOfStack(Type type, InstructionAdapter v, int depth) {
        put(type, v);
    }

    /**
     * Set this value from the top of the stack.
     */
    public void store(Type topOfStackType, InstructionAdapter v) {
        throw new UnsupportedOperationException("cannot store to value " + this);
    }

    public void dupReceiver(InstructionAdapter v) {
    }

    public int receiverSize() {
        return 0;
    }

    public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
        put(this.type, v);
        coerceTo(Type.BOOLEAN_TYPE, v);
        if (jumpIfFalse) {
            v.ifeq(label);
        }
        else {
            v.ifne(label);
        }
    }

    public static Local local(int index, Type type) {
        return new Local(index, type);
    }

    public static StackValue shared(int index, Type type) {
        return new Shared(index, type);
    }

    public static StackValue onStack(Type type) {
        return type == Type.VOID_TYPE ? none() : new OnStack(type);
    }

    public static StackValue constant(@Nullable Object value, Type type) {
        return new Constant(value, type);
    }

    public static StackValue cmp(IElementType opToken, Type type) {
        return type.getSort() == Type.OBJECT ? new ObjectCompare(opToken, type) : new NumberCompare(opToken, type);
    }

    public static StackValue not(StackValue stackValue) {
        return new Invert(stackValue);
    }

    @NotNull
    public static StackValue arrayElement(Type type) {
        return new ArrayElement(type);
    }

    @NotNull
    public static StackValue collectionElement(
            Type type,
            ResolvedCall getter,
            ResolvedCall setter,
            ExpressionCodegen codegen,
            GenerationState state
    ) {
        return new CollectionElement(type, getter, setter, codegen, state);
    }

    @NotNull
    public static Field field(@NotNull Type type, @NotNull Type owner, @NotNull String name, boolean isStatic) {
        return new Field(type, owner, name, isStatic);
    }

    @NotNull
    public static Property property(
            @NotNull PropertyDescriptor descriptor,
            @NotNull Type methodOwner,
            @NotNull Type type,
            boolean isStatic,
            @Nullable String fieldName,
            @Nullable CallableMethod getter,
            @Nullable CallableMethod setter,
            GenerationState state
    ) {
        return new Property(descriptor, methodOwner, getter, setter, isStatic, fieldName, type, state);
    }

    @NotNull
    public static StackValue expression(Type type, JetExpression expression, ExpressionCodegen generator) {
        return new Expression(type, expression, generator);
    }

    private static void box(Type type, Type toType, InstructionAdapter v) {
        if (type == Type.BYTE_TYPE || toType.getInternalName().equals(NULLABLE_BYTE_TYPE_NAME) && type == Type.INT_TYPE) {
            v.cast(type, Type.BYTE_TYPE);
            v.invokestatic(NULLABLE_BYTE_TYPE_NAME, "valueOf", "(B)L" + NULLABLE_BYTE_TYPE_NAME + ";");
        }
        else if (type == Type.SHORT_TYPE || toType.getInternalName().equals(NULLABLE_SHORT_TYPE_NAME) && type == Type.INT_TYPE) {
            v.cast(type, Type.SHORT_TYPE);
            v.invokestatic(NULLABLE_SHORT_TYPE_NAME, "valueOf", "(S)L" + NULLABLE_SHORT_TYPE_NAME + ";");
        }
        else if (type == Type.LONG_TYPE || toType.getInternalName().equals(NULLABLE_LONG_TYPE_NAME) && type == Type.INT_TYPE) {
            v.cast(type, Type.LONG_TYPE);
            v.invokestatic(NULLABLE_LONG_TYPE_NAME, "valueOf", "(J)L" + NULLABLE_LONG_TYPE_NAME +";");
        }
        else if (type == Type.INT_TYPE) {
            v.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
        }
        else if (type == Type.BOOLEAN_TYPE) {
            v.invokestatic("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
        }
        else if (type == Type.CHAR_TYPE) {
            v.invokestatic("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
        }
        else if (type == Type.FLOAT_TYPE) {
            v.invokestatic("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
        }
        else if (type == Type.DOUBLE_TYPE) {
            v.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
        }
    }

    private static void unbox(Type type, InstructionAdapter v) {
        if (type == Type.INT_TYPE) {
            v.invokevirtual("java/lang/Number", "intValue", "()I");
        }
        else if (type == Type.BOOLEAN_TYPE) {
            v.invokevirtual("java/lang/Boolean", "booleanValue", "()Z");
        }
        else if (type == Type.CHAR_TYPE) {
            v.invokevirtual("java/lang/Character", "charValue", "()C");
        }
        else if (type == Type.SHORT_TYPE) {
            v.invokevirtual("java/lang/Number", "shortValue", "()S");
        }
        else if (type == Type.LONG_TYPE) {
            v.invokevirtual("java/lang/Number", "longValue", "()J");
        }
        else if (type == Type.BYTE_TYPE) {
            v.invokevirtual("java/lang/Number", "byteValue", "()B");
        }
        else if (type == Type.FLOAT_TYPE) {
            v.invokevirtual("java/lang/Number", "floatValue", "()F");
        }
        else if (type == Type.DOUBLE_TYPE) {
            v.invokevirtual("java/lang/Number", "doubleValue", "()D");
        }
    }

    protected void coerceTo(Type toType, InstructionAdapter v) {
        coerce(this.type, toType, v);
    }

    protected void coerceFrom(Type topOfStackType, InstructionAdapter v) {
        coerce(topOfStackType, this.type, v);
    }

    public static void coerce(Type fromType, Type toType, InstructionAdapter v) {
        if (toType.equals(fromType)) 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 if (toType.getSort() == Type.OBJECT || toType.getSort() == Type.ARRAY) {
                v.aconst(null);
            }
            else {
                pushDefaultPrimitiveValueOnStack(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) {
            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) {
            if (fromType.equals(getType(Boolean.class)) || fromType.equals(getType(Character.class))) {
                unbox(unboxType(fromType), v);
                coerce(unboxType(fromType), toType, v);
            }
            else {
                if (toType.getSort() == Type.BOOLEAN || toType.getSort() == Type.CHAR) {
                    coerce(fromType, boxType(toType), v);
                }
                else {
                    coerce(fromType, getType(Number.class), v);
                }
                unbox(toType, v);
            }
        }
        else {
            v.cast(fromType, toType);
        }
    }

    public static void putUnitInstance(InstructionAdapter v) {
        v.visitFieldInsn(GETSTATIC, UNIT_TYPE.getInternalName(), "VALUE", UNIT_TYPE.getDescriptor());
    }

    protected void putAsBoolean(InstructionAdapter v) {
        Label ifTrue = new Label();
        Label end = new Label();
        condJump(ifTrue, false, v);
        v.iconst(0);
        v.goTo(end);
        v.mark(ifTrue);
        v.iconst(1);
        v.mark(end);
    }

    public static StackValue none() {
        return None.INSTANCE;
    }

    public static StackValue fieldForSharedVar(Type localType, Type classType, String fieldName) {
        return new FieldForSharedVar(localType, classType, fieldName);
    }

    public static StackValue composed(StackValue prefix, StackValue suffix) {
        return new Composed(prefix, suffix);
    }

    public static StackValue thisOrOuter(ExpressionCodegen codegen, ClassDescriptor descriptor, boolean isSuper, boolean isExplicit) {
        // Coerce this/super for traits to support traits with required classes.
        // Coerce explicit 'this' for the case when it is smartcasted.
        // Do not coerce for other classes due to the 'protected' access issues (JVMS 7, 4.9.2 Structural Constraints).
        boolean coerceType = descriptor.getKind() == ClassKind.TRAIT || (isExplicit && !isSuper);
        return new ThisOuter(codegen, descriptor, isSuper, coerceType);
    }

    public static StackValue postIncrement(int index, int increment) {
        return new PostIncrement(index, increment);
    }

    public static StackValue preIncrement(int index, int increment) {
        return new PreIncrement(index, increment);
    }

    public static StackValue receiver(
            ResolvedCall resolvedCall,
            StackValue receiver,
            ExpressionCodegen codegen,
            @Nullable CallableMethod callableMethod
    ) {
        if (resolvedCall.getThisObject().exists() || resolvedCall.getReceiverArgument().exists() || isLocalFunCall(callableMethod)) {
            return new CallReceiver(resolvedCall, receiver, codegen, callableMethod, true);
        }
        return receiver;
    }

    private static boolean isLocalFunCall(@Nullable CallableMethod callableMethod) {
        return callableMethod != null && callableMethod.getGenerateCalleeType() != null;
    }

    public static StackValue receiverWithoutReceiverArgument(StackValue receiverWithParameter) {
        if (receiverWithParameter instanceof CallReceiver) {
            CallReceiver callReceiver = (CallReceiver) receiverWithParameter;
            return new CallReceiver(callReceiver.resolvedCall, callReceiver.receiver,
                                    callReceiver.codegen, callReceiver.callableMethod, false);
        }
        return receiverWithParameter;
    }

    public static Field singleton(ClassDescriptor classDescriptor, JetTypeMapper typeMapper) {
        FieldInfo info = FieldInfo.createForSingleton(classDescriptor, typeMapper);
        return field(info.getFieldType(), Type.getObjectType(info.getOwnerInternalName()), info.getFieldName(), true);
    }

    private static class None extends StackValue {
        public static final None INSTANCE = new None();

        private None() {
            super(Type.VOID_TYPE);
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            coerceTo(type, v);
        }
    }

    public static class Local extends StackValue {
        public final int index;

        private Local(int index, Type type) {
            super(type);
            this.index = index;

            if (index < 0) {
                throw new IllegalStateException("local variable index must be non-negative");
            }
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            v.load(index, this.type);
            coerceTo(type, v);
            // TODO unbox
        }

        @Override
        public void store(Type topOfStackType, InstructionAdapter v) {
            coerceFrom(topOfStackType, v);
            v.store(index, this.type);
        }
    }

    public static class OnStack extends StackValue {
        public OnStack(Type type) {
            super(type);
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            coerceTo(type, v);
        }

        @Override
        public void moveToTopOfStack(Type type, InstructionAdapter v, int depth) {
            if (depth == 0) {
                put(type, v);
            }
            else if (depth == 1) {
                if (type.getSize() != 1) {
                    throw new UnsupportedOperationException("don't know how to move type " + type + " to top of stack");
                }
                v.swap();
            }
            else {
                throw new UnsupportedOperationException("unsupported move-to-top depth " + depth);
            }
        }
    }

    public static class Constant extends StackValue {
        @Nullable
        private final Object value;

        public Constant(@Nullable Object value, Type type) {
            super(type);
            this.value = value;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            if (value instanceof Integer) {
                v.iconst((Integer) value);
            }
            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);
            }

            coerceTo(type, v);
        }

        @Override
        public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
            if (value instanceof Boolean) {
                boolean boolValue = (Boolean) value;
                if (boolValue ^ jumpIfFalse) {
                    v.goTo(label);
                }
            }
            else {
                throw new UnsupportedOperationException("don't know how to generate this condjump");
            }
        }
    }

    private static class NumberCompare extends StackValue {
        protected final IElementType opToken;
        private final Type operandType;

        public NumberCompare(IElementType opToken, Type operandType) {
            super(Type.BOOLEAN_TYPE);
            this.opToken = opToken;
            this.operandType = operandType;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            putAsBoolean(v);
            coerceTo(type, v);
        }

        @Override
        public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
            int opcode;
            if (opToken == JetTokens.EQEQ) {
                opcode = jumpIfFalse ? IFNE : IFEQ;
            }
            else if (opToken == JetTokens.EXCLEQ) {
                opcode = jumpIfFalse ? IFEQ : IFNE;
            }
            else if (opToken == JetTokens.GT) {
                opcode = jumpIfFalse ? IFLE : IFGT;
            }
            else if (opToken == JetTokens.GTEQ) {
                opcode = jumpIfFalse ? IFLT : IFGE;
            }
            else if (opToken == JetTokens.LT) {
                opcode = jumpIfFalse ? IFGE : IFLT;
            }
            else if (opToken == JetTokens.LTEQ) {
                opcode = jumpIfFalse ? IFGT : IFLE;
            }
            else {
                throw new UnsupportedOperationException("don't know how to generate this condjump");
            }
            if (operandType == Type.FLOAT_TYPE || operandType == Type.DOUBLE_TYPE) {
                if (opToken == JetTokens.GT || opToken == JetTokens.GTEQ) {
                    v.cmpl(operandType);
                }
                else {
                    v.cmpg(operandType);
                }
            }
            else if (operandType == Type.LONG_TYPE) {
                v.lcmp();
            }
            else {
                opcode += (IF_ICMPEQ - IFEQ);
            }
            v.visitJumpInsn(opcode, label);
        }
    }

    private static class ObjectCompare extends NumberCompare {
        public ObjectCompare(IElementType opToken, Type operandType) {
            super(opToken, operandType);
        }

        @Override
        public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
            int opcode;
            if (opToken == JetTokens.EQEQEQ) {
                opcode = jumpIfFalse ? IF_ACMPNE : IF_ACMPEQ;
            }
            else if (opToken == JetTokens.EXCLEQEQEQ) {
                opcode = jumpIfFalse ? IF_ACMPEQ : IF_ACMPNE;
            }
            else {
                throw new UnsupportedOperationException("don't know how to generate this condjump");
            }
            v.visitJumpInsn(opcode, label);
        }
    }

    private static class Invert extends StackValue {
        private final StackValue myOperand;

        private Invert(StackValue operand) {
            super(Type.BOOLEAN_TYPE);
            myOperand = operand;
            if (myOperand.type != Type.BOOLEAN_TYPE) {
                throw new UnsupportedOperationException("operand of ! must be boolean");
            }
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            putAsBoolean(v);
            coerceTo(type, v);
        }

        @Override
        public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
            myOperand.condJump(label, !jumpIfFalse, v);
        }
    }

    private static class ArrayElement extends StackValue {
        public ArrayElement(Type type) {
            super(type);
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            v.aload(this.type);    // assumes array and index are on the stack
            coerceTo(type, v);
        }

        @Override
        public void store(Type topOfStackType, InstructionAdapter v) {
            coerceFrom(topOfStackType, v);
            v.astore(this.type);
        }

        @Override
        public void dupReceiver(InstructionAdapter v) {
            v.dup2();   // array and index
        }

        @Override
        public int receiverSize() {
            return 2;
        }
    }

    private static class CollectionElement extends StackValue {
        private final Callable getter;
        private final Callable setter;
        private final ExpressionCodegen codegen;
        private final GenerationState state;
        private final FrameMap frame;
        private final ResolvedCall resolvedGetCall;
        private final ResolvedCall resolvedSetCall;
        private final FunctionDescriptor setterDescriptor;
        private final FunctionDescriptor getterDescriptor;

        public CollectionElement(
                Type type,
                ResolvedCall resolvedGetCall,
                ResolvedCall resolvedSetCall,
                ExpressionCodegen codegen,
                GenerationState state
        ) {
            super(type);
            this.resolvedGetCall = resolvedGetCall;
            this.resolvedSetCall = resolvedSetCall;
            this.state = state;
            this.setterDescriptor = resolvedSetCall == null ? null : resolvedSetCall.getResultingDescriptor();
            this.getterDescriptor = resolvedGetCall == null ? null : resolvedGetCall.getResultingDescriptor();
            this.setter = resolvedSetCall == null ? null : codegen.resolveToCallable(setterDescriptor, false);
            this.getter = resolvedGetCall == null ? null : codegen.resolveToCallable(getterDescriptor, false);
            this.codegen = codegen;
            this.frame = codegen.myFrameMap;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            if (getter == null) {
                throw new UnsupportedOperationException("no getter specified");
            }
            if (getter instanceof CallableMethod) {
                ((CallableMethod) getter).invokeWithNotNullAssertion(v, state, resolvedGetCall);
            }
            else {
                ((IntrinsicMethod) getter).generate(codegen, v, this.type, null, null, null);
            }
            coerceTo(type, v);
        }

        @Override
        public void store(Type topOfStackType, InstructionAdapter v) {
            if (setter == null) {
                throw new UnsupportedOperationException("no setter specified");
            }
            if (setter instanceof CallableMethod) {
                CallableMethod method = (CallableMethod) setter;
                Method asmMethod = method.getAsmMethod();
                Type[] argumentTypes = asmMethod.getArgumentTypes();
                coerce(topOfStackType, argumentTypes[argumentTypes.length - 1], v);
                method.invokeWithNotNullAssertion(v, state, resolvedSetCall);
                Type returnType = asmMethod.getReturnType();
                if (returnType != Type.VOID_TYPE) {
                    pop(v, returnType);
                }
            }
            else {
                //noinspection ConstantConditions
                ((IntrinsicMethod) setter).generate(codegen, v, null, null, null, null);
            }
        }

        @Override
        public int receiverSize() {
            if (isStandardStack(resolvedGetCall, 1) && isStandardStack(resolvedSetCall, 2)) {
                return 2;
            }
            else {
                return -1;
            }
        }

        @Override
        public void dupReceiver(InstructionAdapter v) {
            if (isStandardStack(resolvedGetCall, 1) && isStandardStack(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.getReceiverArgument();
            int receiverIndex = -1;
            if (receiverParameter.exists()) {
                Type type = codegen.typeMapper.mapType(receiverParameter.getType());
                receiverIndex = frame.enterTemp(type);
                v.store(receiverIndex, type);
            }

            ReceiverValue thisObject = resolvedGetCall.getThisObject();
            int thisIndex = -1;
            if (thisObject.exists()) {
                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.getThisObject().exists()) {
                if (resolvedSetCall.getReceiverArgument().exists()) {
                    codegen.generateFromResolvedCall(resolvedSetCall.getThisObject(), OBJECT_TYPE);
                }
                v.load(realReceiverIndex, realReceiverType);
            }
            else {
                if (resolvedSetCall.getReceiverArgument().exists()) {
                    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();
        }

        private boolean isStandardStack(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 (codegen.typeMapper.mapType(valueParameter.getType()).getSize() != 1) {
                    return false;
                }
            }

            if (call.getThisObject().exists()) {
                if (call.getReceiverArgument().exists()) {
                    return false;
                }
            }
            else {
                if (codegen.typeMapper.mapType(call.getResultingDescriptor().getReceiverParameter().getType())
                            .getSize() != 1) {
                    return false;
                }
            }

            return true;
        }
    }


    public static class Field extends StackValueWithSimpleReceiver {
        public final Type owner;
        public final String name;

        public Field(Type type, Type owner, String name, boolean isStatic) {
            super(type, isStatic);
            this.owner = owner;
            this.name = name;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            v.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, owner.getInternalName(), name, this.type.getDescriptor());
            coerceTo(type, v);
        }

        @Override
        public void store(Type topOfStackType, InstructionAdapter v) {
            coerceFrom(topOfStackType, v);
            v.visitFieldInsn(isStatic ? PUTSTATIC : PUTFIELD, owner.getInternalName(), name, this.type.getDescriptor());
        }
    }

    static class Property extends StackValueWithSimpleReceiver {
        private final CallableMethod getter;
        private final CallableMethod setter;
        private final Type methodOwner;

        private final PropertyDescriptor descriptor;
        private final GenerationState state;

        private final String fieldName;

        public Property(
                @NotNull PropertyDescriptor descriptor, @NotNull Type methodOwner,
                @Nullable CallableMethod getter, @Nullable CallableMethod setter, boolean isStatic,
                @Nullable String fieldName, @NotNull Type type, @NotNull GenerationState state
        ) {
            super(type, isStatic);
            this.methodOwner = methodOwner;
            this.getter = getter;
            this.setter = setter;
            this.descriptor = descriptor;
            this.state = state;
            this.fieldName = fieldName;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            if (getter == null) {
                assert fieldName != null : "Property should have either a getter or a field name: " + descriptor;
                v.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, methodOwner.getInternalName(), fieldName, this.type.getDescriptor());
                genNotNullAssertionForField(v, state, descriptor);
                coerceTo(type, v);
            }
            else {
                Method method = getter.getAsmMethod();
                v.visitMethodInsn(getter.getInvokeOpcode(), getter.getOwner().getInternalName(), method.getName(), method.getDescriptor());
                coerce(method.getReturnType(), type, v);
            }
        }

        @Override
        public void store(Type topOfStackType, InstructionAdapter v) {
            coerceFrom(topOfStackType, v);
            if (setter == null) {
                assert fieldName != null : "Property should have either a setter or a field name: " + descriptor;
                v.visitFieldInsn(isStatic ? PUTSTATIC : PUTFIELD, methodOwner.getInternalName(), fieldName, this.type.getDescriptor());
            }
            else {
                Method method = setter.getAsmMethod();
                v.visitMethodInsn(setter.getInvokeOpcode(), setter.getOwner().getInternalName(), method.getName(), method.getDescriptor());
            }
        }

        public boolean isPropertyWithBackingFieldInOuterClass() {
            return descriptor instanceof AccessorForPropertyBackingFieldInOuterClass;
        }
    }

    private static class Expression extends StackValue {
        private final JetExpression expression;
        private final ExpressionCodegen generator;

        public Expression(Type type, JetExpression expression, ExpressionCodegen generator) {
            super(type);
            this.expression = expression;
            this.generator = generator;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            generator.gen(expression, type);
        }
    }

    public static class Shared extends StackValue {
        private final int index;
        private boolean isReleaseOnPut = false;

        public Shared(int index, Type type) {
            super(type);
            this.index = index;
        }

        public void releaseOnPut() {
            isReleaseOnPut = true;
        }

        public int getIndex() {
            return index;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            v.load(index, OBJECT_TYPE);
            Type refType = refType(this.type);
            Type sharedType = sharedTypeForType(this.type);
            v.visitFieldInsn(GETFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
            coerceFrom(refType, v);
            coerceTo(type, v);
            if (isReleaseOnPut) {
                v.aconst(null);
                v.store(index, OBJECT_TYPE);
            }
        }

        @Override
        public void store(Type topOfStackType, InstructionAdapter v) {
            coerceFrom(topOfStackType, v);
            v.load(index, OBJECT_TYPE);
            AsmUtil.swap(v, sharedTypeForType(this.type), topOfStackType);
            Type refType = refType(this.type);
            Type sharedType = sharedTypeForType(this.type);
            v.visitFieldInsn(PUTFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
        }
    }

    public static Type sharedTypeForType(Type type) {
        switch (type.getSort()) {
            case Type.OBJECT:
            case Type.ARRAY:
                return OBJECT_REF_TYPE;
            case Type.BYTE:
                return Type.getObjectType("kotlin/jvm/internal/Ref$ByteRef");
            case Type.SHORT:
                return Type.getObjectType("kotlin/jvm/internal/Ref$ShortRef");
            case Type.CHAR:
                return Type.getObjectType("kotlin/jvm/internal/Ref$CharRef");
            case Type.INT:
                return Type.getObjectType("kotlin/jvm/internal/Ref$IntRef");
            case Type.LONG:
                return Type.getObjectType("kotlin/jvm/internal/Ref$LongRef");
            case Type.BOOLEAN:
                return Type.getObjectType("kotlin/jvm/internal/Ref$BooleanRef");
            case Type.FLOAT:
                return Type.getObjectType("kotlin/jvm/internal/Ref$FloatRef");
            case Type.DOUBLE:
                return Type.getObjectType("kotlin/jvm/internal/Ref$DoubleRef");
            default:
                throw new UnsupportedOperationException();
        }
    }

    public static Type refType(Type type) {
        if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
            return OBJECT_TYPE;
        }

        return type;
    }

    static class FieldForSharedVar extends StackValueWithSimpleReceiver {
        final Type owner;
        final String name;

        public FieldForSharedVar(Type type, Type owner, String name) {
            super(type, false);
            this.owner = owner;
            this.name = name;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            Type sharedType = sharedTypeForType(this.type);
            Type refType = refType(this.type);
            v.visitFieldInsn(GETFIELD, sharedType.getInternalName(), "element", refType.getDescriptor());
            coerceFrom(refType, v);
            coerceTo(type, v);
        }

        @Override
        public void store(Type topOfStackType, InstructionAdapter v) {
            coerceFrom(topOfStackType, v);
            v.visitFieldInsn(PUTFIELD, sharedTypeForType(type).getInternalName(), "element", refType(type).getDescriptor());
        }
    }

    public static class Composed extends StackValue {
        public final StackValue prefix;
        public final StackValue suffix;

        public Composed(StackValue prefix, StackValue suffix) {
            super(suffix.type);
            this.prefix = prefix;
            this.suffix = suffix;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            prefix.put(prefix.type, v);
            suffix.put(type, v);
        }

        @Override
        public void store(Type topOfStackType, InstructionAdapter v) {
            prefix.put(OBJECT_TYPE, v);
            suffix.store(topOfStackType, v);
        }
    }

    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);
            this.codegen = codegen;
            this.descriptor = descriptor;
            this.isSuper = isSuper;
            this.coerceType = coerceType;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            StackValue stackValue = codegen.generateThisOrOuter(descriptor, isSuper);
            stackValue.put(coerceType ? type : stackValue.type, 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 put(Type type, InstructionAdapter v) {
            if (!type.equals(Type.VOID_TYPE)) {
                v.load(index, Type.INT_TYPE);
                coerceTo(type, v);
            }
            v.iinc(index, increment);
        }
    }

    private static class PreIncrement extends StackValue {
        private final int index;
        private final int increment;

        public PreIncrement(int index, int increment) {
            super(Type.INT_TYPE);
            this.index = index;
            this.increment = increment;
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            v.iinc(index, increment);
            if (!type.equals(Type.VOID_TYPE)) {
                v.load(index, Type.INT_TYPE);
                coerceTo(type, v);
            }
        }
    }

    public static class CallReceiver extends StackValue {
        private final ResolvedCall resolvedCall;
        final StackValue receiver;
        private final ExpressionCodegen codegen;
        private final CallableMethod callableMethod;
        private final boolean putReceiverArgumentOnStack;

        public CallReceiver(
                ResolvedCall resolvedCall,
                StackValue receiver,
                ExpressionCodegen codegen,
                CallableMethod callableMethod,
                boolean putReceiverArgumentOnStack
        ) {
            super(calcType(resolvedCall, codegen, callableMethod));
            this.resolvedCall = resolvedCall;
            this.receiver = receiver;
            this.codegen = codegen;
            this.callableMethod = callableMethod;
            this.putReceiverArgumentOnStack = putReceiverArgumentOnStack;
        }

        private static Type calcType(ResolvedCall resolvedCall, ExpressionCodegen codegen, CallableMethod callableMethod) {
            ReceiverValue thisObject = resolvedCall.getThisObject();
            ReceiverValue receiverArgument = resolvedCall.getReceiverArgument();

            CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();

            if (receiverArgument.exists()) {
                if (callableMethod != null) {
                    return callableMethod.getReceiverClass();
                }
                else {
                    return codegen.typeMapper.mapType(descriptor.getReceiverParameter().getType());
                }
            } else if (thisObject.exists()) {
                if (callableMethod != null) {
                    return callableMethod.getThisType();
                }
                else {
                    return codegen.typeMapper.mapType(descriptor.getExpectedThisObject().getType());
                }
            }
            else if (isLocalFunCall(callableMethod)) {
                return callableMethod.getGenerateCalleeType();
            }
            else {
                return Type.VOID_TYPE;
            }
        }

        @Override
        public void put(Type type, InstructionAdapter v) {
            CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();

            ReceiverValue thisObject = resolvedCall.getThisObject();
            ReceiverValue receiverArgument = resolvedCall.getReceiverArgument();
            int depth;
            if (thisObject.exists()) {
                if (receiverArgument.exists()) {
                    Type resultType = callableMethod != null ? callableMethod.getOwner() : codegen.typeMapper
                            .mapType(descriptor.getExpectedThisObject().getType());

                    codegen.generateFromResolvedCall(thisObject, resultType);
                }
                else {
                    genReceiver(v, thisObject, type, null, 0);
                }

                depth = 1;
            } else if (isLocalFunCall(callableMethod)) {
                assert receiver == none() || receiverArgument.exists(): "Receiver should be present only for local extension function: " + callableMethod;
                StackValue value = codegen.findLocalOrCapturedValue(descriptor.getOriginal());
                assert value != null : "Local fun should be found in locals or in captured params: " + resolvedCall;
                value.put(callableMethod.getGenerateCalleeType(), v);

                depth = 1;
            }
            else {
                depth = 0;
            }

            if (putReceiverArgumentOnStack && receiverArgument.exists()) {
                genReceiver(v, receiverArgument, type, descriptor.getReceiverParameter(), depth);
            }
        }

        private void genReceiver(
                InstructionAdapter v, ReceiverValue receiverArgument, Type type,
                @Nullable ReceiverParameterDescriptor receiverParameter, int depth
        ) {
            if (receiver == StackValue.none()) {
                if (receiverParameter != null) {
                    Type receiverType = codegen.typeMapper.mapType(receiverParameter.getType());
                    codegen.generateFromResolvedCall(receiverArgument, receiverType);
                    StackValue.onStack(receiverType).put(type, v);
                }
                else {
                    codegen.generateFromResolvedCall(receiverArgument, type);
                }
            }
            else {
                receiver.moveToTopOfStack(type, v, depth);
            }
        }
    }

    public abstract static class StackValueWithSimpleReceiver extends StackValue {

        public final boolean isStatic;

        public StackValueWithSimpleReceiver(@NotNull Type type, boolean isStatic) {
            super(type);
            this.isStatic = isStatic;
        }

        @Override
        public void dupReceiver(InstructionAdapter v) {
            if (!isStatic) {
                v.dup();
            }
        }

        @Override
        public int receiverSize() {
            return isStatic ? 0 : 1;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy