diff --git a/teavm-classlib/pom.xml b/teavm-classlib/pom.xml index b9fc345ec..1024cc56a 100644 --- a/teavm-classlib/pom.xml +++ b/teavm-classlib/pom.xml @@ -72,6 +72,7 @@ process-test-classes false + true 1 en, en_US, en_GB, ru, ru_RU diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index bb989401d..c0e920a3d 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -44,12 +44,21 @@ public class Decompiler { private RangeTree.Node parentNode; private Map generators = new HashMap<>(); private Set methodsToPass = new HashSet<>(); + private RegularMethodNodeCache regularMethodCache; public Decompiler(ClassHolderSource classSource, ClassLoader classLoader) { this.classSource = classSource; this.classLoader = classLoader; } + public RegularMethodNodeCache getRegularMethodCache() { + return regularMethodCache; + } + + public void setRegularMethodCache(RegularMethodNodeCache regularMethodCache) { + this.regularMethodCache = regularMethodCache; + } + public int getGraphSize() { return this.graph.size(); } @@ -165,6 +174,18 @@ public class Decompiler { } public RegularMethodNode decompileRegular(MethodHolder method) { + if (regularMethodCache == null) { + return decompileRegularCacheMiss(method); + } + RegularMethodNode node = regularMethodCache.get(method.getReference()); + if (node == null) { + node = decompileRegularCacheMiss(method); + regularMethodCache.store(method.getReference(), node); + } + return node; + } + + public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) { lastBlockId = 1; graph = ProgramUtils.buildControlFlowGraph(method.getProgram()); indexer = new GraphIndexer(graph); diff --git a/teavm-core/src/main/java/org/teavm/javascript/EmptyRegularMethodNodeCache.java b/teavm-core/src/main/java/org/teavm/javascript/EmptyRegularMethodNodeCache.java new file mode 100644 index 000000000..b08e93c94 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/EmptyRegularMethodNodeCache.java @@ -0,0 +1,34 @@ +/* + * 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.javascript; + +import org.teavm.javascript.ast.RegularMethodNode; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class EmptyRegularMethodNodeCache implements RegularMethodNodeCache { + @Override + public RegularMethodNode get(MethodReference methodReference) { + return null; + } + + @Override + public void store(MethodReference methodReference, RegularMethodNode node) { + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/InMemoryRegularMethodNodeCache.java b/teavm-core/src/main/java/org/teavm/javascript/InMemoryRegularMethodNodeCache.java new file mode 100644 index 000000000..330c2bdc7 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/InMemoryRegularMethodNodeCache.java @@ -0,0 +1,39 @@ +/* + * 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.javascript; + +import java.util.HashMap; +import java.util.Map; +import org.teavm.javascript.ast.RegularMethodNode; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class InMemoryRegularMethodNodeCache implements RegularMethodNodeCache { + private Map cache = new HashMap<>(); + + @Override + public RegularMethodNode get(MethodReference methodReference) { + return cache.get(methodReference); + } + + @Override + public void store(MethodReference methodReference, RegularMethodNode node) { + cache.put(methodReference, node); + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/RegularMethodNodeCache.java b/teavm-core/src/main/java/org/teavm/javascript/RegularMethodNodeCache.java new file mode 100644 index 000000000..62015c967 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/RegularMethodNodeCache.java @@ -0,0 +1,29 @@ +/* + * 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.javascript; + +import org.teavm.javascript.ast.RegularMethodNode; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public interface RegularMethodNodeCache { + RegularMethodNode get(MethodReference methodReference); + + void store(MethodReference methodReference, RegularMethodNode node); +} diff --git a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java index 3f7e9e061..7d62967d2 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java @@ -21,6 +21,9 @@ import org.apache.commons.io.IOUtils; import org.teavm.common.FiniteExecutor; import org.teavm.common.SimpleFiniteExecutor; import org.teavm.common.ThreadPoolFiniteExecutor; +import org.teavm.javascript.EmptyRegularMethodNodeCache; +import org.teavm.javascript.InMemoryRegularMethodNodeCache; +import org.teavm.javascript.RegularMethodNodeCache; import org.teavm.model.*; import org.teavm.parsing.ClasspathClassHolderSource; import org.teavm.testing.JUnitTestAdapter; @@ -48,6 +51,8 @@ public class TeaVMTestTool { private List testClasses = new ArrayList<>(); private ClassLoader classLoader = TeaVMTestTool.class.getClassLoader(); private TeaVMToolLog log = new EmptyTeaVMToolLog(); + private boolean incremental; + private RegularMethodNodeCache astCache; public File getOutputDir() { return outputDir; @@ -113,6 +118,14 @@ public class TeaVMTestTool { this.log = log; } + public boolean isIncremental() { + return incremental; + } + + public void setIncremental(boolean incremental) { + this.incremental = incremental; + } + public void generate() throws TeaVMToolException { Runnable finalizer = null; try { @@ -131,7 +144,8 @@ public class TeaVMTestTool { resourceToFile(prefix + "/res/toggle-small-expand.png", "res/toggle-small-expand.png"); resourceToFile(prefix + "/res/toggle-small.png", "res/toggle-small.png"); resourceToFile(prefix + "/junit.html", "junit.html"); - final ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader); + final ClassHolderSource classSource = new PreOptimizingClassHolderSource( + new ClasspathClassHolderSource(classLoader)); for (String testClass : testClasses) { ClassHolder classHolder = classSource.get(testClass); if (classHolder == null) { @@ -141,6 +155,10 @@ public class TeaVMTestTool { } includeAdditionalScripts(classLoader); + astCache = new EmptyRegularMethodNodeCache(); + if (incremental) { + astCache = new InMemoryRegularMethodNodeCache(); + } File allTestsFile = new File(outputDir, "tests/all.js"); try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) { allTestsWriter.write("prepare = function() {\n"); @@ -280,6 +298,8 @@ public class TeaVMTestTool { .setClassLoader(classLoader) .setClassSource(classSource) .build(); + vm.setIncremental(incremental); + vm.setAstCache(astCache); vm.setProperties(properties); vm.setMinifying(minifying); vm.installPlugins(); diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 8417befa6..107613c37 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -22,9 +22,7 @@ import org.teavm.common.ServiceRepository; import org.teavm.debugging.information.DebugInformationEmitter; import org.teavm.debugging.information.SourceLocation; import org.teavm.dependency.*; -import org.teavm.javascript.Decompiler; -import org.teavm.javascript.Renderer; -import org.teavm.javascript.RenderingException; +import org.teavm.javascript.*; import org.teavm.javascript.ast.ClassNode; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Injector; @@ -82,6 +80,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private Map, Object> services = new HashMap<>(); private Properties properties = new Properties(); private DebugInformationEmitter debugEmitter; + private RegularMethodNodeCache astCache = new EmptyRegularMethodNodeCache(); + private boolean incremental; TeaVM(ClassReaderSource classSource, ClassLoader classLoader) { this.classSource = classSource; @@ -164,6 +164,22 @@ public class TeaVM implements TeaVMHost, ServiceRepository { return new Properties(properties); } + public RegularMethodNodeCache getAstCache() { + return astCache; + } + + public void setAstCache(RegularMethodNodeCache methodAstCache) { + this.astCache = methodAstCache; + } + + public boolean isIncremental() { + return incremental; + } + + public void setIncremental(boolean incremental) { + this.incremental = incremental; + } + /** *

Adds an entry point. TeaVM guarantees, that all methods that are required by the entry point * will be available at run-time in browser. Also you need to specify for each parameter of entry point @@ -314,7 +330,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository { ListableClassHolderSource classSet = linker.link(dependencyChecker); // Optimize and allocate registers - devirtualize(classSet, dependencyChecker); + if (!incremental) { + devirtualize(classSet, dependencyChecker); + } ClassSetOptimizer optimizer = new ClassSetOptimizer(); optimizer.optimizeAll(classSet); allocateRegisters(classSet); @@ -328,6 +346,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { // Decompile Decompiler decompiler = new Decompiler(classSet, classLoader); + decompiler.setRegularMethodCache(incremental ? astCache : null); for (Map.Entry entry : methodGenerators.entrySet()) { decompiler.addGenerator(entry.getKey(), entry.getValue()); } diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java index 6702cc11c..bc8117e40 100644 --- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java +++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java @@ -89,6 +89,9 @@ public class BuildJavascriptTestMojo extends AbstractMojo { @Parameter private Properties properties; + @Parameter + private boolean incremental; + private TeaVMTestTool tool = new TeaVMTestTool(); public void setProject(MavenProject project) { @@ -135,6 +138,10 @@ public class BuildJavascriptTestMojo extends AbstractMojo { this.properties = properties; } + public void setIncremental(boolean incremental) { + this.incremental = incremental; + } + @Override public void execute() throws MojoExecutionException, MojoFailureException { if (System.getProperty("maven.test.skip", "false").equals("true") ||