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

org.teavm.cache.AstIO Maven / Gradle / Ivy

/*
 *  Copyright 2014 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.cache;

import java.io.IOException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.teavm.ast.ArrayFromDataExpr;
import org.teavm.ast.ArrayType;
import org.teavm.ast.AssignmentStatement;
import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.AsyncMethodPart;
import org.teavm.ast.BinaryExpr;
import org.teavm.ast.BinaryOperation;
import org.teavm.ast.BlockStatement;
import org.teavm.ast.BoundCheckExpr;
import org.teavm.ast.BreakStatement;
import org.teavm.ast.CastExpr;
import org.teavm.ast.ConditionalExpr;
import org.teavm.ast.ConditionalStatement;
import org.teavm.ast.ConstantExpr;
import org.teavm.ast.ContinueStatement;
import org.teavm.ast.ControlFlowEntry;
import org.teavm.ast.Expr;
import org.teavm.ast.ExprVisitor;
import org.teavm.ast.GotoPartStatement;
import org.teavm.ast.IdentifiedStatement;
import org.teavm.ast.InitClassStatement;
import org.teavm.ast.InstanceOfExpr;
import org.teavm.ast.InvocationExpr;
import org.teavm.ast.InvocationType;
import org.teavm.ast.MonitorEnterStatement;
import org.teavm.ast.MonitorExitStatement;
import org.teavm.ast.NewArrayExpr;
import org.teavm.ast.NewExpr;
import org.teavm.ast.NewMultiArrayExpr;
import org.teavm.ast.OperationType;
import org.teavm.ast.PrimitiveCastExpr;
import org.teavm.ast.QualificationExpr;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.ReturnStatement;
import org.teavm.ast.SequentialStatement;
import org.teavm.ast.Statement;
import org.teavm.ast.StatementVisitor;
import org.teavm.ast.SubscriptExpr;
import org.teavm.ast.SwitchClause;
import org.teavm.ast.SwitchStatement;
import org.teavm.ast.ThrowStatement;
import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.UnaryExpr;
import org.teavm.ast.UnaryOperation;
import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr;
import org.teavm.ast.VariableNode;
import org.teavm.ast.WhileStatement;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference;
import org.teavm.model.InliningInfo;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ReferenceCache;
import org.teavm.model.TextLocation;
import org.teavm.model.ValueType;
import org.teavm.model.util.VariableType;

public class AstIO {
    private static final ElementModifier[] nodeModifiers = ElementModifier.values();
    private static final BinaryOperation[] binaryOperations = BinaryOperation.values();
    private static final UnaryOperation[] unaryOperations = UnaryOperation.values();
    private final SymbolTable symbolTable;
    private final SymbolTable fileTable;
    private final SymbolTable variableTable;
    private final Map statementMap = new HashMap<>();
    private ReferenceCache referenceCache;
    private TextLocation lastWrittenLocation;
    private TextLocation lastReadLocation;
    private InliningInfo lastReadInlining;

    public AstIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable,
            SymbolTable variableTable) {
        this.referenceCache = referenceCache;
        this.symbolTable = symbolTable;
        this.fileTable = fileTable;
        this.variableTable = variableTable;
    }

    public void write(VarDataOutput output, ControlFlowEntry[] cfg) throws IOException {
        lastWrittenLocation = TextLocation.EMPTY;
        output.writeUnsigned(cfg.length);
        for (ControlFlowEntry entry : cfg) {
            writeLocation(output, entry.from);
            output.writeUnsigned(entry.to.length);
            for (TextLocation loc : entry.to) {
                writeLocation(output, loc);
            }
        }
    }

    public void write(VarDataOutput output, RegularMethodNode method) throws IOException {
        output.writeUnsigned(ElementModifier.pack(method.getModifiers()));
        output.writeUnsigned(method.getVariables().size());
        for (VariableNode var : method.getVariables()) {
            write(output, var);
        }
        try {
            method.getBody().acceptVisitor(new NodeWriter(output));
        } catch (IOExceptionWrapper e) {
            throw new IOException("Error writing method body", e.getCause());
        }
    }

    private void write(VarDataOutput output, VariableNode variable) throws IOException {
        output.writeUnsigned(variable.getIndex());
        output.writeUnsigned(variable.getType().ordinal());
        output.writeUnsigned(variable.getName() != null ? variableTable.lookup(variable.getName()) + 1 : 0);
    }

    public ControlFlowEntry[] readControlFlow(VarDataInput input) throws IOException {
        lastReadLocation = TextLocation.EMPTY;
        int size = input.readUnsigned();
        ControlFlowEntry[] result = new ControlFlowEntry[size];
        for (int i = 0; i < size; ++i) {
            TextLocation from = readLocation(input);
            int toSize = input.readUnsigned();
            TextLocation[] to = new TextLocation[toSize];
            for (int j = 0; j < toSize; ++j) {
                to[j] = readLocation(input);
            }
            result[i] = new ControlFlowEntry(from, to);
        }
        return result;
    }

    public RegularMethodNode read(VarDataInput input, MethodReference method) throws IOException {
        RegularMethodNode node = new RegularMethodNode(method);
        node.getModifiers().addAll(unpackModifiers(input.readUnsigned()));
        int varCount = input.readUnsigned();
        for (int i = 0; i < varCount; ++i) {
            node.getVariables().add(readVariable(input));
        }
        lastReadLocation = TextLocation.EMPTY;
        node.setBody(readStatement(input));
        return node;
    }

    private VariableNode readVariable(VarDataInput input) throws IOException {
        int index = input.readUnsigned();
        VariableType type = VariableType.values()[input.readUnsigned()];
        VariableNode variable = new VariableNode(index, type);
        int nameIndex = input.readUnsigned();
        variable.setName(nameIndex != 0 ? variableTable.at(nameIndex - 1) : null);
        return variable;
    }

    public void writeAsync(VarDataOutput output, AsyncMethodNode method) throws IOException {
        output.writeUnsigned(ElementModifier.pack(method.getModifiers()));
        output.writeUnsigned(method.getVariables().size());
        for (VariableNode var : method.getVariables()) {
            write(output, var);
        }
        try {
            output.writeUnsigned(method.getBody().size());
            NodeWriter writer = new NodeWriter(output);
            for (int i = 0; i < method.getBody().size(); ++i) {
                method.getBody().get(i).getStatement().acceptVisitor(writer);
            }
        } catch (IOExceptionWrapper e) {
            throw new IOException("Error writing method body", e.getCause());
        }
    }

    public AsyncMethodNode readAsync(VarDataInput input, MethodReference method) throws IOException {
        AsyncMethodNode node = new AsyncMethodNode(method);
        node.getModifiers().addAll(unpackModifiers(input.readUnsigned()));
        int varCount = input.readUnsigned();
        for (int i = 0; i < varCount; ++i) {
            node.getVariables().add(readVariable(input));
        }
        int partCount = input.readUnsigned();
        lastReadLocation = null;
        for (int i = 0; i < partCount; ++i) {
            AsyncMethodPart part = new AsyncMethodPart();
            part.setStatement(readStatement(input));
            node.getBody().add(part);
        }
        return node;
    }

    private Set unpackModifiers(int packed) {
        EnumSet modifiers = EnumSet.noneOf(ElementModifier.class);
        while (packed != 0) {
            int shift = Integer.numberOfTrailingZeros(packed);
            modifiers.add(nodeModifiers[shift]);
            packed ^= 1 << shift;
        }
        return modifiers;
    }

    private void writeLocation(VarDataOutput output, TextLocation location) throws IOException {
        if (location == null) {
            location = TextLocation.EMPTY;
        }
        if (location.isEmpty()) {
            output.writeUnsigned(0);
        } else if (!lastWrittenLocation.isEmpty() && lastWrittenLocation.getFileName().equals(location.getFileName())) {
            output.writeUnsigned(1);
            output.writeSigned(location.getLine() - lastWrittenLocation.getLine());
        } else {
            output.writeUnsigned(fileTable.lookup(location.getFileName()) + 2);
            output.writeUnsigned(location.getLine());
        }
        lastWrittenLocation = location;
    }

    private class NodeWriter implements ExprVisitor, StatementVisitor {
        private final VarDataOutput output;
        private TextLocation lastLocation = TextLocation.EMPTY;

        NodeWriter(VarDataOutput output) {
            super();
            this.output = output;
        }

        void writeExpr(Expr expr) throws IOException {
            writeLocation(expr.getLocation());
            expr.acceptVisitor(this);
        }

        private void writeLocation(TextLocation location) throws IOException {
            if (location == null) {
                location = TextLocation.EMPTY;
            }
            if (Objects.equals(location, lastLocation)) {
                return;
            }

            String fileName = lastLocation.getFileName();
            int lineNumber = lastLocation.getLine();

            if (location.getInlining() != lastLocation.getInlining()) {
                InliningInfo lastCommonInlining = null;
                InliningInfo[] prevPath = lastLocation.getInliningPath();
                InliningInfo[] newPath = location.getInliningPath();
                int pathIndex = 0;
                while (pathIndex < prevPath.length && pathIndex < newPath.length
                        && prevPath[pathIndex].equals(newPath[pathIndex])) {
                    lastCommonInlining = prevPath[pathIndex++];
                }

                InliningInfo prevInlining = location.getInlining();
                while (prevInlining != lastCommonInlining) {
                    output.writeUnsigned(123);
                    fileName = prevInlining.getFileName();
                    lineNumber = prevInlining.getLine();
                    prevInlining = prevInlining.getParent();
                }

                while (pathIndex < newPath.length) {
                    InliningInfo inlining = newPath[pathIndex++];
                    writeSimpleLocation(fileName, lineNumber, inlining.getFileName(), inlining.getLine());
                    fileName = null;
                    lineNumber = -1;

                    output.writeUnsigned(124);
                    MethodReference method = inlining.getMethod();
                    output.writeUnsigned(symbolTable.lookup(method.getClassName()));
                    output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
                }
            }

            writeSimpleLocation(fileName, lineNumber, location.getFileName(), location.getLine());

            lastLocation = location;
        }

        private void writeSimpleLocation(String fileName, int lineNumber, String newFileName, int newLineNumber)
                throws IOException {
            if (Objects.equals(fileName, newFileName) && lineNumber == newLineNumber) {
                return;
            }

            if (newFileName == null) {
                output.writeUnsigned(127);
            } else if (fileName != null && fileName.equals(newFileName)) {
                output.writeUnsigned(126);
                output.writeSigned(newLineNumber - lineNumber);
            } else {
                output.writeUnsigned(125);
                output.writeUnsigned(fileTable.lookup(newFileName));
                output.writeUnsigned(newLineNumber);
            }
        }

        private void writeSequence(List sequence) throws IOException {
            output.writeUnsigned(sequence.size());
            for (Statement part : sequence) {
                part.acceptVisitor(this);
            }
        }

        @Override
        public void visit(AssignmentStatement statement) {
            try {
                writeLocation(statement.getLocation());
                output.writeUnsigned(statement.getLeftValue() != null ? 0 : 1);
                if (statement.getLeftValue() != null) {
                    writeExpr(statement.getLeftValue());
                }
                writeExpr(statement.getRightValue());
                output.writeUnsigned(statement.isAsync() ? 1 : 0);
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(SequentialStatement statement) {
            try {
                output.writeUnsigned(2);
                writeSequence(statement.getSequence());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(ConditionalStatement statement) {
            try {
                output.writeUnsigned(3);
                writeExpr(statement.getCondition());
                writeSequence(statement.getConsequent());
                writeSequence(statement.getAlternative());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(SwitchStatement statement) {
            try {
                output.writeUnsigned(4);
                output.write(statement.getId());
                writeExpr(statement.getValue());
                output.writeUnsigned(statement.getClauses().size());
                for (SwitchClause clause : statement.getClauses()) {
                    int[] conditions = clause.getConditions();
                    output.writeUnsigned(conditions.length);
                    for (int condition : conditions) {
                        output.writeSigned(condition);
                    }
                    writeSequence(clause.getBody());
                }
                writeSequence(statement.getDefaultClause());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(WhileStatement statement) {
            try {
                output.writeUnsigned(statement.getCondition() != null ? 5 : 6);
                output.write(statement.getId());
                if (statement.getCondition() != null) {
                    writeExpr(statement.getCondition());
                }
                writeSequence(statement.getBody());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(BlockStatement statement) {
            try {
                output.writeUnsigned(7);
                output.write(statement.getId());
                writeSequence(statement.getBody());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(BreakStatement statement) {
            try {
                writeLocation(statement.getLocation());
                output.writeUnsigned(statement.getTarget() != null && statement.getTarget().getId() != null ? 8 : 9);
                if (statement.getTarget() != null && statement.getTarget().getId() != null) {
                    output.write(statement.getTarget().getId());
                }
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(ContinueStatement statement) {
            try {
                writeLocation(statement.getLocation());
                output.writeUnsigned(statement.getTarget() != null && statement.getTarget().getId() != null ? 10 : 11);
                if (statement.getTarget() != null && statement.getTarget().getId() != null) {
                    output.write(statement.getTarget().getId());
                }
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(ReturnStatement statement) {
            try {
                writeLocation(statement.getLocation());
                output.writeUnsigned(statement.getResult() != null ? 12 : 13);
                if (statement.getResult() != null) {
                    writeExpr(statement.getResult());
                }
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(ThrowStatement statement) {
            try {
                writeLocation(statement.getLocation());
                output.writeUnsigned(14);
                writeExpr(statement.getException());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(InitClassStatement statement) {
            try {
                writeLocation(statement.getLocation());
                output.writeUnsigned(15);
                output.writeUnsigned(symbolTable.lookup(statement.getClassName()));
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(TryCatchStatement statement) {
            try {
                output.writeUnsigned(16);
                writeSequence(statement.getProtectedBody());
                output.writeUnsigned(statement.getExceptionType() != null
                        ? symbolTable.lookup(statement.getExceptionType()) + 1 : 0);
                output.writeUnsigned(statement.getExceptionVariable() != null
                        ? statement.getExceptionVariable() + 1 : 0);
                writeSequence(statement.getHandler());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(GotoPartStatement statement) {
            try {
                output.writeUnsigned(17);
                output.writeUnsigned(statement.getPart());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(MonitorEnterStatement statement) {
            try {
                writeLocation(statement.getLocation());
                output.writeUnsigned(18);
                writeExpr(statement.getObjectRef());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(MonitorExitStatement statement) {
            try {
                writeLocation(statement.getLocation());
                output.writeUnsigned(19);
                writeExpr(statement.getObjectRef());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(BinaryExpr expr) {
            try {
                output.writeUnsigned(0);
                output.writeUnsigned(expr.getOperation().ordinal());
                output.writeUnsigned(expr.getType() != null ? expr.getType().ordinal() + 1 : 0);
                writeExpr(expr.getFirstOperand());
                writeExpr(expr.getSecondOperand());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(UnaryExpr expr) {
            try {
                output.writeUnsigned(1);
                output.writeUnsigned(expr.getOperation().ordinal());
                output.writeUnsigned(expr.getType() != null ? expr.getType().ordinal() + 1 : 0);
                writeExpr(expr.getOperand());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(ConditionalExpr expr) {
            try {
                output.writeUnsigned(2);
                writeExpr(expr.getCondition());
                writeExpr(expr.getConsequent());
                writeExpr(expr.getAlternative());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(ConstantExpr expr) {
            try {
                Object value = expr.getValue();
                if (value == null) {
                    output.writeUnsigned(3);
                } else if (value instanceof Integer) {
                    output.writeUnsigned(4);
                    output.writeSigned((Integer) value);
                } else if (value instanceof Long) {
                    output.writeUnsigned(5);
                    output.writeSigned((Long) value);
                } else if (value instanceof Float) {
                    output.writeUnsigned(6);
                    output.writeFloat((Float) value);
                } else if (value instanceof Double) {
                    output.writeUnsigned(7);
                    output.writeDouble((Double) value);
                } else if (value instanceof String) {
                    output.writeUnsigned(8);
                    output.write((String) value);
                } else if (value instanceof ValueType) {
                    output.writeUnsigned(9);
                    output.writeUnsigned(symbolTable.lookup(value.toString()));
                }
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(VariableExpr expr) {
            try {
                output.writeUnsigned(10);
                output.writeUnsigned(expr.getIndex());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(SubscriptExpr expr) {
            try {
                output.writeUnsigned(11);
                writeExpr(expr.getArray());
                writeExpr(expr.getIndex());
                output.writeUnsigned(expr.getType().ordinal());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(UnwrapArrayExpr expr) {
            try {
                output.writeUnsigned(12);
                output.writeUnsigned(expr.getElementType().ordinal());
                writeExpr(expr.getArray());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(InvocationExpr expr) {
            try {
                switch (expr.getType()) {
                    case CONSTRUCTOR:
                        output.writeUnsigned(13);
                        break;
                    case STATIC:
                        output.writeUnsigned(14);
                        break;
                    case SPECIAL:
                        output.writeUnsigned(15);
                        break;
                    case DYNAMIC:
                        output.writeUnsigned(16);
                        break;
                }
                output.writeUnsigned(symbolTable.lookup(expr.getMethod().getClassName()));
                output.writeUnsigned(symbolTable.lookup(expr.getMethod().getDescriptor().toString()));
                output.writeUnsigned(expr.getArguments().size());
                for (int i = 0; i < expr.getArguments().size(); ++i) {
                    writeExpr(expr.getArguments().get(i));
                }
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(QualificationExpr expr) {
            try {
                output.writeUnsigned(expr.getQualified() == null ? 17 : 18);
                if (expr.getQualified() != null) {
                    writeExpr(expr.getQualified());
                }
                output.writeUnsigned(symbolTable.lookup(expr.getField().getClassName()));
                output.writeUnsigned(symbolTable.lookup(expr.getField().getFieldName()));
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(NewExpr expr) {
            try {
                output.writeUnsigned(19);
                output.writeUnsigned(symbolTable.lookup(expr.getConstructedClass()));
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(NewArrayExpr expr) {
            try {
                output.writeUnsigned(20);
                writeExpr(expr.getLength());
                output.writeUnsigned(symbolTable.lookup(expr.getType().toString()));
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(NewMultiArrayExpr expr) {
            try {
                output.writeUnsigned(21);
                output.writeUnsigned(expr.getDimensions().size());
                for (Expr dimension : expr.getDimensions()) {
                    writeExpr(dimension);
                }
                output.writeUnsigned(symbolTable.lookup(expr.getType().toString()));
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(ArrayFromDataExpr expr) {
            try {
                output.writeUnsigned(28);
                output.writeUnsigned(symbolTable.lookup(expr.getType().toString()));
                output.writeUnsigned(expr.getData().size());
                for (Expr element : expr.getData()) {
                    writeExpr(element);
                }
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(InstanceOfExpr expr) {
            try {
                output.writeUnsigned(22);
                writeExpr(expr.getExpr());
                output.writeUnsigned(symbolTable.lookup(expr.getType().toString()));
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(CastExpr expr) {
            try {
                output.writeUnsigned(23);
                output.writeUnsigned(symbolTable.lookup(expr.getTarget().toString()));
                writeExpr(expr.getValue());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(PrimitiveCastExpr expr) {
            try {
                output.writeUnsigned(24);
                output.writeUnsigned(expr.getSource().ordinal());
                output.writeUnsigned(expr.getTarget().ordinal());
                writeExpr(expr.getValue());
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }

        @Override
        public void visit(BoundCheckExpr expr) {
            try {
                output.writeUnsigned(expr.getArray() == null ? 27 : !expr.isLower() ? 26 : 25);
                writeExpr(expr.getIndex());
                if (expr.getArray() != null) {
                    writeExpr(expr.getArray());
                }
            } catch (IOException e) {
                throw new IOExceptionWrapper(e);
            }
        }
    }

    private TextLocation readLocation(VarDataInput input) throws IOException {
        int fileIndex = input.readUnsigned();
        if (fileIndex == 0) {
            lastReadLocation = null;
        } else if (fileIndex == 1) {
            lastReadLocation = new TextLocation(lastReadLocation.getFileName(),
                    lastReadLocation.getLine() + input.readSigned());
        } else {
            lastReadLocation = new TextLocation(fileTable.at(fileIndex - 2), input.readUnsigned());
            return lastReadLocation;
        }
        return lastReadLocation;
    }

    private int readNodeLocation(int type, VarDataInput input) throws IOException {
        switch (type) {
            case 127:
                lastReadLocation = null;
                break;
            case 126:
                lastReadLocation = new TextLocation(lastReadLocation.getFileName(),
                        lastReadLocation.getLine() + input.readSigned(), lastReadInlining);
                break;
            case 125:
                lastReadLocation = new TextLocation(fileTable.at(input.readUnsigned()), input.readUnsigned(),
                        lastReadInlining);
                break;
            case 124: {
                String className = symbolTable.at(input.readUnsigned());
                MethodDescriptor methodDescriptor = MethodDescriptor.parse(symbolTable.at(input.readUnsigned()));
                methodDescriptor = referenceCache.getCached(methodDescriptor);
                lastReadInlining = new InliningInfo(referenceCache.getCached(className, methodDescriptor),
                        lastReadLocation.getFileName(), lastReadLocation.getLine(), lastReadInlining);
                lastReadLocation = new TextLocation(null, -1, lastReadInlining);
                break;
            }
            case 123:
                lastReadLocation = new TextLocation(lastReadInlining.getFileName(), lastReadInlining.getLine());
                lastReadInlining = lastReadInlining.getParent();
                break;
            default:
                return type;
        }
        return input.readUnsigned();
    }

    private Statement readStatement(VarDataInput input) throws IOException {
        int type = readNodeLocation(input.readUnsigned(), input);
        switch (type) {
            case 0: {
                AssignmentStatement stmt = new AssignmentStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setLeftValue(readExpr(input));
                stmt.setRightValue(readExpr(input));
                stmt.setAsync(input.readUnsigned() != 0);
                return stmt;
            }
            case 1: {
                AssignmentStatement stmt = new AssignmentStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setRightValue(readExpr(input));
                stmt.setAsync(input.readUnsigned() != 0);
                return stmt;
            }
            case 2: {
                SequentialStatement stmt = new SequentialStatement();
                readSequence(input, stmt.getSequence());
                return stmt;
            }
            case 3: {
                ConditionalStatement stmt = new ConditionalStatement();
                stmt.setCondition(readExpr(input));
                readSequence(input, stmt.getConsequent());
                readSequence(input, stmt.getAlternative());
                return stmt;
            }
            case 4: {
                SwitchStatement stmt = new SwitchStatement();
                stmt.setId(input.read());
                stmt.setValue(readExpr(input));
                int clauseCount = input.readUnsigned();
                for (int i = 0; i < clauseCount; ++i) {
                    SwitchClause clause = new SwitchClause();
                    int conditionCount = input.readUnsigned();
                    int[] conditions = new int[conditionCount];
                    for (int j = 0; j < conditionCount; ++j) {
                        conditions[j] = input.readSigned();
                    }
                    clause.setConditions(conditions);
                    readSequence(input, clause.getBody());
                    stmt.getClauses().add(clause);
                }
                readSequence(input, stmt.getDefaultClause());
                return stmt;
            }
            case 5: {
                WhileStatement stmt = new WhileStatement();
                stmt.setId(input.read());
                stmt.setCondition(readExpr(input));
                if (stmt.getId() != null) {
                    statementMap.put(stmt.getId(), stmt);
                }
                readSequence(input, stmt.getBody());
                return stmt;
            }
            case 6: {
                WhileStatement stmt = new WhileStatement();
                stmt.setId(input.read());
                if (stmt.getId() != null) {
                    statementMap.put(stmt.getId(), stmt);
                }
                readSequence(input, stmt.getBody());
                return stmt;
            }
            case 7: {
                BlockStatement stmt = new BlockStatement();
                stmt.setId(input.read());
                if (stmt.getId() != null) {
                    statementMap.put(stmt.getId(), stmt);
                }
                readSequence(input, stmt.getBody());
                return stmt;
            }
            case 8: {
                BreakStatement stmt = new BreakStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setTarget(statementMap.get(input.read()));
                return stmt;
            }
            case 9: {
                BreakStatement stmt = new BreakStatement();
                stmt.setLocation(lastReadLocation);
                return stmt;
            }
            case 10: {
                ContinueStatement stmt = new ContinueStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setTarget(statementMap.get(input.read()));
                return stmt;
            }
            case 11: {
                ContinueStatement stmt = new ContinueStatement();
                stmt.setLocation(lastReadLocation);
                return stmt;
            }
            case 12: {
                ReturnStatement stmt = new ReturnStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setResult(readExpr(input));
                return stmt;
            }
            case 13: {
                ReturnStatement stmt = new ReturnStatement();
                stmt.setLocation(lastReadLocation);
                return stmt;
            }
            case 14: {
                ThrowStatement stmt = new ThrowStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setException(readExpr(input));
                return stmt;
            }
            case 15: {
                InitClassStatement stmt = new InitClassStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setClassName(symbolTable.at(input.readUnsigned()));
                return stmt;
            }
            case 16: {
                TryCatchStatement stmt = new TryCatchStatement();
                readSequence(input, stmt.getProtectedBody());
                int exceptionTypeIndex = input.readUnsigned();
                if (exceptionTypeIndex > 0) {
                    stmt.setExceptionType(symbolTable.at(exceptionTypeIndex - 1));
                }
                int exceptionVarIndex = input.readUnsigned();
                if (exceptionVarIndex > 0) {
                    stmt.setExceptionVariable(exceptionVarIndex - 1);
                }
                readSequence(input, stmt.getHandler());
                return stmt;
            }
            case 17: {
                GotoPartStatement stmt = new GotoPartStatement();
                stmt.setPart(input.readUnsigned());
                return stmt;
            }
            case 18: {
                MonitorEnterStatement stmt = new MonitorEnterStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setObjectRef(readExpr(input));
                return stmt;
            }
            case 19: {
                MonitorExitStatement stmt = new MonitorExitStatement();
                stmt.setLocation(lastReadLocation);
                stmt.setObjectRef(readExpr(input));
                return stmt;
            }
            default:
                throw new RuntimeException("Unexpected statement type: " + type);
        }
    }

    private void readSequence(VarDataInput input, List statements) throws IOException {
        int count = input.readUnsigned();
        for (int i = 0; i < count; ++i) {
            statements.add(readStatement(input));
        }
    }

    private Expr readExpr(VarDataInput input) throws IOException {
        int type = readNodeLocation(input.readUnsigned(), input);
        switch (type) {
            case 0: {
                BinaryExpr expr = new BinaryExpr();
                expr.setLocation(lastReadLocation);
                expr.setOperation(binaryOperations[input.readUnsigned()]);
                int valueType = input.readUnsigned();
                expr.setType(valueType > 0 ? OperationType.values()[valueType - 1] : null);
                expr.setFirstOperand(readExpr(input));
                expr.setSecondOperand(readExpr(input));
                return expr;
            }
            case 1: {
                UnaryExpr expr = new UnaryExpr();
                expr.setLocation(lastReadLocation);
                expr.setOperation(unaryOperations[input.readUnsigned()]);
                int valueType = input.readUnsigned();
                expr.setType(valueType > 0 ? OperationType.values()[valueType - 1] : null);
                expr.setOperand(readExpr(input));
                return expr;
            }
            case 2: {
                ConditionalExpr expr = new ConditionalExpr();
                expr.setLocation(lastReadLocation);
                expr.setCondition(readExpr(input));
                expr.setConsequent(readExpr(input));
                expr.setAlternative(readExpr(input));
                return expr;
            }
            case 3: {
                ConstantExpr expr = new ConstantExpr();
                expr.setLocation(lastReadLocation);
                return expr;
            }
            case 4: {
                ConstantExpr expr = new ConstantExpr();
                expr.setLocation(lastReadLocation);
                expr.setValue(input.readSigned());
                return expr;
            }
            case 5: {
                ConstantExpr expr = new ConstantExpr();
                expr.setValue(input.readSignedLong());
                return expr;
            }
            case 6: {
                ConstantExpr expr = new ConstantExpr();
                expr.setLocation(lastReadLocation);
                expr.setValue(input.readFloat());
                return expr;
            }
            case 7: {
                ConstantExpr expr = new ConstantExpr();
                expr.setLocation(lastReadLocation);
                expr.setValue(input.readDouble());
                return expr;
            }
            case 8: {
                ConstantExpr expr = new ConstantExpr();
                expr.setLocation(lastReadLocation);
                expr.setValue(input.read());
                return expr;
            }
            case 9: {
                ConstantExpr expr = new ConstantExpr();
                expr.setLocation(lastReadLocation);
                expr.setValue(ValueType.parse(symbolTable.at(input.readUnsigned())));
                return expr;
            }
            case 10: {
                VariableExpr expr = new VariableExpr();
                expr.setLocation(lastReadLocation);
                expr.setIndex(input.readUnsigned());
                return expr;
            }
            case 11: {
                SubscriptExpr expr = new SubscriptExpr();
                expr.setLocation(lastReadLocation);
                expr.setArray(readExpr(input));
                expr.setIndex(readExpr(input));
                expr.setType(ArrayType.values()[input.readUnsigned()]);
                return expr;
            }
            case 12: {
                UnwrapArrayExpr expr = new UnwrapArrayExpr(ArrayType.values()[input.readUnsigned()]);
                expr.setLocation(lastReadLocation);
                expr.setArray(readExpr(input));
                return expr;
            }
            case 13:
                return parseInvocationExpr(InvocationType.CONSTRUCTOR, input);
            case 14:
                return parseInvocationExpr(InvocationType.STATIC, input);
            case 15:
                return parseInvocationExpr(InvocationType.SPECIAL, input);
            case 16:
                return parseInvocationExpr(InvocationType.DYNAMIC, input);
            case 17: {
                QualificationExpr expr = new QualificationExpr();
                expr.setLocation(lastReadLocation);
                String className = symbolTable.at(input.readUnsigned());
                String fieldName = symbolTable.at(input.readUnsigned());
                expr.setField(new FieldReference(className, fieldName));
                return expr;
            }
            case 18: {
                QualificationExpr expr = new QualificationExpr();
                expr.setLocation(lastReadLocation);
                expr.setQualified(readExpr(input));
                String className = symbolTable.at(input.readUnsigned());
                String fieldName = symbolTable.at(input.readUnsigned());
                expr.setField(new FieldReference(className, fieldName));
                return expr;
            }
            case 19: {
                NewExpr expr = new NewExpr();
                expr.setLocation(lastReadLocation);
                expr.setConstructedClass(symbolTable.at(input.readUnsigned()));
                return expr;
            }
            case 20: {
                NewArrayExpr expr = new NewArrayExpr();
                expr.setLocation(lastReadLocation);
                expr.setLength(readExpr(input));
                expr.setType(ValueType.parse(symbolTable.at(input.readUnsigned())));
                return expr;
            }
            case 21: {
                NewMultiArrayExpr expr = new NewMultiArrayExpr();
                expr.setLocation(lastReadLocation);
                int dimensionCount = input.readUnsigned();
                for (int i = 0; i < dimensionCount; ++i) {
                    expr.getDimensions().add(readExpr(input));
                }
                expr.setType(ValueType.parse(symbolTable.at(input.readUnsigned())));
                return expr;
            }
            case 22: {
                InstanceOfExpr expr = new InstanceOfExpr();
                expr.setLocation(lastReadLocation);
                expr.setExpr(readExpr(input));
                expr.setType(ValueType.parse(symbolTable.at(input.readUnsigned())));
                return expr;
            }
            case 23: {
                CastExpr expr = new CastExpr();
                expr.setLocation(lastReadLocation);
                expr.setTarget(ValueType.parse(symbolTable.at(input.readUnsigned())));
                expr.setValue(readExpr(input));
                return expr;
            }
            case 24: {
                PrimitiveCastExpr expr = new PrimitiveCastExpr();
                expr.setLocation(lastReadLocation);
                expr.setSource(OperationType.values()[input.readUnsigned()]);
                expr.setTarget(OperationType.values()[input.readUnsigned()]);
                expr.setValue(readExpr(input));
                return expr;
            }
            case 25:
            case 26: {
                BoundCheckExpr expr = new BoundCheckExpr();
                expr.setLocation(lastReadLocation);
                expr.setIndex(readExpr(input));
                expr.setArray(readExpr(input));
                expr.setLower(type == 25);
                return expr;
            }
            case 27: {
                BoundCheckExpr expr = new BoundCheckExpr();
                expr.setLocation(lastReadLocation);
                expr.setIndex(readExpr(input));
                expr.setLower(true);
                return expr;
            }
            case 28: {
                ArrayFromDataExpr expr = new ArrayFromDataExpr();
                expr.setType(ValueType.parse(symbolTable.at(input.readUnsigned())));
                int count = input.readUnsigned();
                for (int i = 0; i < count; ++i) {
                    expr.getData().add(readExpr(input));
                }
                return expr;
            }
            default:
                throw new RuntimeException("Unknown expression type: " + type);
        }
    }

    private InvocationExpr parseInvocationExpr(InvocationType invocationType, VarDataInput input) throws IOException {
        InvocationExpr expr = new InvocationExpr();
        expr.setLocation(lastReadLocation);
        expr.setType(invocationType);
        String className = symbolTable.at(input.readUnsigned());
        String signature = symbolTable.at(input.readUnsigned());
        MethodReference methodRef = referenceCache.getCached(className, MethodDescriptor.parse(signature));
        expr.setMethod(methodRef);
        int argCount = input.readUnsigned();
        for (int i = 0; i < argCount; ++i) {
            expr.getArguments().add(readExpr(input));
        }
        return expr;
    }

    private static class IOExceptionWrapper extends RuntimeException {
        private static final long serialVersionUID = -7566355431593608333L;

        IOExceptionWrapper(Throwable cause) {
            super(cause);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy