diff --git a/teavm-core/src/main/java/org/teavm/cache/AstIO.java b/teavm-core/src/main/java/org/teavm/cache/AstIO.java index c9fd5cde9..f443aa097 100644 --- a/teavm-core/src/main/java/org/teavm/cache/AstIO.java +++ b/teavm-core/src/main/java/org/teavm/cache/AstIO.java @@ -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()); diff --git a/teavm-core/src/main/java/org/teavm/cache/DiskCachedClassHolderSource.java b/teavm-core/src/main/java/org/teavm/cache/DiskCachedClassHolderSource.java index 5a62200aa..800b20a12 100644 --- a/teavm-core/src/main/java/org/teavm/cache/DiskCachedClassHolderSource.java +++ b/teavm-core/src/main/java/org/teavm/cache/DiskCachedClassHolderSource.java @@ -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); } } diff --git a/teavm-core/src/main/java/org/teavm/cache/DiskProgramCache.java b/teavm-core/src/main/java/org/teavm/cache/DiskProgramCache.java index dc40c9d91..716fa20dc 100644 --- a/teavm-core/src/main/java/org/teavm/cache/DiskProgramCache.java +++ b/teavm-core/src/main/java/org/teavm/cache/DiskProgramCache.java @@ -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 { diff --git a/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java b/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java new file mode 100644 index 000000000..1f46ddd95 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/cache/DiskRegularMethodNodeCache.java @@ -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 cache = new HashMap<>(); + private Set 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 dependencies = new HashSet<>(); + + private void visitSequence(List 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; + } +} diff --git a/teavm-core/src/main/java/org/teavm/cache/FileNameEncoder.java b/teavm-core/src/main/java/org/teavm/cache/FileNameEncoder.java new file mode 100644 index 000000000..684af4435 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/cache/FileNameEncoder.java @@ -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(); + } +} diff --git a/teavm-core/src/main/java/org/teavm/cache/FileSymbolTable.java b/teavm-core/src/main/java/org/teavm/cache/FileSymbolTable.java index c42052af6..ae971f3e0 100644 --- a/teavm-core/src/main/java/org/teavm/cache/FileSymbolTable.java +++ b/teavm-core/src/main/java/org/teavm/cache/FileSymbolTable.java @@ -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);