Store AST cash in binary format. Introduce more compact

AST serialization format.

Reduce memory consumption of incremental compilation on dev server
This commit is contained in:
Alexey Andreev 2019-03-05 19:06:21 +03:00
parent 573c5f6064
commit 3c9a3bb359
10 changed files with 280 additions and 246 deletions

View File

@ -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<String, IdentifiedStatement> statementMap = new HashMap<>();
private Map<String, MethodDescriptor> methodDescriptorCache = new HashMap<>();
private Map<String, Map<MethodDescriptor, MethodReference>> 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<Statement> 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<Statement> statements) throws IOException {
int count = input.readShort();
private void readSequence(VarDataInput input, List<Statement> 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));
}

View File

@ -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);
}

View File

@ -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<MethodReference, RegularItem> newItems = new HashMap<>();
private Map<MethodReference, AsyncItem> asyncCache = new HashMap<>();
private Map<MethodReference, AsyncItem> 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;
}
}

View File

@ -29,9 +29,11 @@ import org.teavm.model.ProgramCache;
public class InMemoryProgramCache implements ProgramCache {
private Map<MethodReference, Item> cache = new HashMap<>();
private Map<MethodReference, Item> 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 {

View File

@ -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<String> symbols = new ArrayList<>();
private Map<String, Integer> indexes = new HashMap<>();

View File

@ -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();

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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<MethodReference> updatedMethods = new HashSet<>();
boolean capturing;
CapturingMethodNodeCache() {
super(new InMemorySymbolTable(), new InMemorySymbolTable());
}
@Override
public void store(MethodReference methodReference, AstCacheEntry node, Supplier<String[]> dependencies) {
super.store(methodReference, node, dependencies);
@ -257,6 +262,10 @@ public class IncrementalTest {
final Set<MethodReference> updatedMethods = new HashSet<>();
boolean capturing;
CapturingProgramCache() {
super(new InMemorySymbolTable(), new InMemorySymbolTable());
}
@Override
public void store(MethodReference method, Program program, Supplier<String[]> dependencies) {
super.store(method, program, dependencies);

View File

@ -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<DevServerListener> 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() {