Add incomplete implementation of AST disk cache

This commit is contained in:
konsoletyper 2014-09-09 17:52:27 +04:00
parent a7d3f26447
commit 0d1a59c03b
6 changed files with 332 additions and 54 deletions

View File

@ -32,6 +32,11 @@ public class AstIO {
private SymbolTable symbolTable;
private SymbolTable fileTable;
public AstIO(SymbolTable symbolTable, SymbolTable fileTable) {
this.symbolTable = symbolTable;
this.fileTable = fileTable;
}
public void write(DataOutput output, RegularMethodNode method) throws IOException {
output.writeInt(packModifiers(method.getModifiers()));
output.writeShort(method.getVariables().size());

View File

@ -80,7 +80,7 @@ public class DiskCachedClassHolderSource implements ClassHolderSource {
if (item.cls != null) {
File classFile = new File(directory, className.replace('.', '/') + ".teavm-cls");
classFile.getParentFile().mkdirs();
try (OutputStream output = new FileOutputStream(classFile)) {
try (OutputStream output = new BufferedOutputStream(new FileOutputStream(classFile))) {
writeClass(output, item.cls);
}
}

View File

@ -91,7 +91,7 @@ public class DiskProgramCache implements ProgramCache {
}
}
file.getParentFile().mkdirs();
try (OutputStream stream = new FileOutputStream(file)) {
try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(file))) {
DataOutput output = new DataOutputStream(stream);
output.writeShort(analyzer.dependencies.size());
for (String dep : analyzer.dependencies) {
@ -104,57 +104,7 @@ public class DiskProgramCache implements ProgramCache {
private File getMethodFile(MethodReference method) {
File dir = new File(directory, method.getClassName().replace('.', '/'));
String desc = method.getDescriptor().toString();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < desc.length(); ++i) {
char c = desc.charAt(i);
switch (c) {
case '/':
sb.append("$s");
break;
case '\\':
sb.append("$b");
break;
case '?':
sb.append("$q");
break;
case '%':
sb.append("$p");
break;
case '*':
sb.append("$a");
break;
case ':':
sb.append("$c");
break;
case '|':
sb.append("$v");
break;
case '$':
sb.append("$$");
break;
case '"':
sb.append("$Q");
break;
case '<':
sb.append("$l");
break;
case '>':
sb.append("$g");
break;
case '.':
sb.append("$d");
break;
case ' ':
sb.append("$w");
break;
default:
sb.append(c);
break;
}
}
return new File(dir, sb + ".teavm-opt");
return new File(dir, FileNameEncoder.encodeFileName(method.getDescriptor().toString()) + ".teavm-opt");
}
static class Item {

View File

@ -0,0 +1,244 @@
/*
* 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.*;
import java.util.*;
import org.teavm.javascript.RegularMethodNodeCache;
import org.teavm.javascript.ast.*;
import org.teavm.model.MethodReference;
import org.teavm.parsing.ClassDateProvider;
/**
*
* @author Alexey Andreev
*/
public class DiskRegularMethodNodeCache implements RegularMethodNodeCache {
private File directory;
private AstIO astIO;
private ClassDateProvider classDateProvider;
private Map<MethodReference, Item> cache = new HashMap<>();
private Set<MethodReference> newMethods = new HashSet<>();
public DiskRegularMethodNodeCache(File directory, SymbolTable symbolTable, SymbolTable fileTable,
ClassDateProvider classDateProvider) {
this.directory = directory;
astIO = new AstIO(symbolTable, fileTable);
this.classDateProvider = classDateProvider;
}
@Override
public RegularMethodNode get(MethodReference methodReference) {
Item item = cache.get(methodReference);
if (item == null) {
item = new Item();
cache.put(methodReference, item);
}
return item.node;
}
@Override
public void store(MethodReference methodReference, RegularMethodNode node) {
Item item = new Item();
item.node = node;
cache.put(methodReference, item);
newMethods.add(methodReference);
}
public void flush() throws IOException {
for (MethodReference method : newMethods) {
File file = getMethodFile(method);
AstDependencyAnalyzer analyzer = new AstDependencyAnalyzer();
RegularMethodNode node = cache.get(method).node;
node.getBody().acceptVisitor(analyzer);
analyzer.dependencies.add(method.getClassName());
try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
output.writeShort(analyzer.dependencies.size());
for (String dependency : analyzer.dependencies) {
output.writeUTF(dependency);
}
astIO.write(output, node);
}
}
}
private File getMethodFile(MethodReference method) {
File dir = new File(directory, method.getClassName().replace('.', '/'));
return new File(dir, FileNameEncoder.encodeFileName(method.getDescriptor().toString()) + ".teavm-ast");
}
static class AstDependencyAnalyzer implements StatementVisitor, ExprVisitor {
Set<String> dependencies = new HashSet<>();
private void visitSequence(List<Statement> statements) {
for (Statement stmt : statements) {
stmt.acceptVisitor(this);
}
}
@Override
public void visit(AssignmentStatement statement) {
if (statement.getLeftValue() != null) {
statement.getLeftValue().acceptVisitor(this);
}
statement.getRightValue().acceptVisitor(this);
}
@Override
public void visit(SequentialStatement statement) {
visitSequence(statement.getSequence());
}
@Override
public void visit(ConditionalStatement statement) {
statement.getCondition().acceptVisitor(this);
visitSequence(statement.getConsequent());
visitSequence(statement.getAlternative());
}
@Override
public void visit(SwitchStatement statement) {
statement.getValue().acceptVisitor(this);
for (SwitchClause clause : statement.getClauses()) {
visitSequence(clause.getBody());
}
visitSequence(statement.getDefaultClause());
}
@Override
public void visit(WhileStatement statement) {
if (statement.getCondition() != null) {
statement.getCondition().acceptVisitor(this);
}
visitSequence(statement.getBody());
}
@Override
public void visit(BlockStatement statement) {
visitSequence(statement.getBody());
}
@Override
public void visit(BreakStatement statement) {
}
@Override
public void visit(ContinueStatement statement) {
}
@Override
public void visit(ReturnStatement statement) {
if (statement.getResult() != null) {
statement.getResult().acceptVisitor(this);
}
}
@Override
public void visit(ThrowStatement statement) {
statement.getException().acceptVisitor(this);
}
@Override
public void visit(InitClassStatement statement) {
}
@Override
public void visit(TryCatchStatement statement) {
visitSequence(statement.getProtectedBody());
visitSequence(statement.getHandler());
}
@Override
public void visit(BinaryExpr expr) {
expr.getFirstOperand().acceptVisitor(this);
expr.getSecondOperand().acceptVisitor(this);
}
@Override
public void visit(UnaryExpr expr) {
expr.getOperand().acceptVisitor(this);
}
@Override
public void visit(ConditionalExpr expr) {
expr.getCondition().acceptVisitor(this);
expr.getConsequent().acceptVisitor(this);
expr.getAlternative().acceptVisitor(this);
}
@Override
public void visit(ConstantExpr expr) {
}
@Override
public void visit(VariableExpr expr) {
}
@Override
public void visit(SubscriptExpr expr) {
expr.getArray().acceptVisitor(this);
expr.getIndex().acceptVisitor(this);
}
@Override
public void visit(UnwrapArrayExpr expr) {
expr.getArray().acceptVisitor(this);
}
@Override
public void visit(InvocationExpr expr) {
dependencies.add(expr.getMethod().getClassName());
for (Expr argument : expr.getArguments()) {
argument.acceptVisitor(this);
}
}
@Override
public void visit(QualificationExpr expr) {
dependencies.add(expr.getField().getClassName());
expr.getQualified().acceptVisitor(this);
}
@Override
public void visit(NewExpr expr) {
}
@Override
public void visit(NewArrayExpr expr) {
expr.getLength().acceptVisitor(this);
}
@Override
public void visit(NewMultiArrayExpr expr) {
for (Expr dimension : expr.getDimensions()) {
dimension.acceptVisitor(this);
}
}
@Override
public void visit(InstanceOfExpr expr) {
expr.getExpr().acceptVisitor(this);
}
@Override
public void visit(StaticClassExpr expr) {
}
}
static class Item {
RegularMethodNode node;
}
}

View File

@ -0,0 +1,78 @@
/*
* 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;
/**
*
* @author Alexey Andreev
*/
public final class FileNameEncoder {
private FileNameEncoder() {
}
public static String encodeFileName(String name) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < name.length(); ++i) {
char c = name.charAt(i);
switch (c) {
case '/':
sb.append("$s");
break;
case '\\':
sb.append("$b");
break;
case '?':
sb.append("$q");
break;
case '%':
sb.append("$p");
break;
case '*':
sb.append("$a");
break;
case ':':
sb.append("$c");
break;
case '|':
sb.append("$v");
break;
case '$':
sb.append("$$");
break;
case '"':
sb.append("$Q");
break;
case '<':
sb.append("$l");
break;
case '>':
sb.append("$g");
break;
case '.':
sb.append("$d");
break;
case ' ':
sb.append("$w");
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
}

View File

@ -62,7 +62,8 @@ public class FileSymbolTable implements SymbolTable {
if (firstUnstoredIndex >= symbols.size()) {
return;
}
try (DataOutputStream output = new DataOutputStream(new FileOutputStream(file, true))) {
try (DataOutputStream output = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream(file, true)))) {
while (firstUnstoredIndex < symbols.size()) {
String symbol = symbols.get(firstUnstoredIndex);
output.writeByte((symbol.length() >> 8) & 0xFF);