From 13b64fd4c1eb6125161d81eb8c2baeed24662f1e Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Tue, 9 Sep 2014 12:18:46 +0400 Subject: [PATCH] Fixes problems with reading and writing a program --- .../cache/ClassPathClassDateProvider.java | 4 +- .../org/teavm/cache/DiskProgramCache.java | 209 ++++++++++++++++++ .../main/java/org/teavm/cache/ProgramIO.java | 2 + .../jso/plugin/JavascriptNativeProcessor.java | 5 + 4 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/cache/DiskProgramCache.java diff --git a/teavm-core/src/main/java/org/teavm/cache/ClassPathClassDateProvider.java b/teavm-core/src/main/java/org/teavm/cache/ClassPathClassDateProvider.java index 8ff68516a..159597936 100644 --- a/teavm-core/src/main/java/org/teavm/cache/ClassPathClassDateProvider.java +++ b/teavm-core/src/main/java/org/teavm/cache/ClassPathClassDateProvider.java @@ -45,10 +45,10 @@ public class ClassPathClassDateProvider implements ClassDateProvider { // If URI is invalid, we just report that class should be reparsed return null; } - } else if (url.getProtocol().equals("jar")) { + } else if (url.getProtocol().equals("jar") && url.getPath().startsWith("file:")) { int exclIndex = url.getPath().indexOf('!'); String jarFileName = exclIndex >= 0 ? url.getPath().substring(0, exclIndex) : url.getPath(); - File file = new File(jarFileName); + File file = new File(jarFileName.substring("file:".length())); return file.exists() ? new Date(file.lastModified()) : null; } else { return null; diff --git a/teavm-core/src/main/java/org/teavm/cache/DiskProgramCache.java b/teavm-core/src/main/java/org/teavm/cache/DiskProgramCache.java new file mode 100644 index 000000000..66801d0f4 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/cache/DiskProgramCache.java @@ -0,0 +1,209 @@ +/* + * 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.model.*; +import org.teavm.model.instructions.*; + +/** + * + * @author Alexey Andreev + */ +public class DiskProgramCache implements ProgramCache { + private File directory; + private ProgramIO programIO; + private Map cache = new HashMap<>(); + private Set newMethods = new HashSet<>(); + private ClassDateProvider classDateProvider; + + public DiskProgramCache(File directory, SymbolTable symbolTable, SymbolTable fileTable, + ClassDateProvider classDateProvider) { + this.directory = directory; + programIO = new ProgramIO(symbolTable, fileTable); + this.classDateProvider = classDateProvider; + } + + @Override + public Program get(MethodReference method) { + Item item = cache.get(method); + if (item == null) { + item = new Item(); + cache.put(method, item); + File file = getMethodFile(method); + if (file.exists()) { + try (InputStream stream = new FileInputStream(file)) { + DataInput input = new DataInputStream(stream); + int depCount = input.readShort(); + boolean dependenciesChanged = false; + for (int i = 0; i < depCount; ++i) { + String depClass = input.readUTF(); + Date depDate = classDateProvider.getModificationDate(depClass); + if (depDate == null || depDate.after(new Date(file.lastModified()))) { + dependenciesChanged = true; + break; + } + } + if (!dependenciesChanged) { + item.program = programIO.read(stream); + } + } catch (IOException e) { + // we could not read program, just leave it empty + } + } + } + if (item.program == null) { + System.out.println(method); + } + return item.program; + } + + @Override + public void store(MethodReference method, Program program) { + Item item = new Item(); + cache.put(method, item); + item.program = program; + newMethods.add(method); + } + + public void flush() throws IOException { + for (MethodReference method : newMethods) { + File file = getMethodFile(method); + ProgramDependencyAnalyzer analyzer = new ProgramDependencyAnalyzer(); + analyzer.dependencies.add(method.getClassName()); + Program program = cache.get(method).program; + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + for (Instruction insn : block.getInstructions()) { + insn.acceptVisitor(analyzer); + } + } + file.getParentFile().mkdirs(); + try (OutputStream stream = new FileOutputStream(file)) { + DataOutput output = new DataOutputStream(stream); + output.writeShort(analyzer.dependencies.size()); + for (String dep : analyzer.dependencies) { + output.writeUTF(dep); + } + programIO.write(program, stream); + } + } + } + + 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"); + } + + static class Item { + Program program; + } + + static class ProgramDependencyAnalyzer implements InstructionVisitor { + Set dependencies = new HashSet<>(); + @Override public void visit(GetFieldInstruction insn) { + dependencies.add(insn.getField().getClassName()); + } + @Override public void visit(PutFieldInstruction insn) { + dependencies.add(insn.getField().getClassName()); + } + @Override public void visit(InvokeInstruction insn) { + dependencies.add(insn.getMethod().getClassName()); + } + @Override public void visit(EmptyInstruction insn) { } + @Override public void visit(ClassConstantInstruction insn) { } + @Override public void visit(NullConstantInstruction insn) { } + @Override public void visit(IntegerConstantInstruction insn) { } + @Override public void visit(LongConstantInstruction insn) { } + @Override public void visit(FloatConstantInstruction insn) { } + @Override public void visit(DoubleConstantInstruction insn) { } + @Override public void visit(StringConstantInstruction insn) { } + @Override public void visit(BinaryInstruction insn) { } + @Override public void visit(NegateInstruction insn) { } + @Override public void visit(AssignInstruction insn) { } + @Override public void visit(CastInstruction insn) { } + @Override public void visit(CastNumberInstruction insn) { } + @Override public void visit(CastIntegerInstruction insn) { } + @Override public void visit(BranchingInstruction insn) { } + @Override public void visit(BinaryBranchingInstruction insn) { } + @Override public void visit(JumpInstruction insn) { } + @Override public void visit(SwitchInstruction insn) { } + @Override public void visit(ExitInstruction insn) { } + @Override public void visit(RaiseInstruction insn) { } + @Override public void visit(ConstructArrayInstruction insn) { } + @Override public void visit(ConstructInstruction insn) { } + @Override public void visit(ConstructMultiArrayInstruction insn) { } + @Override public void visit(ArrayLengthInstruction insn) { } + @Override public void visit(CloneArrayInstruction insn) { } + @Override public void visit(UnwrapArrayInstruction insn) { } + @Override public void visit(GetElementInstruction insn) { } + @Override public void visit(PutElementInstruction insn) { } + @Override public void visit(IsInstanceInstruction insn) { } + @Override public void visit(InitClassInstruction insn) { } + @Override public void visit(NullCheckInstruction insn) { } + } +} diff --git a/teavm-core/src/main/java/org/teavm/cache/ProgramIO.java b/teavm-core/src/main/java/org/teavm/cache/ProgramIO.java index 3598eee47..40d9f1542 100644 --- a/teavm-core/src/main/java/org/teavm/cache/ProgramIO.java +++ b/teavm-core/src/main/java/org/teavm/cache/ProgramIO.java @@ -770,6 +770,7 @@ public class ProgramIO { String className = symbolTable.at(input.readInt()); String fieldName = symbolTable.at(input.readInt()); insn.setField(new FieldReference(className, fieldName)); + insn.setFieldType(ValueType.parse(symbolTable.at(input.readInt()))); return insn; } case 25: { @@ -778,6 +779,7 @@ public class ProgramIO { String className = symbolTable.at(input.readInt()); String fieldName = symbolTable.at(input.readInt()); insn.setField(new FieldReference(className, fieldName)); + insn.setFieldType(ValueType.parse(symbolTable.at(input.readInt()))); return insn; } case 26: { diff --git a/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java b/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java index 73e6a0971..3ae229c10 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java +++ b/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java @@ -173,6 +173,7 @@ class JavascriptNativeProcessor { private void addPropertyGet(String propertyName, Variable instance, Variable receiver) { Variable nameVar = addStringWrap(addString(propertyName)); InvokeInstruction insn = new InvokeInstruction(); + insn.setType(InvocationType.SPECIAL); insn.setMethod(new MethodReference(JS.class.getName(), "get", ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()))); insn.setReceiver(receiver); @@ -184,6 +185,7 @@ class JavascriptNativeProcessor { private void addPropertySet(String propertyName, Variable instance, Variable value) { Variable nameVar = addStringWrap(addString(propertyName)); InvokeInstruction insn = new InvokeInstruction(); + insn.setType(InvocationType.SPECIAL); insn.setMethod(new MethodReference(JS.class.getName(), "set", ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), ValueType.VOID)); @@ -195,6 +197,7 @@ class JavascriptNativeProcessor { private void addIndexerGet(Variable array, Variable index, Variable receiver) { InvokeInstruction insn = new InvokeInstruction(); + insn.setType(InvocationType.SPECIAL); insn.setMethod(new MethodReference(JS.class.getName(), "get", ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()))); insn.setReceiver(receiver); @@ -205,6 +208,7 @@ class JavascriptNativeProcessor { private void addIndexerSet(Variable array, Variable index, Variable value) { InvokeInstruction insn = new InvokeInstruction(); + insn.setType(InvocationType.SPECIAL); insn.setMethod(new MethodReference(JS.class.getName(), "set", ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), ValueType.VOID)); @@ -304,6 +308,7 @@ class JavascriptNativeProcessor { Variable functor = program.createVariable(); Variable nameVar = addStringWrap(addString(name)); InvokeInstruction insn = new InvokeInstruction(); + insn.setType(InvocationType.SPECIAL); insn.setMethod(new MethodReference(JS.class.getName(), "function", ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName()), ValueType.object(JSObject.class.getName())));