diff --git a/core/src/main/java/org/teavm/cache/AstIO.java b/core/src/main/java/org/teavm/cache/AstIO.java index 53e9429b7..214aeeda5 100644 --- a/core/src/main/java/org/teavm/cache/AstIO.java +++ b/core/src/main/java/org/teavm/cache/AstIO.java @@ -15,8 +15,6 @@ */ package org.teavm.cache; -import java.io.DataInput; -import java.io.DataOutput; import java.io.IOException; import java.util.EnumSet; import java.util.HashMap; @@ -73,6 +71,7 @@ import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; 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; @@ -84,34 +83,27 @@ public class AstIO { private final SymbolTable symbolTable; private final SymbolTable fileTable; private final Map statementMap = new HashMap<>(); - private Map methodDescriptorCache = new HashMap<>(); - private Map> methodReferenceCache = new HashMap<>(); + private ReferenceCache referenceCache = new ReferenceCache(); public AstIO(SymbolTable symbolTable, SymbolTable fileTable) { this.symbolTable = symbolTable; this.fileTable = fileTable; } - public void write(DataOutput output, ControlFlowEntry[] cfg) throws IOException { - output.writeShort(cfg.length); + public void write(VarDataOutput output, ControlFlowEntry[] cfg) throws IOException { + output.writeUnsigned(cfg.length); for (ControlFlowEntry entry : cfg) { writeLocation(output, entry.from); - output.writeShort(entry.to.length); + output.writeUnsigned(entry.to.length); for (TextLocation loc : entry.to) { - if (loc != null) { - output.writeByte(1); - writeLocation(output, loc); - } else { - output.writeByte(0); - } - + writeLocation(output, loc); } } } - public void write(DataOutput output, RegularMethodNode method) throws IOException { - output.writeInt(ElementModifier.pack(method.getModifiers())); - output.writeShort(method.getVariables().size()); + 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); } @@ -122,33 +114,31 @@ public class AstIO { } } - private void write(DataOutput output, VariableNode variable) throws IOException { - output.writeShort(variable.getIndex()); - output.writeByte(variable.getType().ordinal()); - output.writeUTF(variable.getName() != null ? variable.getName() : ""); + private void write(VarDataOutput output, VariableNode variable) throws IOException { + output.writeUnsigned(variable.getIndex()); + output.writeUnsigned(variable.getType().ordinal()); + output.write(variable.getName()); } - public ControlFlowEntry[] readControlFlow(DataInput input) throws IOException { - short size = input.readShort(); + public ControlFlowEntry[] readControlFlow(VarDataInput input) throws IOException { + int size = input.readUnsigned(); ControlFlowEntry[] result = new ControlFlowEntry[size]; for (int i = 0; i < size; ++i) { TextLocation from = readLocation(input); - int toSize = input.readShort(); + int toSize = input.readUnsigned(); TextLocation[] to = new TextLocation[toSize]; for (int j = 0; j < toSize; ++j) { - if (input.readByte() != 0) { - to[j] = readLocation(input); - } + to[j] = readLocation(input); } result[i] = new ControlFlowEntry(from, to); } return result; } - public RegularMethodNode read(DataInput input, MethodReference method) throws IOException { + public RegularMethodNode read(VarDataInput input, MethodReference method) throws IOException { RegularMethodNode node = new RegularMethodNode(method); - node.getModifiers().addAll(unpackModifiers(input.readInt())); - int varCount = input.readShort(); + node.getModifiers().addAll(unpackModifiers(input.readUnsigned())); + int varCount = input.readUnsigned(); for (int i = 0; i < varCount; ++i) { node.getVariables().add(readVariable(input)); } @@ -156,28 +146,22 @@ public class AstIO { return node; } - private VariableNode readVariable(DataInput input) throws IOException { - int index = input.readShort(); - VariableType type = VariableType.values()[input.readByte()]; + private VariableNode readVariable(VarDataInput input) throws IOException { + int index = input.readUnsigned(); + VariableType type = VariableType.values()[input.readUnsigned()]; VariableNode variable = new VariableNode(index, type); - int nameCount = input.readByte(); - for (int i = 0; i < nameCount; ++i) { - variable.setName(input.readUTF()); - if (variable.getName().isEmpty()) { - variable.setName(null); - } - } + variable.setName(input.read()); return variable; } - public void writeAsync(DataOutput output, AsyncMethodNode method) throws IOException { - output.writeInt(ElementModifier.pack(method.getModifiers())); - output.writeShort(method.getVariables().size()); + 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.writeShort(method.getBody().size()); + output.writeUnsigned(method.getBody().size()); for (int i = 0; i < method.getBody().size(); ++i) { method.getBody().get(i).getStatement().acceptVisitor(new NodeWriter(output)); } @@ -186,14 +170,14 @@ public class AstIO { } } - public AsyncMethodNode readAsync(DataInput input, MethodReference method) throws IOException { + public AsyncMethodNode readAsync(VarDataInput input, MethodReference method) throws IOException { AsyncMethodNode node = new AsyncMethodNode(method); - node.getModifiers().addAll(unpackModifiers(input.readInt())); - int varCount = input.readShort(); + 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.readShort(); + int partCount = input.readUnsigned(); for (int i = 0; i < partCount; ++i) { AsyncMethodPart part = new AsyncMethodPart(); part.setStatement(readStatement(input)); @@ -212,19 +196,19 @@ public class AstIO { return modifiers; } - private void writeLocation(DataOutput output, TextLocation location) throws IOException { + private void writeLocation(VarDataOutput output, TextLocation location) throws IOException { if (location == null || location.getFileName() == null) { - output.writeShort(-1); + output.writeUnsigned(0); } else { - output.writeShort(fileTable.lookup(location.getFileName())); - output.writeShort(location.getLine()); + output.writeUnsigned(fileTable.lookup(location.getFileName()) + 1); + output.writeUnsigned(location.getLine()); } } private class NodeWriter implements ExprVisitor, StatementVisitor { - private final DataOutput output; + private final VarDataOutput output; - NodeWriter(DataOutput output) { + NodeWriter(VarDataOutput output) { super(); this.output = output; } @@ -239,31 +223,22 @@ public class AstIO { } private void writeSequence(List sequence) throws IOException { - output.writeShort(sequence.size()); + output.writeUnsigned(sequence.size()); for (Statement part : sequence) { part.acceptVisitor(this); } } - private void writeNullableString(String str) throws IOException { - if (str == null) { - output.writeBoolean(false); - } else { - output.writeBoolean(true); - output.writeUTF(str); - } - } - @Override public void visit(AssignmentStatement statement) { try { - output.writeByte(statement.getLeftValue() != null ? 0 : 1); + output.writeUnsigned(statement.getLeftValue() != null ? 0 : 1); writeLocation(statement.getLocation()); if (statement.getLeftValue() != null) { writeExpr(statement.getLeftValue()); } writeExpr(statement.getRightValue()); - output.writeBoolean(statement.isAsync()); + output.writeUnsigned(statement.isAsync() ? 1 : 0); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -272,7 +247,7 @@ public class AstIO { @Override public void visit(SequentialStatement statement) { try { - output.writeByte(2); + output.writeUnsigned(2); writeSequence(statement.getSequence()); } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -282,7 +257,7 @@ public class AstIO { @Override public void visit(ConditionalStatement statement) { try { - output.writeByte(3); + output.writeUnsigned(3); writeExpr(statement.getCondition()); writeSequence(statement.getConsequent()); writeSequence(statement.getAlternative()); @@ -294,15 +269,15 @@ public class AstIO { @Override public void visit(SwitchStatement statement) { try { - output.writeByte(4); - writeNullableString(statement.getId()); + output.writeUnsigned(4); + output.write(statement.getId()); writeExpr(statement.getValue()); - output.writeShort(statement.getClauses().size()); + output.writeUnsigned(statement.getClauses().size()); for (SwitchClause clause : statement.getClauses()) { int[] conditions = clause.getConditions(); - output.writeShort(conditions.length); + output.writeUnsigned(conditions.length); for (int condition : conditions) { - output.writeInt(condition); + output.writeSigned(condition); } writeSequence(clause.getBody()); } @@ -315,8 +290,8 @@ public class AstIO { @Override public void visit(WhileStatement statement) { try { - output.writeByte(statement.getCondition() != null ? 5 : 6); - writeNullableString(statement.getId()); + output.writeUnsigned(statement.getCondition() != null ? 5 : 6); + output.write(statement.getId()); if (statement.getCondition() != null) { writeExpr(statement.getCondition()); } @@ -329,8 +304,8 @@ public class AstIO { @Override public void visit(BlockStatement statement) { try { - output.writeByte(7); - writeNullableString(statement.getId()); + output.writeUnsigned(7); + output.write(statement.getId()); writeSequence(statement.getBody()); } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -340,10 +315,10 @@ public class AstIO { @Override public void visit(BreakStatement statement) { try { - output.writeByte(statement.getTarget() != null && statement.getTarget().getId() != null ? 8 : 9); + output.writeUnsigned(statement.getTarget() != null && statement.getTarget().getId() != null ? 8 : 9); writeLocation(statement.getLocation()); if (statement.getTarget() != null && statement.getTarget().getId() != null) { - output.writeUTF(statement.getTarget().getId()); + output.write(statement.getTarget().getId()); } } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -353,10 +328,10 @@ public class AstIO { @Override public void visit(ContinueStatement statement) { try { - output.writeByte(statement.getTarget() != null && statement.getTarget().getId() != null ? 10 : 11); + output.writeUnsigned(statement.getTarget() != null && statement.getTarget().getId() != null ? 10 : 11); writeLocation(statement.getLocation()); if (statement.getTarget() != null && statement.getTarget().getId() != null) { - output.writeUTF(statement.getTarget().getId()); + output.write(statement.getTarget().getId()); } } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -366,7 +341,7 @@ public class AstIO { @Override public void visit(ReturnStatement statement) { try { - output.writeByte(statement.getResult() != null ? 12 : 13); + output.writeUnsigned(statement.getResult() != null ? 12 : 13); writeLocation(statement.getLocation()); if (statement.getResult() != null) { writeExpr(statement.getResult()); @@ -379,7 +354,7 @@ public class AstIO { @Override public void visit(ThrowStatement statement) { try { - output.writeByte(14); + output.writeUnsigned(14); writeLocation(statement.getLocation()); writeExpr(statement.getException()); } catch (IOException e) { @@ -390,9 +365,9 @@ public class AstIO { @Override public void visit(InitClassStatement statement) { try { - output.writeByte(15); + output.writeUnsigned(15); writeLocation(statement.getLocation()); - output.writeInt(symbolTable.lookup(statement.getClassName())); + output.writeUnsigned(symbolTable.lookup(statement.getClassName())); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -401,12 +376,12 @@ public class AstIO { @Override public void visit(TryCatchStatement statement) { try { - output.writeByte(16); + output.writeUnsigned(16); writeSequence(statement.getProtectedBody()); - output.writeInt(statement.getExceptionType() != null - ? symbolTable.lookup(statement.getExceptionType()) : -1); - output.writeShort(statement.getExceptionVariable() != null - ? statement.getExceptionVariable() : -1); + 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); @@ -416,8 +391,8 @@ public class AstIO { @Override public void visit(GotoPartStatement statement) { try { - output.writeByte(17); - output.writeShort(statement.getPart()); + output.writeUnsigned(17); + output.writeUnsigned(statement.getPart()); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -426,7 +401,7 @@ public class AstIO { @Override public void visit(MonitorEnterStatement statement) { try { - output.writeByte(18); + output.writeUnsigned(18); writeLocation(statement.getLocation()); writeExpr(statement.getObjectRef()); } catch (IOException e) { @@ -437,7 +412,7 @@ public class AstIO { @Override public void visit(MonitorExitStatement statement) { try { - output.writeByte(19); + output.writeUnsigned(19); writeLocation(statement.getLocation()); writeExpr(statement.getObjectRef()); } catch (IOException e) { @@ -448,9 +423,9 @@ public class AstIO { @Override public void visit(BinaryExpr expr) { try { - output.writeByte(0); - output.writeByte(expr.getOperation().ordinal()); - output.writeByte(expr.getType() != null ? expr.getType().ordinal() + 1 : 0); + 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) { @@ -461,9 +436,9 @@ public class AstIO { @Override public void visit(UnaryExpr expr) { try { - output.writeByte(1); - output.writeByte(expr.getOperation().ordinal()); - output.writeByte(expr.getType() != null ? expr.getType().ordinal() + 1 : 0); + 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); @@ -473,7 +448,7 @@ public class AstIO { @Override public void visit(ConditionalExpr expr) { try { - output.writeByte(2); + output.writeUnsigned(2); writeExpr(expr.getCondition()); writeExpr(expr.getConsequent()); writeExpr(expr.getAlternative()); @@ -487,25 +462,25 @@ public class AstIO { try { Object value = expr.getValue(); if (value == null) { - output.writeByte(3); + output.writeUnsigned(3); } else if (value instanceof Integer) { - output.writeByte(4); - output.writeInt((Integer) value); + output.writeUnsigned(4); + output.writeSigned((Integer) value); } else if (value instanceof Long) { - output.writeByte(5); - output.writeLong((Long) value); + output.writeUnsigned(5); + output.writeSigned((Long) value); } else if (value instanceof Float) { - output.writeByte(6); + output.writeUnsigned(6); output.writeFloat((Float) value); } else if (value instanceof Double) { - output.writeByte(7); + output.writeUnsigned(7); output.writeDouble((Double) value); } else if (value instanceof String) { - output.writeByte(8); - output.writeUTF((String) value); + output.writeUnsigned(8); + output.write((String) value); } else if (value instanceof ValueType) { - output.writeByte(9); - output.writeInt(symbolTable.lookup(value.toString())); + output.writeUnsigned(9); + output.writeUnsigned(symbolTable.lookup(value.toString())); } } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -515,8 +490,8 @@ public class AstIO { @Override public void visit(VariableExpr expr) { try { - output.writeByte(10); - output.writeShort(expr.getIndex()); + output.writeUnsigned(10); + output.writeUnsigned(expr.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -525,10 +500,10 @@ public class AstIO { @Override public void visit(SubscriptExpr expr) { try { - output.writeByte(11); + output.writeUnsigned(11); writeExpr(expr.getArray()); writeExpr(expr.getIndex()); - output.writeByte(expr.getType().ordinal()); + output.writeUnsigned(expr.getType().ordinal()); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -537,8 +512,8 @@ public class AstIO { @Override public void visit(UnwrapArrayExpr expr) { try { - output.writeByte(12); - output.writeByte(expr.getElementType().ordinal()); + output.writeUnsigned(12); + output.writeUnsigned(expr.getElementType().ordinal()); writeExpr(expr.getArray()); } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -550,21 +525,21 @@ public class AstIO { try { switch (expr.getType()) { case CONSTRUCTOR: - output.writeByte(13); + output.writeUnsigned(13); break; case STATIC: - output.writeByte(14); + output.writeUnsigned(14); break; case SPECIAL: - output.writeByte(15); + output.writeUnsigned(15); break; case DYNAMIC: - output.writeByte(16); + output.writeUnsigned(16); break; } - output.writeInt(symbolTable.lookup(expr.getMethod().getClassName())); - output.writeInt(symbolTable.lookup(expr.getMethod().getDescriptor().toString())); - output.writeShort(expr.getArguments().size()); + 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)); } @@ -576,12 +551,12 @@ public class AstIO { @Override public void visit(QualificationExpr expr) { try { - output.writeByte(expr.getQualified() != null ? 17 : 18); + output.writeUnsigned(expr.getQualified() == null ? 17 : 18); if (expr.getQualified() != null) { writeExpr(expr.getQualified()); } - output.writeInt(symbolTable.lookup(expr.getField().getClassName())); - output.writeInt(symbolTable.lookup(expr.getField().getFieldName())); + output.writeUnsigned(symbolTable.lookup(expr.getField().getClassName())); + output.writeUnsigned(symbolTable.lookup(expr.getField().getFieldName())); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -590,8 +565,8 @@ public class AstIO { @Override public void visit(NewExpr expr) { try { - output.writeByte(19); - output.writeInt(symbolTable.lookup(expr.getConstructedClass())); + output.writeUnsigned(19); + output.writeUnsigned(symbolTable.lookup(expr.getConstructedClass())); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -600,9 +575,9 @@ public class AstIO { @Override public void visit(NewArrayExpr expr) { try { - output.writeByte(20); + output.writeUnsigned(20); writeExpr(expr.getLength()); - output.writeInt(symbolTable.lookup(expr.getType().toString())); + output.writeUnsigned(symbolTable.lookup(expr.getType().toString())); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -611,12 +586,12 @@ public class AstIO { @Override public void visit(NewMultiArrayExpr expr) { try { - output.writeByte(21); - output.writeByte(expr.getDimensions().size()); + output.writeUnsigned(21); + output.writeUnsigned(expr.getDimensions().size()); for (Expr dimension : expr.getDimensions()) { writeExpr(dimension); } - output.writeInt(symbolTable.lookup(expr.getType().toString())); + output.writeUnsigned(symbolTable.lookup(expr.getType().toString())); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -625,9 +600,9 @@ public class AstIO { @Override public void visit(InstanceOfExpr expr) { try { - output.writeByte(22); + output.writeUnsigned(22); writeExpr(expr.getExpr()); - output.writeInt(symbolTable.lookup(expr.getType().toString())); + output.writeUnsigned(symbolTable.lookup(expr.getType().toString())); } catch (IOException e) { throw new IOExceptionWrapper(e); } @@ -636,8 +611,8 @@ public class AstIO { @Override public void visit(CastExpr expr) { try { - output.writeByte(23); - output.writeInt(symbolTable.lookup(expr.getTarget().toString())); + output.writeUnsigned(23); + output.writeUnsigned(symbolTable.lookup(expr.getTarget().toString())); writeExpr(expr.getValue()); } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -647,9 +622,9 @@ public class AstIO { @Override public void visit(PrimitiveCastExpr expr) { try { - output.writeByte(24); - output.writeByte(expr.getSource().ordinal()); - output.writeByte(expr.getTarget().ordinal()); + output.writeUnsigned(24); + output.writeUnsigned(expr.getSource().ordinal()); + output.writeUnsigned(expr.getTarget().ordinal()); writeExpr(expr.getValue()); } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -657,31 +632,31 @@ public class AstIO { } } - private TextLocation readLocation(DataInput input) throws IOException { - int fileIndex = input.readShort(); - if (fileIndex == -1) { + private TextLocation readLocation(VarDataInput input) throws IOException { + int fileIndex = input.readUnsigned(); + if (fileIndex == 0) { return null; } else { - return new TextLocation(fileTable.at(fileIndex), input.readShort()); + return new TextLocation(fileTable.at(fileIndex - 1), input.readUnsigned()); } } - private Statement readStatement(DataInput input) throws IOException { - byte type = input.readByte(); + private Statement readStatement(VarDataInput input) throws IOException { + int type = input.readUnsigned(); switch (type) { case 0: { AssignmentStatement stmt = new AssignmentStatement(); stmt.setLocation(readLocation(input)); stmt.setLeftValue(readExpr(input)); stmt.setRightValue(readExpr(input)); - stmt.setAsync(input.readBoolean()); + stmt.setAsync(input.readUnsigned() != 0); return stmt; } case 1: { AssignmentStatement stmt = new AssignmentStatement(); stmt.setLocation(readLocation(input)); stmt.setRightValue(readExpr(input)); - stmt.setAsync(input.readBoolean()); + stmt.setAsync(input.readUnsigned() != 0); return stmt; } case 2: { @@ -698,15 +673,15 @@ public class AstIO { } case 4: { SwitchStatement stmt = new SwitchStatement(); - stmt.setId(readNullableString(input)); + stmt.setId(input.read()); stmt.setValue(readExpr(input)); - int clauseCount = input.readShort(); + int clauseCount = input.readUnsigned(); for (int i = 0; i < clauseCount; ++i) { SwitchClause clause = new SwitchClause(); - int conditionCount = input.readShort(); + int conditionCount = input.readUnsigned(); int[] conditions = new int[conditionCount]; for (int j = 0; j < conditionCount; ++j) { - conditions[j] = input.readInt(); + conditions[j] = input.readSigned(); } clause.setConditions(conditions); readSequence(input, clause.getBody()); @@ -717,7 +692,7 @@ public class AstIO { } case 5: { WhileStatement stmt = new WhileStatement(); - stmt.setId(readNullableString(input)); + stmt.setId(input.read()); stmt.setCondition(readExpr(input)); if (stmt.getId() != null) { statementMap.put(stmt.getId(), stmt); @@ -727,7 +702,7 @@ public class AstIO { } case 6: { WhileStatement stmt = new WhileStatement(); - stmt.setId(readNullableString(input)); + stmt.setId(input.read()); if (stmt.getId() != null) { statementMap.put(stmt.getId(), stmt); } @@ -736,7 +711,7 @@ public class AstIO { } case 7: { BlockStatement stmt = new BlockStatement(); - stmt.setId(readNullableString(input)); + stmt.setId(input.read()); if (stmt.getId() != null) { statementMap.put(stmt.getId(), stmt); } @@ -746,7 +721,7 @@ public class AstIO { case 8: { BreakStatement stmt = new BreakStatement(); stmt.setLocation(readLocation(input)); - stmt.setTarget(statementMap.get(input.readUTF())); + stmt.setTarget(statementMap.get(input.read())); return stmt; } case 9: { @@ -757,7 +732,7 @@ public class AstIO { case 10: { ContinueStatement stmt = new ContinueStatement(); stmt.setLocation(readLocation(input)); - stmt.setTarget(statementMap.get(input.readUTF())); + stmt.setTarget(statementMap.get(input.read())); return stmt; } case 11: { @@ -785,26 +760,26 @@ public class AstIO { case 15: { InitClassStatement stmt = new InitClassStatement(); stmt.setLocation(readLocation(input)); - stmt.setClassName(symbolTable.at(input.readInt())); + stmt.setClassName(symbolTable.at(input.readUnsigned())); return stmt; } case 16: { TryCatchStatement stmt = new TryCatchStatement(); readSequence(input, stmt.getProtectedBody()); - int exceptionTypeIndex = input.readInt(); - if (exceptionTypeIndex >= 0) { - stmt.setExceptionType(symbolTable.at(exceptionTypeIndex)); + int exceptionTypeIndex = input.readUnsigned(); + if (exceptionTypeIndex > 0) { + stmt.setExceptionType(symbolTable.at(exceptionTypeIndex - 1)); } - int exceptionVarIndex = input.readShort(); - if (exceptionVarIndex >= 0) { - stmt.setExceptionVariable(exceptionVarIndex); + 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.readShort()); + stmt.setPart(input.readUnsigned()); return stmt; } case 18: { @@ -824,41 +799,37 @@ public class AstIO { } } - private void readSequence(DataInput input, List statements) throws IOException { - int count = input.readShort(); + 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 String readNullableString(DataInput input) throws IOException { - return input.readBoolean() ? input.readUTF() : null; - } - - private Expr readExpr(DataInput input) throws IOException { + private Expr readExpr(VarDataInput input) throws IOException { TextLocation location = readLocation(input); Expr expr = readExprWithoutLocation(input); expr.setLocation(location); return expr; } - private Expr readExprWithoutLocation(DataInput input) throws IOException { - byte type = input.readByte(); + private Expr readExprWithoutLocation(VarDataInput input) throws IOException { + int type = input.readUnsigned(); switch (type) { case 0: { BinaryExpr expr = new BinaryExpr(); - expr.setOperation(binaryOperations[input.readByte()]); - byte valueType = input.readByte(); - expr.setType(valueType > 0 ? OperationType.values()[valueType] : null); + 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.setOperation(unaryOperations[input.readByte()]); - byte valueType = input.readByte(); - expr.setType(valueType > 0 ? OperationType.values()[valueType] : null); + expr.setOperation(unaryOperations[input.readUnsigned()]); + int valueType = input.readUnsigned(); + expr.setType(valueType > 0 ? OperationType.values()[valueType - 1] : null); expr.setOperand(readExpr(input)); return expr; } @@ -874,12 +845,12 @@ public class AstIO { } case 4: { ConstantExpr expr = new ConstantExpr(); - expr.setValue(input.readInt()); + expr.setValue(input.readSigned()); return expr; } case 5: { ConstantExpr expr = new ConstantExpr(); - expr.setValue(input.readLong()); + expr.setValue(input.readSignedLong()); return expr; } case 6: { @@ -894,28 +865,28 @@ public class AstIO { } case 8: { ConstantExpr expr = new ConstantExpr(); - expr.setValue(input.readUTF()); + expr.setValue(input.read()); return expr; } case 9: { ConstantExpr expr = new ConstantExpr(); - expr.setValue(ValueType.parse(symbolTable.at(input.readInt()))); + expr.setValue(ValueType.parse(symbolTable.at(input.readUnsigned()))); return expr; } case 10: { VariableExpr expr = new VariableExpr(); - expr.setIndex(input.readShort()); + expr.setIndex(input.readUnsigned()); return expr; } case 11: { SubscriptExpr expr = new SubscriptExpr(); expr.setArray(readExpr(input)); expr.setIndex(readExpr(input)); - expr.setType(ArrayType.values()[input.readByte()]); + expr.setType(ArrayType.values()[input.readUnsigned()]); return expr; } case 12: { - UnwrapArrayExpr expr = new UnwrapArrayExpr(ArrayType.values()[input.readByte()]); + UnwrapArrayExpr expr = new UnwrapArrayExpr(ArrayType.values()[input.readUnsigned()]); expr.setArray(readExpr(input)); return expr; } @@ -929,55 +900,55 @@ public class AstIO { return parseInvocationExpr(InvocationType.DYNAMIC, input); case 17: { QualificationExpr expr = new QualificationExpr(); - String className = symbolTable.at(input.readInt()); - String fieldName = symbolTable.at(input.readInt()); + 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.setQualified(readExpr(input)); - String className = symbolTable.at(input.readInt()); - String fieldName = symbolTable.at(input.readInt()); + 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.setConstructedClass(symbolTable.at(input.readInt())); + expr.setConstructedClass(symbolTable.at(input.readUnsigned())); return expr; } case 20: { NewArrayExpr expr = new NewArrayExpr(); expr.setLength(readExpr(input)); - expr.setType(ValueType.parse(symbolTable.at(input.readInt()))); + expr.setType(ValueType.parse(symbolTable.at(input.readUnsigned()))); return expr; } case 21: { NewMultiArrayExpr expr = new NewMultiArrayExpr(); - int dimensionCount = input.readByte(); + int dimensionCount = input.readUnsigned(); for (int i = 0; i < dimensionCount; ++i) { expr.getDimensions().add(readExpr(input)); } - expr.setType(ValueType.parse(symbolTable.at(input.readInt()))); + expr.setType(ValueType.parse(symbolTable.at(input.readUnsigned()))); return expr; } case 22: { InstanceOfExpr expr = new InstanceOfExpr(); expr.setExpr(readExpr(input)); - expr.setType(ValueType.parse(symbolTable.at(input.readInt()))); + expr.setType(ValueType.parse(symbolTable.at(input.readUnsigned()))); return expr; } case 23: { CastExpr expr = new CastExpr(); - expr.setTarget(ValueType.parse(symbolTable.at(input.readInt()))); + expr.setTarget(ValueType.parse(symbolTable.at(input.readUnsigned()))); expr.setValue(readExpr(input)); return expr; } case 24: { PrimitiveCastExpr expr = new PrimitiveCastExpr(); - expr.setSource(OperationType.values()[input.readByte()]); - expr.setTarget(OperationType.values()[input.readByte()]); + expr.setSource(OperationType.values()[input.readUnsigned()]); + expr.setTarget(OperationType.values()[input.readUnsigned()]); expr.setValue(readExpr(input)); return expr; } @@ -986,17 +957,14 @@ public class AstIO { } } - private InvocationExpr parseInvocationExpr(InvocationType invocationType, DataInput input) throws IOException { + private InvocationExpr parseInvocationExpr(InvocationType invocationType, VarDataInput input) throws IOException { InvocationExpr expr = new InvocationExpr(); expr.setType(invocationType); - String className = symbolTable.at(input.readInt()); - MethodDescriptor method = methodDescriptorCache.computeIfAbsent(symbolTable.at(input.readInt()), - MethodDescriptor::parse); - MethodReference methodRef = methodReferenceCache - .computeIfAbsent(className, k -> new HashMap<>()) - .computeIfAbsent(method, k -> new MethodReference(className, k)); + 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.readShort(); + int argCount = input.readUnsigned(); for (int i = 0; i < argCount; ++i) { expr.getArguments().add(readExpr(input)); } diff --git a/core/src/main/java/org/teavm/cache/DiskMethodNodeCache.java b/core/src/main/java/org/teavm/cache/DiskMethodNodeCache.java index 430a77933..37a21e910 100644 --- a/core/src/main/java/org/teavm/cache/DiskMethodNodeCache.java +++ b/core/src/main/java/org/teavm/cache/DiskMethodNodeCache.java @@ -17,9 +17,6 @@ package org.teavm.cache; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -57,7 +54,7 @@ public class DiskMethodNodeCache implements MethodNodeCache { File file = getMethodFile(methodReference, false); if (file.exists()) { try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { - DataInput input = new DataInputStream(stream); + VarDataInput input = new VarDataInput(stream); if (!checkIfDependenciesChanged(input, cacheStatus)) { RegularMethodNode node = astIO.read(input, methodReference); ControlFlowEntry[] cfg = astIO.readControlFlow(input); @@ -89,7 +86,7 @@ public class DiskMethodNodeCache implements MethodNodeCache { File file = getMethodFile(methodReference, true); if (file.exists()) { try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { - DataInput input = new DataInputStream(stream); + VarDataInput input = new VarDataInput(stream); if (!checkIfDependenciesChanged(input, cacheStatus)) { item.node = astIO.readAsync(input, methodReference); } @@ -101,10 +98,10 @@ public class DiskMethodNodeCache implements MethodNodeCache { return item.node; } - private boolean checkIfDependenciesChanged(DataInput input, CacheStatus cacheStatus) throws IOException { - int depCount = input.readShort(); + private boolean checkIfDependenciesChanged(VarDataInput input, CacheStatus cacheStatus) throws IOException { + int depCount = input.readUnsigned(); for (int i = 0; i < depCount; ++i) { - String depClass = input.readUTF(); + String depClass = input.read(); if (cacheStatus.isStaleClass(depClass)) { return true; } @@ -125,10 +122,10 @@ public class DiskMethodNodeCache implements MethodNodeCache { for (MethodReference method : newMethods) { File file = getMethodFile(method, true); Item item = cache.get(method); - try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { - output.writeShort(item.dependencies.length); + try (VarDataOutput output = new VarDataOutput(new BufferedOutputStream(new FileOutputStream(file)))) { + output.writeUnsigned(item.dependencies.length); for (String dependency : item.dependencies) { - output.writeUTF(dependency); + output.write(dependency); } astIO.write(output, item.entry.method); astIO.write(output, item.entry.cfg); @@ -137,10 +134,10 @@ public class DiskMethodNodeCache implements MethodNodeCache { for (MethodReference method : newAsyncMethods) { File file = getMethodFile(method, true); AsyncItem item = asyncCache.get(method); - try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { - output.writeShort(item.dependencies.length); + try (VarDataOutput output = new VarDataOutput(new BufferedOutputStream(new FileOutputStream(file)))) { + output.writeUnsigned(item.dependencies.length); for (String dependency : item.dependencies) { - output.writeUTF(dependency); + output.write(dependency); } astIO.writeAsync(output, item.node); } diff --git a/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java b/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java index 20390ca91..ef4b3b754 100644 --- a/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java +++ b/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java @@ -15,11 +15,16 @@ */ package org.teavm.cache; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; import org.teavm.ast.AsyncMethodNode; +import org.teavm.ast.ControlFlowEntry; +import org.teavm.ast.RegularMethodNode; import org.teavm.model.MethodReference; public class InMemoryMethodNodeCache implements MethodNodeCache { @@ -27,6 +32,11 @@ public class InMemoryMethodNodeCache implements MethodNodeCache { private Map newItems = new HashMap<>(); private Map asyncCache = new HashMap<>(); private Map newAsyncItems = new HashMap<>(); + private AstIO io; + + public InMemoryMethodNodeCache(InMemorySymbolTable symbolTable, InMemorySymbolTable fileSymbolTable) { + io = new AstIO(symbolTable, fileSymbolTable); + } @Override public AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus) { @@ -39,7 +49,14 @@ public class InMemoryMethodNodeCache implements MethodNodeCache { return null; } - return item.entry; + VarDataInput input = new VarDataInput(new ByteArrayInputStream(item.entry)); + try { + ControlFlowEntry[] cfg = io.readControlFlow(input); + RegularMethodNode ast = io.read(input, methodReference); + return new AstCacheEntry(ast, cfg); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override @@ -58,7 +75,12 @@ public class InMemoryMethodNodeCache implements MethodNodeCache { return null; } - return item.node; + VarDataInput input = new VarDataInput(new ByteArrayInputStream(item.node)); + try { + return io.readAsync(input, methodReference); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override @@ -85,22 +107,37 @@ public class InMemoryMethodNodeCache implements MethodNodeCache { newAsyncItems.clear(); } - static final class RegularItem { - final AstCacheEntry entry; + final class RegularItem { + final byte[] entry; final String[] dependencies; RegularItem(AstCacheEntry entry, String[] dependencies) { - this.entry = entry; + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + VarDataOutput data = new VarDataOutput(output); + io.write(data, entry.cfg); + io.write(data, entry.method); + this.entry = output.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } this.dependencies = dependencies; } } - static final class AsyncItem { - final AsyncMethodNode node; + final class AsyncItem { + final byte[] node; final String[] dependencies; AsyncItem(AsyncMethodNode node, String[] dependencies) { - this.node = node; + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + VarDataOutput data = new VarDataOutput(output); + io.writeAsync(data, node); + this.node = output.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } this.dependencies = dependencies; } } diff --git a/core/src/main/java/org/teavm/cache/InMemoryProgramCache.java b/core/src/main/java/org/teavm/cache/InMemoryProgramCache.java index fd65978e1..4bbb45ed5 100644 --- a/core/src/main/java/org/teavm/cache/InMemoryProgramCache.java +++ b/core/src/main/java/org/teavm/cache/InMemoryProgramCache.java @@ -29,9 +29,11 @@ import org.teavm.model.ProgramCache; public class InMemoryProgramCache implements ProgramCache { private Map cache = new HashMap<>(); private Map newItems = new HashMap<>(); - private InMemorySymbolTable symbolTable = new InMemorySymbolTable(); - private InMemorySymbolTable fileSymbolTable = new InMemorySymbolTable(); - private ProgramIO io = new ProgramIO(new InMemorySymbolTable(), new InMemorySymbolTable()); + private ProgramIO io; + + public InMemoryProgramCache(InMemorySymbolTable symbolTable, InMemorySymbolTable fileSymbolTable) { + io = new ProgramIO(symbolTable, fileSymbolTable); + } @Override public Program get(MethodReference method, CacheStatus cacheStatus) { @@ -78,8 +80,6 @@ public class InMemoryProgramCache implements ProgramCache { public void invalidate() { cache.clear(); newItems.clear(); - symbolTable.invalidate(); - fileSymbolTable.invalidate(); } static final class Item { diff --git a/core/src/main/java/org/teavm/cache/InMemorySymbolTable.java b/core/src/main/java/org/teavm/cache/InMemorySymbolTable.java index 54effd518..19617db28 100644 --- a/core/src/main/java/org/teavm/cache/InMemorySymbolTable.java +++ b/core/src/main/java/org/teavm/cache/InMemorySymbolTable.java @@ -20,7 +20,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -class InMemorySymbolTable implements SymbolTable { +public class InMemorySymbolTable implements SymbolTable { private List symbols = new ArrayList<>(); private Map indexes = new HashMap<>(); diff --git a/core/src/main/java/org/teavm/cache/ProgramIO.java b/core/src/main/java/org/teavm/cache/ProgramIO.java index 937e8cca9..31dd44abd 100644 --- a/core/src/main/java/org/teavm/cache/ProgramIO.java +++ b/core/src/main/java/org/teavm/cache/ProgramIO.java @@ -106,7 +106,7 @@ public class ProgramIO { for (int i = 0; i < program.variableCount(); ++i) { Variable var = program.variableAt(i); data.writeUnsigned(var.getRegister()); - data.write(var.getDebugName() != null ? var.getDebugName() : ""); + data.write(var.getDebugName()); } for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock basicBlock = program.basicBlockAt(i); @@ -159,9 +159,6 @@ public class ProgramIO { Variable var = program.createVariable(); var.setRegister(data.readUnsigned()); var.setDebugName(referenceCache.getCached(data.read())); - if (var.getDebugName().isEmpty()) { - var.setDebugName(null); - } } for (int i = 0; i < basicBlockCount; ++i) { program.createBasicBlock(); diff --git a/core/src/main/java/org/teavm/cache/VarDataInput.java b/core/src/main/java/org/teavm/cache/VarDataInput.java index 7cd3e3921..beb73d771 100644 --- a/core/src/main/java/org/teavm/cache/VarDataInput.java +++ b/core/src/main/java/org/teavm/cache/VarDataInput.java @@ -15,6 +15,7 @@ */ package org.teavm.cache; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -33,6 +34,9 @@ public class VarDataInput { int b; do { b = input.read(); + if (b < 0) { + throw new EOFException(); + } value |= (b & DATA) << pos; pos += 7; } while ((b & NEXT) != 0); @@ -41,7 +45,7 @@ public class VarDataInput { public int readSigned() throws IOException { int value = readUnsigned(); - return (value & 1) == 0 ? (value >> 1) : -(value >> 1); + return (value & 1) == 0 ? (value >>> 1) : -(value >>> 1); } public long readUnsignedLong() throws IOException { @@ -50,6 +54,9 @@ public class VarDataInput { int b; do { b = input.read(); + if (b < 0) { + throw new EOFException(); + } value |= ((long) b & DATA) << pos; pos += 7; } while ((b & NEXT) != 0); @@ -91,6 +98,10 @@ public class VarDataInput { public String read() throws IOException { int sz = readUnsigned(); + if (sz == 0) { + return null; + } + sz--; char[] chars = new char[sz]; for (int i = 0; i < sz; ++i) { chars[i] = (char) readUnsigned(); diff --git a/core/src/main/java/org/teavm/cache/VarDataOutput.java b/core/src/main/java/org/teavm/cache/VarDataOutput.java index 36372fa01..9a7bd571a 100644 --- a/core/src/main/java/org/teavm/cache/VarDataOutput.java +++ b/core/src/main/java/org/teavm/cache/VarDataOutput.java @@ -15,10 +15,11 @@ */ package org.teavm.cache; +import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; -public class VarDataOutput { +public class VarDataOutput implements Closeable { private static final int DATA = 0x7F; private static final int NEXT = 0x80; private OutputStream output; @@ -76,9 +77,18 @@ public class VarDataOutput { } public void write(String s) throws IOException { - writeUnsigned(s.length()); + if (s == null) { + writeUnsigned(0); + return; + } + writeUnsigned(s.length() + 1); for (int i = 0; i < s.length(); ++i) { writeUnsigned(s.charAt(i)); } } + + @Override + public void close() throws IOException { + output.close(); + } } diff --git a/tests/src/test/java/org/teavm/incremental/IncrementalTest.java b/tests/src/test/java/org/teavm/incremental/IncrementalTest.java index 9a4126f9a..5ba4f665d 100644 --- a/tests/src/test/java/org/teavm/incremental/IncrementalTest.java +++ b/tests/src/test/java/org/teavm/incremental/IncrementalTest.java @@ -48,6 +48,7 @@ import org.teavm.cache.AstCacheEntry; import org.teavm.cache.CacheStatus; import org.teavm.cache.InMemoryMethodNodeCache; import org.teavm.cache.InMemoryProgramCache; +import org.teavm.cache.InMemorySymbolTable; import org.teavm.callgraph.CallGraph; import org.teavm.dependency.FastDependencyAnalyzer; import org.teavm.diagnostics.DefaultProblemTextConsumer; @@ -236,6 +237,10 @@ public class IncrementalTest { final Set updatedMethods = new HashSet<>(); boolean capturing; + CapturingMethodNodeCache() { + super(new InMemorySymbolTable(), new InMemorySymbolTable()); + } + @Override public void store(MethodReference methodReference, AstCacheEntry node, Supplier dependencies) { super.store(methodReference, node, dependencies); @@ -257,6 +262,10 @@ public class IncrementalTest { final Set updatedMethods = new HashSet<>(); boolean capturing; + CapturingProgramCache() { + super(new InMemorySymbolTable(), new InMemorySymbolTable()); + } + @Override public void store(MethodReference method, Program program, Supplier dependencies) { super.store(method, program, dependencies); diff --git a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java index e5c043c49..085bd2f6a 100644 --- a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java +++ b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java @@ -69,6 +69,7 @@ import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.cache.InMemoryMethodNodeCache; import org.teavm.cache.InMemoryProgramCache; +import org.teavm.cache.InMemorySymbolTable; import org.teavm.cache.MemoryCachedClassReaderSource; import org.teavm.debugging.information.DebugInformation; import org.teavm.debugging.information.DebugInformationBuilder; @@ -136,6 +137,8 @@ public class CodeServlet extends HttpServlet { private List listeners = new ArrayList<>(); private HttpClient httpClient; private WebSocketClient wsClient = new WebSocketClient(); + private InMemorySymbolTable symbolTable = new InMemorySymbolTable(); + private InMemorySymbolTable fileSymbolTable = new InMemorySymbolTable(); public CodeServlet(String mainClass, String[] classPath) { this.mainClass = mainClass; @@ -223,6 +226,8 @@ public class CodeServlet extends HttpServlet { astCache.invalidate(); programCache.invalidate(); classSource.invalidate(); + symbolTable.invalidate(); + fileSymbolTable.invalidate(); } } @@ -702,8 +707,8 @@ public class CodeServlet extends HttpServlet { watcher = new FileSystemWatcher(classPath); classSource = new MemoryCachedClassReaderSource(); - astCache = new InMemoryMethodNodeCache(); - programCache = new InMemoryProgramCache(); + astCache = new InMemoryMethodNodeCache(symbolTable, fileSymbolTable); + programCache = new InMemoryProgramCache(symbolTable, fileSymbolTable); } private void shutdownBuilder() {