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

org.teavm.model.emit.ValueEmitter Maven / Gradle / Ivy

There is a newer version: 0.2.8
Show newest version
/*
 *  Copyright 2015 Alexey Andreev.
 *
 *  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.teavm.model.emit;

import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassReader;
import org.teavm.model.FieldReference;
import org.teavm.model.Incoming;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.PrimitiveType;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.ArrayElementType;
import org.teavm.model.instructions.ArrayLengthInstruction;
import org.teavm.model.instructions.BinaryBranchingCondition;
import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BinaryInstruction;
import org.teavm.model.instructions.BinaryOperation;
import org.teavm.model.instructions.BranchingCondition;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.CastIntegerDirection;
import org.teavm.model.instructions.CastIntegerInstruction;
import org.teavm.model.instructions.CastNumberInstruction;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.GetElementInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.IntegerSubtype;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.IsInstanceInstruction;
import org.teavm.model.instructions.NegateInstruction;
import org.teavm.model.instructions.NumericOperandType;
import org.teavm.model.instructions.PutElementInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.instructions.RaiseInstruction;
import org.teavm.model.instructions.UnwrapArrayInstruction;

public class ValueEmitter {
    ProgramEmitter pe;
    BasicBlock block;
    Variable variable;
    ValueType type;

    ValueEmitter(ProgramEmitter programEmitter, BasicBlock block, Variable variable, ValueType type) {
        this.pe = programEmitter;
        this.block = block;
        this.variable = variable;
        this.type = type;
    }

    public ProgramEmitter getProgramEmitter() {
        return pe;
    }

    public BasicBlock getBlock() {
        return block;
    }

    public Variable getVariable() {
        return variable;
    }

    public ValueType getType() {
        return type;
    }

    public ValueEmitter getField(String name, ValueType type) {
        if (!(this.type instanceof ValueType.Object)) {
            throw new EmitException("Can't get field of non-object type: " + type);
        }

        String className = ((ValueType.Object) this.type).getClassName();
        Variable var = pe.getProgram().createVariable();
        GetFieldInstruction insn = new GetFieldInstruction();
        insn.setField(new FieldReference(className, name));
        insn.setFieldType(type);
        insn.setReceiver(var);
        insn.setInstance(variable);
        pe.addInstruction(insn);
        return pe.var(var, type);
    }

    public ValueEmitter getField(String name, Class type) {
        return getField(name, ValueType.parse(type));
    }

    public ProgramEmitter setField(String name, ValueEmitter value) {
        if (!(type instanceof ValueType.Object)) {
            throw new EmitException("Can't get field of non-object type: " + type);
        }

        String className = ((ValueType.Object) type).getClassName();
        PutFieldInstruction insn = new PutFieldInstruction();
        insn.setField(new FieldReference(className, name));
        insn.setFieldType(type);
        insn.setInstance(variable);
        insn.setValue(value.getVariable());
        pe.addInstruction(insn);
        return pe;
    }

    public ValueEmitter neg() {
        if (!(type instanceof ValueType.Primitive)) {
            throw new EmitException("Can't negate non-primitive: " + type);
        }

        ValueEmitter value = this;
        PrimitiveType type = ((ValueType.Primitive) this.type).getKind();
        IntegerSubtype subtype = convertToIntegerSubtype(type);
        if (subtype != null) {
            value = value.castToInteger(subtype);
            type = PrimitiveType.INTEGER;
        }

        ValueEmitter result = pe.newVar(ValueType.primitive(type));
        NegateInstruction insn = new NegateInstruction(convertToNumeric(type));
        insn.setOperand(value.variable);
        insn.setReceiver(result.variable);
        pe.addInstruction(insn);
        return result;
    }

    static class Pair {
        ValueEmitter first;
        ValueEmitter second;

        public Pair(ValueEmitter first, ValueEmitter second) {
            this.first = first;
            this.second = second;
        }
    }

    private Pair commonNumeric(ValueEmitter other) {
        if (!(type instanceof ValueType.Primitive)) {
            throw new EmitException("First argument is not a primitive: " + type);
        }
        if (!(other.type instanceof ValueType.Primitive)) {
            throw new EmitException("First argument is not a primitive: " + other.type);
        }

        PrimitiveType firstType = ((ValueType.Primitive) type).getKind();
        PrimitiveType secondType = ((ValueType.Primitive) other.type).getKind();

        if (firstType == PrimitiveType.BOOLEAN) {
            throw new EmitException("First argument is not numeric: " + type);
        }
        if (secondType == PrimitiveType.BOOLEAN) {
            throw new EmitException("Second argument is not numeric: " + other.type);
        }

        ValueEmitter a = this;
        ValueEmitter b = other;

        IntegerSubtype firstSubtype = convertToIntegerSubtype(firstType);
        if (firstSubtype != null) {
            a = castFromInteger(firstSubtype);
            firstType = PrimitiveType.INTEGER;
        }
        IntegerSubtype secondSubtype = convertToIntegerSubtype(secondType);
        if (secondSubtype != null) {
            b = castFromInteger(secondSubtype);
            secondType = PrimitiveType.INTEGER;
        }

        NumericOperandType firstNumeric = convertToNumeric(firstType);
        NumericOperandType secondNumeric = convertToNumeric(secondType);
        int commonIndex = Math.max(firstNumeric.ordinal(), secondNumeric.ordinal());
        NumericOperandType common = NumericOperandType.values()[commonIndex];
        ValueType commonType = ValueType.primitive(convertNumeric(common));

        if (firstNumeric != common) {
            CastNumberInstruction insn = new CastNumberInstruction(firstNumeric, common);
            insn.setValue(a.getVariable());
            a = pe.newVar(commonType);
            insn.setReceiver(a.getVariable());
            pe.addInstruction(insn);
        }
        if (secondNumeric != common) {
            CastNumberInstruction insn = new CastNumberInstruction(secondNumeric, common);
            insn.setValue(b.getVariable());
            b = pe.newVar(commonType);
            insn.setReceiver(b.getVariable());
            pe.addInstruction(insn);
        }

        return new Pair(a, b);
    }

    private ValueEmitter binary(BinaryOperation op, ValueEmitter other) {
        Pair pair = commonNumeric(other);
        return binaryOp(op, pair.first, pair.second, pair.first.type);
    }

    private ValueEmitter binaryOp(BinaryOperation op, ValueEmitter a, ValueEmitter b, ValueType type) {
        Variable var = pe.getProgram().createVariable();
        PrimitiveType common = ((ValueType.Primitive) a.type).getKind();

        BinaryInstruction insn = new BinaryInstruction(op, convertToNumeric(common));
        insn.setFirstOperand(a.getVariable());
        insn.setSecondOperand(b.getVariable());
        insn.setReceiver(var);
        pe.addInstruction(insn);
        return pe.var(var, type);
    }

    private IntegerSubtype convertToIntegerSubtype(PrimitiveType type) {
        switch (type) {
            case BYTE:
                return IntegerSubtype.BYTE;
            case SHORT:
                return IntegerSubtype.SHORT;
            case CHARACTER:
                return IntegerSubtype.CHAR;
            default:
                break;
        }
        return null;
    }

    private NumericOperandType convertToNumeric(PrimitiveType type) {
        switch (type) {
            case BYTE:
            case SHORT:
            case CHARACTER:
            case INTEGER:
                return NumericOperandType.INT;
            case LONG:
                return NumericOperandType.LONG;
            case FLOAT:
                return NumericOperandType.FLOAT;
            case DOUBLE:
                return NumericOperandType.DOUBLE;
            default:
                break;
        }
        throw new AssertionError("Unexpected type: " + type);
    }

    private PrimitiveType convertNumeric(NumericOperandType type) {
        switch (type) {
            case INT:
                return PrimitiveType.INTEGER;
            case LONG:
                return PrimitiveType.LONG;
            case FLOAT:
                return PrimitiveType.FLOAT;
            case DOUBLE:
                return PrimitiveType.DOUBLE;
            default:
                break;
        }
        throw new AssertionError("Unknown type: " + type);
    }

    public ValueEmitter add(ValueEmitter other) {
        return binary(BinaryOperation.ADD, other);
    }

    public ValueEmitter add(int value) {
        return binary(BinaryOperation.ADD, pe.constant(value));
    }

    public ValueEmitter sub(ValueEmitter other) {
        return binary(BinaryOperation.SUBTRACT, other);
    }

    public ValueEmitter sub(int value) {
        return binary(BinaryOperation.SUBTRACT, pe.constant(value));
    }

    public ValueEmitter mul(ValueEmitter other) {
        return binary(BinaryOperation.MULTIPLY, other);
    }

    public ValueEmitter mul(int value) {
        return binary(BinaryOperation.MULTIPLY, pe.constant(value));
    }

    public ValueEmitter div(ValueEmitter other) {
        return binary(BinaryOperation.DIVIDE, other);
    }

    public ValueEmitter div(int value) {
        return binary(BinaryOperation.DIVIDE, pe.constant(value));
    }

    public ValueEmitter rem(ValueEmitter other) {
        return binary(BinaryOperation.MODULO, other);
    }

    public ValueEmitter rem(int value) {
        return binary(BinaryOperation.MODULO, pe.constant(value));
    }

    public ValueEmitter compareTo(ValueEmitter other) {
        Pair pair = commonNumeric(other);
        return binaryOp(BinaryOperation.COMPARE, pair.first, pair.second, ValueType.INTEGER);
    }

    public ValueEmitter compareTo(int value) {
        return compareTo(pe.constant(value));
    }

    private ValueEmitter logical(BinaryOperation op, ValueEmitter other) {
        Pair pair = commonNumeric(other);
        PrimitiveType common = ((ValueType.Primitive) pair.first.type).getKind();
        checkInteger(common);
        return binaryOp(op, pair.first, pair.second, pair.first.type);
    }

    public ValueEmitter bitAnd(ValueEmitter other) {
        return logical(BinaryOperation.AND, other);
    }

    private void checkInteger(PrimitiveType common) {
        switch (common) {
            case FLOAT:
            case DOUBLE:
                throw new EmitException("Can't perform bitwise operation between non-integers: " + common);
            default:
                break;
        }
    }

    public ValueEmitter bitAnd(int value) {
        return bitAnd(pe.constant(value));
    }

    public ValueEmitter bitOr(ValueEmitter other) {
        return logical(BinaryOperation.OR, other);
    }

    public ValueEmitter bitOr(int value) {
        return bitOr(pe.constant(value));
    }

    public ValueEmitter bitXor(ValueEmitter other) {
        return logical(BinaryOperation.XOR, other);
    }

    public ValueEmitter bitXor(int value) {
        return bitXor(pe.constant(value));
    }

    public ValueEmitter shl(ValueEmitter other) {
        return shift(BinaryOperation.SHIFT_LEFT, other);
    }

    public ValueEmitter shl(int value) {
        return shl(pe.constant(value));
    }

    public ValueEmitter shr(ValueEmitter other) {
        return shift(BinaryOperation.SHIFT_RIGHT, other);
    }

    public ValueEmitter shr(int value) {
        return shr(pe.constant(value));
    }

    public ValueEmitter shru(ValueEmitter other) {
        return shift(BinaryOperation.SHIFT_RIGHT_UNSIGNED, other);
    }

    public ValueEmitter shru(int value) {
        return shru(pe.constant(value));
    }

    private ValueEmitter shift(BinaryOperation op, ValueEmitter other) {
        if (!(type instanceof ValueType.Primitive) || !(other.type instanceof ValueType.Primitive)) {
            throw new EmitException("Can't shift " + type + " by " + other.type);
        }

        ValueType valueType = type;
        PrimitiveType kind = ((ValueType.Primitive) type).getKind();
        switch (kind) {
            case FLOAT:
            case DOUBLE:
                throw new EmitException("Can't perform bit shift operation over non-integer: " + type);
            default:
                break;
        }

        PrimitiveType shiftKind = ((ValueType.Primitive) type).getKind();
        switch (kind) {
            case BYTE:
            case SHORT:
            case INTEGER:
                break;
            default:
                throw new EmitException("Can't perform bit shift operation with non-integer "
                        + "shift: " + type);
        }
        other = other.castToInteger(convertToIntegerSubtype(shiftKind));

        ValueEmitter value = this;
        IntegerSubtype subtype = convertToIntegerSubtype(kind);
        if (subtype != null) {
            value = value.castToInteger(subtype);
            valueType = ValueType.INTEGER;
        }

        return binaryOp(op, value, other, valueType);
    }

    public ValueEmitter invoke(InvocationType invokeType, MethodReference method, ValueEmitter... arguments) {
        if (!(type instanceof ValueType.Object)) {
            throw new EmitException("Can't invoke method on non-object type: " + type);
        }

        ClassHierarchy hierarchy = pe.hierarchy;
        for (int i = 0; i < method.parameterCount(); ++i) {
            if (!hierarchy.isSuperType(method.parameterType(i), arguments[i].getType(), false)) {
                throw new EmitException("Argument " + i + " of type " + arguments[i].getType() + " is "
                        + "not compatible with method " + method);
            }
        }

        if (!hierarchy.isSuperType(method.getClassName(), ((ValueType.Object) type).getClassName(), true)) {
            throw new EmitException("Can't call " + method + " on non-compatible class " + type);
        }

        Variable result = null;
        if (method.getReturnType() != ValueType.VOID) {
            result = pe.getProgram().createVariable();
        }
        InvokeInstruction insn = new InvokeInstruction();
        insn.setType(invokeType);
        insn.setMethod(method);
        insn.setInstance(variable);
        insn.setReceiver(result);
        Variable[] insnArguments = new Variable[arguments.length];
        for (int i = 0; i < insnArguments.length; ++i) {
            insnArguments[i] = arguments[i].variable;
        }
        insn.setArguments(insnArguments);
        pe.addInstruction(insn);
        return result != null ? pe.var(result, method.getReturnType()) : null;
    }

    public ValueEmitter invoke(InvocationType invokeType, String className, String name, ValueType resultType,
            ValueEmitter... arguments) {
        if (!(type instanceof ValueType.Object)) {
            throw new EmitException("Can't invoke method on non-object type: " + type);
        }

        Variable result = null;
        ValueType[] signature = new ValueType[arguments.length + 1];
        for (int i = 0; i < arguments.length; ++i) {
            signature[i] = arguments[i].type;
        }
        signature[arguments.length] = resultType;

        ClassReader cls = pe.classSource.get(className);
        MethodReader methodReader = cls != null ? cls.getMethod(new MethodDescriptor(name, signature)) : null;
        MethodReference method = methodReader != null
                ? methodReader.getReference()
                : new MethodReference(className, name, signature);
        if (method.getReturnType() != ValueType.VOID) {
            result = pe.getProgram().createVariable();
        }
        InvokeInstruction insn = new InvokeInstruction();
        insn.setType(invokeType);
        insn.setMethod(method);
        insn.setInstance(variable);
        insn.setReceiver(result);
        Variable[] insnArguments = new Variable[arguments.length];
        for (int i = 0; i < insnArguments.length; ++i) {
            insnArguments[i] = arguments[i].variable;
        }
        insn.setArguments(insnArguments);
        pe.addInstruction(insn);
        return result != null ? pe.var(result, resultType) : null;
    }

    public ValueEmitter invoke(InvocationType invokeType, String name, ValueType resultType,
            ValueEmitter... arguments) {
        return invoke(invokeType, ((ValueType.Object) type).getClassName(), name, resultType, arguments);
    }

    public ValueEmitter invokeSpecial(MethodReference method, ValueEmitter... arguments) {
        return invoke(InvocationType.SPECIAL, method, arguments);
    }

    public ValueEmitter invokeSpecial(String className, String name, ValueType resultType, ValueEmitter... arguments) {
        return invoke(InvocationType.SPECIAL, className, name, resultType, arguments);
    }

    public ValueEmitter invokeSpecial(String name, ValueType resultType, ValueEmitter... arguments) {
        return invoke(InvocationType.SPECIAL, name, resultType, arguments);
    }

    public ValueEmitter invokeSpecial(String name, Class resultType, ValueEmitter... arguments) {
        return invoke(InvocationType.SPECIAL, name, ValueType.parse(resultType), arguments);
    }

    public ProgramEmitter invokeSpecial(String className, String name, ValueEmitter... arguments) {
        invokeSpecial(className, name, ValueType.VOID, arguments);
        return pe;
    }

    public ProgramEmitter invokeSpecial(Class cls, String name, ValueEmitter... arguments) {
        invokeSpecial(cls.getName(), name, ValueType.VOID, arguments);
        return pe;
    }

    public ProgramEmitter invokeSpecial(String name, ValueEmitter... arguments) {
        invokeSpecial(name, ValueType.VOID, arguments);
        return pe;
    }

    public ValueEmitter invokeVirtual(String name, ValueType resultType, ValueEmitter... arguments) {
        return invoke(InvocationType.VIRTUAL, name, resultType, arguments);
    }

    public ValueEmitter invokeVirtual(MethodReference method, ValueEmitter... arguments) {
        return invoke(InvocationType.VIRTUAL, method, arguments);
    }

    public ValueEmitter invokeVirtual(String name, Class resultType, ValueEmitter... arguments) {
        return invoke(InvocationType.VIRTUAL, name, ValueType.parse(resultType), arguments);
    }

    public ProgramEmitter invokeVirtual(String name, ValueEmitter... arguments) {
        invokeVirtual(name, ValueType.VOID, arguments);
        return pe;
    }

    public ValueEmitter join(BasicBlock block, ValueEmitter other, BasicBlock otherBlock, ValueType type) {
        Variable var = pe.getProgram().createVariable();
        Phi phi = new Phi();
        phi.setReceiver(var);
        Incoming incoming = new Incoming();
        incoming.setSource(block);
        incoming.setValue(variable);
        phi.getIncomings().add(incoming);
        incoming = new Incoming();
        incoming.setSource(otherBlock);
        incoming.setValue(other.variable);
        phi.getIncomings().add(incoming);
        pe.getBlock().getPhis().add(phi);
        return new ValueEmitter(pe, pe.getBlock(), var, type);
    }

    public ForkEmitter fork(BinaryBranchingCondition condition, ValueEmitter other) {
        final BinaryBranchingInstruction insn = new BinaryBranchingInstruction(condition);
        insn.setFirstOperand(variable);
        insn.setSecondOperand(other.variable);
        pe.addInstruction(insn);
        return new ForkEmitter(pe) {
            @Override public ForkEmitter setThen(BasicBlock block) {
                insn.setConsequent(block);
                return this;
            }
            @Override public ForkEmitter setElse(BasicBlock block) {
                insn.setAlternative(block);
                return this;
            }
        };
    }

    public ForkEmitter fork(BranchingCondition condition) {
        final BranchingInstruction insn = new BranchingInstruction(condition);
        insn.setOperand(variable);
        pe.addInstruction(insn);
        return new ForkEmitter(pe) {
            @Override public ForkEmitter setThen(BasicBlock block) {
                insn.setConsequent(block);
                return this;
            }
            @Override public ForkEmitter setElse(BasicBlock block) {
                insn.setAlternative(block);
                return this;
            }
        };
    }

    public ConditionEmitter isTrue() {
        return new ConditionEmitter(pe, fork(BranchingCondition.NOT_EQUAL));
    }

    public ConditionEmitter isFalse() {
        return new ConditionEmitter(pe, fork(BranchingCondition.EQUAL));
    }

    public ConditionEmitter isEqualTo(ValueEmitter other) {
        return new ConditionEmitter(pe, fork(BinaryBranchingCondition.EQUAL, other));
    }

    public ConditionEmitter isNotEqualTo(ValueEmitter other) {
        return new ConditionEmitter(pe, fork(BinaryBranchingCondition.NOT_EQUAL, other));
    }

    public ConditionEmitter isSame(ValueEmitter other) {
        return new ConditionEmitter(pe, fork(BinaryBranchingCondition.REFERENCE_EQUAL, other));
    }

    public ConditionEmitter isNotSame(ValueEmitter other) {
        return new ConditionEmitter(pe, fork(BinaryBranchingCondition.REFERENCE_NOT_EQUAL, other));
    }

    public ConditionEmitter isNull() {
        return isSame(pe.constantNull(getType()));
    }

    public ConditionEmitter isNotNull() {
        return isNotSame(pe.constantNull(getType()));
    }

    public ConditionEmitter isGreaterThan(ValueEmitter other) {
        return new ConditionEmitter(pe, compareTo(other).fork(BranchingCondition.GREATER));
    }

    public ConditionEmitter isGreaterOrEqualTo(ValueEmitter other) {
        return new ConditionEmitter(pe, compareTo(other).fork(BranchingCondition.GREATER_OR_EQUAL));
    }

    public ConditionEmitter isLessThan(ValueEmitter other) {
        return new ConditionEmitter(pe, compareTo(other).fork(BranchingCondition.LESS));
    }

    public ConditionEmitter isLessOrEqualTo(ValueEmitter other) {
        return new ConditionEmitter(pe, compareTo(other).fork(BranchingCondition.LESS_OR_EQUAL));
    }

    public void returnValue() {
        ExitInstruction insn = new ExitInstruction();
        insn.setValueToReturn(variable);
        pe.addInstruction(insn);
    }

    public void raise() {
        if (!pe.hierarchy.isSuperType(ValueType.object("java.lang.Throwable"), type, true)) {
            throw new EmitException("Can't throw non-exception value: " + type);
        }

        RaiseInstruction insn = new RaiseInstruction();
        insn.setException(variable);
        pe.addInstruction(insn);
    }

    public ValueEmitter cast(Class type) {
        return cast(ValueType.parse(type));
    }

    public ValueEmitter cast(ValueType type) {
        if (type.equals(this.type)) {
            return this;
        } else if (pe.hierarchy.isSuperType(type, this.type, false)) {
            return pe.var(variable.getIndex(), type);
        }

        if (type instanceof ValueType.Primitive) {
            if (!(this.type instanceof ValueType.Primitive)) {
                throw new EmitException("Can't convert " + this.type + " to " + type);
            }

            ValueEmitter value = this;
            PrimitiveType sourceKind = ((ValueType.Primitive) this.type).getKind();
            PrimitiveType targetKind = ((ValueType.Primitive) type).getKind();

            if (sourceKind == PrimitiveType.BOOLEAN) {
                switch (targetKind) {
                    case BOOLEAN:
                    case BYTE:
                    case SHORT:
                    case INTEGER:
                    case CHARACTER:
                        return pe.var(value.getVariable(), type);
                    default:
                        throw new EmitException("Can't convert " + this.type + " to " + type);
                }
            } else if (targetKind == PrimitiveType.BOOLEAN) {
                switch (sourceKind) {
                    case BOOLEAN:
                    case BYTE:
                    case SHORT:
                    case INTEGER:
                    case CHARACTER:
                        return pe.var(value.getVariable(), type);
                    default:
                        throw new EmitException("Can't convert " + this.type + " to " + type);
                }
            }

            IntegerSubtype sourceSubtype = convertToIntegerSubtype(sourceKind);
            if (sourceSubtype != null) {
                sourceKind = PrimitiveType.INTEGER;
                value = castToInteger(sourceSubtype);
            }
            NumericOperandType sourceNumeric = convertToNumeric(sourceKind);
            NumericOperandType targetNumeric = convertToNumeric(targetKind);

            CastNumberInstruction insn = new CastNumberInstruction(sourceNumeric, targetNumeric);
            insn.setValue(value.getVariable());
            value = pe.newVar(type);
            insn.setReceiver(value.getVariable());
            pe.addInstruction(insn);

            IntegerSubtype targetSubtype = convertToIntegerSubtype(targetKind);
            if (targetSubtype != null) {
                value = castFromInteger(targetSubtype);
            }

            return value;
        } else {
            if (this.type instanceof ValueType.Primitive) {
                return boxPrimitive(type);
            }
            Variable result = pe.getProgram().createVariable();
            CastInstruction insn = new CastInstruction();
            insn.setValue(variable);
            insn.setReceiver(result);
            insn.setTargetType(type);
            pe.addInstruction(insn);
            return pe.var(result, type);
        }
    }

    private ValueEmitter boxPrimitive(ValueType type) {
        if (!(type instanceof ValueType.Object)) {
            throw new EmitException("Can't convert " + this.type + " to " + type);
        }
        String targetClass = ((ValueType.Object) type).getClassName();

        PrimitiveType primitiveType = ((ValueType.Primitive) this.type).getKind();
        String boxClassName = getPrimitiveClassName(primitiveType);
        ValueEmitter result = invokeValueOf(boxClassName);
        if (!pe.hierarchy.isSuperType(targetClass, boxClassName, false)) {
            throw new EmitException("Can't convert " + this.type + " to " + targetClass);
        }
        if (!result.type.equals(type)) {
            result.type = type;
        }
        return result;
    }

    private ValueEmitter invokeValueOf(String cls) {
        return pe.invoke(cls, "valueOf", ValueType.object(cls), this);
    }

    public ValueEmitter cast(NumericOperandType to) {
        if (!(type instanceof ValueType.Primitive)) {
            throw new EmitException("Can't cast non-primitive type: " + type);
        }

        ValueEmitter value = this;
        PrimitiveType kind = ((ValueType.Primitive) type).getKind();
        IntegerSubtype subtype = convertToIntegerSubtype(kind);
        if (subtype != null) {
            value = value.castFromInteger(subtype);
            kind = PrimitiveType.INTEGER;
        }

        ValueEmitter result = pe.newVar(ValueType.INTEGER);
        CastNumberInstruction insn = new CastNumberInstruction(convertToNumeric(kind), to);
        insn.setValue(value.variable);
        insn.setReceiver(result.getVariable());
        pe.addInstruction(insn);

        return result;
    }

    public ValueEmitter castFromInteger(IntegerSubtype subtype) {
        if (type != ValueType.INTEGER) {
            throw new EmitException("Can't cast non-integer value: " + type);
        }

        CastIntegerInstruction insn = new CastIntegerInstruction(subtype, CastIntegerDirection.TO_INTEGER);
        insn.setValue(variable);
        ValueEmitter result = pe.newVar(convertSubtype(subtype));
        insn.setReceiver(result.getVariable());
        pe.addInstruction(insn);
        return result;
    }

    private ValueType convertSubtype(IntegerSubtype subtype) {
        switch (subtype)  {
            case BYTE:
                return ValueType.BYTE;
            case SHORT:
                return ValueType.SHORT;
            case CHAR:
                return ValueType.CHARACTER;
        }
        throw new IllegalArgumentException("Unknown subtype: " + subtype);
    }

    public ValueEmitter castToInteger(IntegerSubtype subtype) {
        switch (subtype) {
            case BYTE:
                if (type != ValueType.BYTE) {
                    throw new EmitException("Can't cast non-byte value: " + type);
                }
                break;
            case SHORT:
                if (type != ValueType.SHORT) {
                    throw new EmitException("Can't cast non-short value: " + type);
                }
                break;
            case CHAR:
                if (type != ValueType.CHARACTER) {
                    throw new EmitException("Can't cast non-char value: " + type);
                }
                break;
        }

        CastIntegerInstruction insn = new CastIntegerInstruction(subtype, CastIntegerDirection.FROM_INTEGER);
        insn.setValue(variable);
        ValueEmitter result = pe.newVar(ValueType.INTEGER);
        insn.setReceiver(result.getVariable());
        pe.addInstruction(insn);
        return result;
    }

    public ValueEmitter widenToInteger() {
        if (!(type instanceof ValueType.Primitive)) {
            throw new EmitException("Can't widen non-primitive: " + type);
        }

        PrimitiveType primitive = ((ValueType.Primitive) type).getKind();
        if (primitive == PrimitiveType.INTEGER) {
            return this;
        }
        IntegerSubtype subtype = convertToIntegerSubtype(primitive);
        if (subtype == null) {
            throw new EmitException("Can't widen to int: " + type);
        }

        return castToInteger(subtype);
    }

    public ValueEmitter assertIs(ValueType type) {
        if (!pe.hierarchy.isSuperType(type, this.type, true)) {
            throw new EmitException("Value type " + this.type + " is not subtype of " + type);
        }
        return this;
    }

    public ValueEmitter assertIs(Class type) {
        return assertIs(ValueType.parse(type));
    }

    public ValueEmitter getElement(ValueEmitter index) {
        if (!(type instanceof ValueType.Array)) {
            throw new EmitException("Can't get element of non-array type: " + type);
        }

        ValueEmitter array = unwrapArray();
        Variable result = pe.getProgram().createVariable();
        ValueType.Array arrayType = (ValueType.Array) array.getType();
        GetElementInstruction insn = new GetElementInstruction(getArrayElementType(arrayType.getItemType()));
        insn.setArray(array.variable);
        insn.setIndex(index.widenToInteger().variable);
        insn.setReceiver(result);
        pe.addInstruction(insn);
        return pe.var(result, ((ValueType.Array) type).getItemType());
    }

    public ValueEmitter getElement(int index) {
        return getElement(pe.constant(index));
    }

    public ProgramEmitter setElement(ValueEmitter index, ValueEmitter value) {
        if (!(type instanceof ValueType.Array)) {
            throw new EmitException("Can't set element of non-array type: " + type);
        }

        PutElementInstruction insn = new PutElementInstruction(getArrayElementType(value.getType()));
        insn.setArray(unwrapArray().variable);
        insn.setIndex(index.widenToInteger().variable);
        insn.setValue(value.variable);
        pe.addInstruction(insn);
        return pe;
    }

    public ProgramEmitter setElement(int index, ValueEmitter value) {
        setElement(pe.constant(index), value);
        return pe;
    }

    private ValueEmitter unwrapArray() {
        ValueType elementType = ((ValueType.Array) type).getItemType();
        Variable result = pe.getProgram().createVariable();
        UnwrapArrayInstruction insn = new UnwrapArrayInstruction(getArrayElementType(elementType));
        insn.setArray(variable);
        insn.setReceiver(result);
        pe.addInstruction(insn);
        return pe.var(result, type);
    }

    public ValueEmitter arrayLength() {
        if (!(type instanceof ValueType.Array)) {
            throw new EmitException("Can't get length of non-array type: " + type);
        }

        Variable result = pe.getProgram().createVariable();
        ArrayLengthInstruction insn = new ArrayLengthInstruction();
        insn.setArray(unwrapArray().variable);
        insn.setReceiver(result);
        pe.addInstruction(insn);
        return pe.var(result, ValueType.INTEGER);
    }

    public ValueEmitter instanceOf(ValueType type) {
        Variable result = pe.getProgram().createVariable();
        IsInstanceInstruction insn = new IsInstanceInstruction();
        insn.setValue(variable);
        insn.setReceiver(result);
        insn.setType(type);
        pe.addInstruction(insn);
        return pe.var(result, ValueType.BOOLEAN);
    }

    public ValueEmitter cloneArray() {
        Variable result = pe.getProgram().createVariable();
        CloneArrayInstruction insn = new CloneArrayInstruction();
        insn.setArray(variable);
        insn.setReceiver(result);
        pe.addInstruction(insn);
        return pe.var(result, type);
    }

    private ArrayElementType getArrayElementType(ValueType type) {
        if (type instanceof ValueType.Primitive) {
            switch (((ValueType.Primitive) type).getKind()) {
                case BOOLEAN:
                case BYTE:
                    return ArrayElementType.BYTE;
                case SHORT:
                    return ArrayElementType.SHORT;
                case CHARACTER:
                    return ArrayElementType.CHAR;
                case INTEGER:
                    return ArrayElementType.INT;
                case LONG:
                    return ArrayElementType.LONG;
                case FLOAT:
                    return ArrayElementType.FLOAT;
                case DOUBLE:
                    return ArrayElementType.DOUBLE;
            }
        }
        return ArrayElementType.OBJECT;
    }

    public ProgramEmitter propagateTo(PhiEmitter phi) {
        Incoming incoming = new Incoming();
        incoming.setValue(variable);
        incoming.setSource(pe.getBlock());
        phi.phi.getIncomings().add(incoming);
        return pe;
    }

    public ValueEmitter box() {
        if (!(type instanceof ValueType.Primitive)) {
            throw new EmitException("Can't box non-primitive: " + type);
        }

        String className = getPrimitiveClassName(((ValueType.Primitive) type).getKind());
        return pe.invoke(className, "valueOf", ValueType.object(className), this);
    }

    private String getPrimitiveClassName(PrimitiveType type) {
        switch (type) {
            case BOOLEAN:
                return "java.lang.Boolean";
            case BYTE:
                return "java.lang.Byte";
            case SHORT:
                return "java.lang.Short";
            case CHARACTER:
                return "java.lang.Character";
            case INTEGER:
                return "java.lang.Integer";
            case LONG:
                return "java.lang.Long";
            case FLOAT:
                return "java.lang.Float";
            case DOUBLE:
                return "java.lang.Double";
            default:
                throw new AssertionError("Unexpected primitive type: " + type);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy