diff --git a/core/src/main/java/org/teavm/ast/ClassNode.java b/core/src/main/java/org/teavm/ast/ClassNode.java deleted file mode 100644 index 642f10a4e..000000000 --- a/core/src/main/java/org/teavm/ast/ClassNode.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2016 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.ast; - -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import org.teavm.model.AccessLevel; -import org.teavm.model.ElementModifier; - -public class ClassNode { - private String name; - private String parentName; - private Set modifiers = EnumSet.noneOf(ElementModifier.class); - private AccessLevel accessLevel = AccessLevel.PUBLIC; - private List fields = new ArrayList<>(); - private List methods = new ArrayList<>(); - private List interfaces = new ArrayList<>(); - - public ClassNode(String name, String parentName) { - this.name = name; - this.parentName = parentName; - } - - public String getName() { - return name; - } - - public String getParentName() { - return parentName; - } - - public List getFields() { - return fields; - } - - public List getMethods() { - return methods; - } - - public List getInterfaces() { - return interfaces; - } - - public Set getModifiers() { - return modifiers; - } - - public AccessLevel getAccessLevel() { - return accessLevel; - } - - public void setAccessLevel(AccessLevel accessLevel) { - this.accessLevel = accessLevel; - } -} diff --git a/core/src/main/java/org/teavm/ast/ControlFlowEntry.java b/core/src/main/java/org/teavm/ast/ControlFlowEntry.java new file mode 100644 index 000000000..05b83bfe5 --- /dev/null +++ b/core/src/main/java/org/teavm/ast/ControlFlowEntry.java @@ -0,0 +1,28 @@ +/* + * Copyright 2019 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.ast; + +import org.teavm.model.TextLocation; + +public class ControlFlowEntry { + public final TextLocation from; + public final TextLocation[] to; + + public ControlFlowEntry(TextLocation from, TextLocation[] to) { + this.from = from; + this.to = to.clone(); + } +} diff --git a/core/src/main/java/org/teavm/ast/FieldNode.java b/core/src/main/java/org/teavm/ast/FieldNode.java deleted file mode 100644 index 7113f86c1..000000000 --- a/core/src/main/java/org/teavm/ast/FieldNode.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016 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.ast; - -import java.util.EnumSet; -import java.util.Set; -import org.teavm.model.ElementModifier; -import org.teavm.model.FieldReference; -import org.teavm.model.ValueType; - -public class FieldNode { - private FieldReference reference; - private ValueType type; - private Set modifiers = EnumSet.noneOf(ElementModifier.class); - private Object initialValue; - - public FieldNode(FieldReference reference, ValueType type) { - this.reference = reference; - this.type = type; - } - - public FieldReference getReference() { - return reference; - } - - public String getName() { - return reference.getFieldName(); - } - - public Set getModifiers() { - return modifiers; - } - - public ValueType getType() { - return type; - } - - public Object getInitialValue() { - return initialValue; - } - - public void setInitialValue(Object initialValue) { - this.initialValue = initialValue; - } -} diff --git a/core/src/main/java/org/teavm/ast/MethodNodeVisitor.java b/core/src/main/java/org/teavm/ast/MethodNodeVisitor.java index 0c51302b7..fee91d5a8 100644 --- a/core/src/main/java/org/teavm/ast/MethodNodeVisitor.java +++ b/core/src/main/java/org/teavm/ast/MethodNodeVisitor.java @@ -19,6 +19,4 @@ public interface MethodNodeVisitor { void visit(RegularMethodNode methodNode); void visit(AsyncMethodNode methodNode); - - void visit(NativeMethodNode methodNode); } diff --git a/core/src/main/java/org/teavm/ast/NativeMethodNode.java b/core/src/main/java/org/teavm/ast/NativeMethodNode.java deleted file mode 100644 index 6b931fad1..000000000 --- a/core/src/main/java/org/teavm/ast/NativeMethodNode.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 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.ast; - -import java.util.Collections; -import java.util.List; -import org.teavm.backend.javascript.spi.Generator; -import org.teavm.model.MethodReference; - -public class NativeMethodNode extends MethodNode { - private Generator generator; - private boolean async; - - public NativeMethodNode(MethodReference reference) { - super(reference); - } - - public Generator getGenerator() { - return generator; - } - - public void setGenerator(Generator generator) { - this.generator = generator; - } - - @Override - public boolean isAsync() { - return async; - } - - public void setAsync(boolean async) { - this.async = async; - } - - @Override - public void acceptVisitor(MethodNodeVisitor visitor) { - visitor.visit(this); - } - - @Override - public List getVariables() { - return Collections.emptyList(); - } -} diff --git a/core/src/main/java/org/teavm/ast/analysis/LocationGraphBuilder.java b/core/src/main/java/org/teavm/ast/analysis/LocationGraphBuilder.java index 175e87532..83795eed6 100644 --- a/core/src/main/java/org/teavm/ast/analysis/LocationGraphBuilder.java +++ b/core/src/main/java/org/teavm/ast/analysis/LocationGraphBuilder.java @@ -35,6 +35,7 @@ import org.teavm.ast.BreakStatement; import org.teavm.ast.ConditionalExpr; import org.teavm.ast.ConditionalStatement; import org.teavm.ast.ContinueStatement; +import org.teavm.ast.ControlFlowEntry; import org.teavm.ast.Expr; import org.teavm.ast.IdentifiedStatement; import org.teavm.ast.InitClassStatement; @@ -56,7 +57,7 @@ public final class LocationGraphBuilder { private LocationGraphBuilder() { } - public static Map build(Statement node) { + public static ControlFlowEntry[] build(Statement node) { Visitor visitor = new Visitor(); node.acceptVisitor(visitor); Graph graph = visitor.builder.build(); @@ -82,9 +83,10 @@ public final class LocationGraphBuilder { } } - Map result = new LinkedHashMap<>(); + ControlFlowEntry[] result = new ControlFlowEntry[builder.size()]; + int index = 0; for (Map.Entry> entry : builder.entrySet()) { - result.put(entry.getKey(), entry.getValue().toArray(new TextLocation[0])); + result[index++] = new ControlFlowEntry(entry.getKey(), entry.getValue().toArray(new TextLocation[0])); } return result; } diff --git a/core/src/main/java/org/teavm/ast/decompilation/Decompiler.java b/core/src/main/java/org/teavm/ast/decompilation/Decompiler.java index 2df701ff9..35a484398 100644 --- a/core/src/main/java/org/teavm/ast/decompilation/Decompiler.java +++ b/core/src/main/java/org/teavm/ast/decompilation/Decompiler.java @@ -18,24 +18,16 @@ package org.teavm.ast.decompilation; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodPart; import org.teavm.ast.BlockStatement; -import org.teavm.ast.ClassNode; -import org.teavm.ast.FieldNode; import org.teavm.ast.GotoPartStatement; import org.teavm.ast.IdentifiedStatement; -import org.teavm.ast.MethodNode; -import org.teavm.ast.NativeMethodNode; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.SequentialStatement; import org.teavm.ast.Statement; @@ -43,31 +35,19 @@ import org.teavm.ast.TryCatchStatement; import org.teavm.ast.VariableNode; import org.teavm.ast.WhileStatement; import org.teavm.ast.optimization.Optimizer; -import org.teavm.backend.javascript.spi.GeneratedBy; -import org.teavm.backend.javascript.spi.Generator; -import org.teavm.backend.javascript.spi.InjectedBy; -import org.teavm.cache.AstDependencyExtractor; -import org.teavm.cache.CacheStatus; -import org.teavm.cache.MethodNodeCache; import org.teavm.common.Graph; import org.teavm.common.GraphIndexer; import org.teavm.common.Loop; import org.teavm.common.LoopGraph; import org.teavm.common.RangeTree; -import org.teavm.interop.PlatformMarker; -import org.teavm.model.AnnotationHolder; import org.teavm.model.BasicBlock; -import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderSource; -import org.teavm.model.ElementModifier; -import org.teavm.model.FieldHolder; import org.teavm.model.Instruction; import org.teavm.model.MethodHolder; import org.teavm.model.MethodReference; import org.teavm.model.Program; import org.teavm.model.TextLocation; import org.teavm.model.TryCatchBlock; -import org.teavm.model.ValueType; import org.teavm.model.instructions.BinaryBranchingInstruction; import org.teavm.model.instructions.BranchingInstruction; import org.teavm.model.instructions.JumpInstruction; @@ -78,8 +58,6 @@ import org.teavm.model.util.TypeInferer; public class Decompiler { private ClassHolderSource classSource; - private ClassLoader classLoader; - private CacheStatus cacheStatus; private Graph graph; private LoopGraph loopGraph; private GraphIndexer indexer; @@ -91,43 +69,21 @@ public class Decompiler { private RangeTree codeTree; private RangeTree.Node currentNode; private RangeTree.Node parentNode; - private Map generators = new HashMap<>(); - private Set methodsToSkip = new HashSet<>(); - private MethodNodeCache regularMethodCache; - private Set asyncMethods; - private Set splitMethods = new HashSet<>(); + private Set splitMethods; private List tryCatchBookmarks = new ArrayList<>(); - private final AstDependencyExtractor astDependencyExtractor = new AstDependencyExtractor(); private Deque stack; private Program program; private boolean friendlyToDebugger; private boolean moveConstants; - public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, - CacheStatus cacheStatus, Set asyncMethods, Set asyncFamilyMethods, - boolean friendlyToDebugger, boolean moveConstants) { + public Decompiler(ClassHolderSource classSource, Set splitMethods, boolean friendlyToDebugger, + boolean moveConstants) { this.classSource = classSource; - this.classLoader = classLoader; - this.cacheStatus = cacheStatus; - this.asyncMethods = asyncMethods; - splitMethods.addAll(asyncMethods); - splitMethods.addAll(asyncFamilyMethods); + this.splitMethods = splitMethods; this.friendlyToDebugger = friendlyToDebugger; this.moveConstants = moveConstants; } - public MethodNodeCache getRegularMethodCache() { - return regularMethodCache; - } - - public void setRegularMethodCache(MethodNodeCache regularMethodCache) { - this.regularMethodCache = regularMethodCache; - } - - public int getGraphSize() { - return this.graph.size(); - } - static class Block { Block parent; int parentOffset; @@ -175,134 +131,7 @@ public class Decompiler { int exceptionHandler; } - public List decompile(Collection classNames) { - List sequence = new ArrayList<>(); - Set visited = new HashSet<>(); - for (String className : classNames) { - orderClasses(className, visited, sequence); - } - final List result = new ArrayList<>(); - for (int i = 0; i < sequence.size(); ++i) { - final String className = sequence.get(i); - result.add(decompile(classSource.get(className))); - } - return result; - } - - public List getClassOrdering(Collection classNames) { - List sequence = new ArrayList<>(); - Set visited = new HashSet<>(); - for (String className : classNames) { - orderClasses(className, visited, sequence); - } - return sequence; - } - - public void addGenerator(MethodReference method, Generator generator) { - generators.put(method, generator); - } - - public void addMethodToSkip(MethodReference method) { - methodsToSkip.add(method); - } - - private void orderClasses(String className, Set visited, List order) { - if (!visited.add(className)) { - return; - } - ClassHolder cls = classSource.get(className); - if (cls == null) { - return; - } - if (cls.getParent() != null) { - orderClasses(cls.getParent(), visited, order); - } - for (String iface : cls.getInterfaces()) { - orderClasses(iface, visited, order); - } - order.add(className); - } - - public ClassNode decompile(ClassHolder cls) { - ClassNode clsNode = new ClassNode(cls.getName(), cls.getParent()); - for (FieldHolder field : cls.getFields()) { - FieldNode fieldNode = new FieldNode(field.getReference(), field.getType()); - fieldNode.getModifiers().addAll(field.getModifiers()); - fieldNode.setInitialValue(field.getInitialValue()); - clsNode.getFields().add(fieldNode); - } - for (MethodHolder method : cls.getMethods()) { - if (method.getModifiers().contains(ElementModifier.ABSTRACT)) { - continue; - } - if ((!isBootstrap() && method.getAnnotations().get(InjectedBy.class.getName()) != null) - || methodsToSkip.contains(method.getReference())) { - continue; - } - if (!method.hasModifier(ElementModifier.NATIVE) && method.getProgram() == null) { - continue; - } - - MethodNode methodNode = decompile(method); - clsNode.getMethods().add(methodNode); - } - clsNode.getInterfaces().addAll(cls.getInterfaces()); - clsNode.getModifiers().addAll(cls.getModifiers()); - clsNode.setAccessLevel(cls.getLevel()); - return clsNode; - } - - public MethodNode decompile(MethodHolder method) { - return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method) - : !asyncMethods.contains(method.getReference()) ? decompileRegular(method) : decompileAsync(method); - } - - public NativeMethodNode decompileNative(MethodHolder method) { - Generator generator = generators.get(method.getReference()); - if (generator == null && !isBootstrap()) { - AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName()); - if (annotHolder == null) { - throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor() - + " is native, but no " + GeneratedBy.class.getName() + " annotation found"); - } - ValueType annotValue = annotHolder.getValues().get("value").getJavaClass(); - String generatorClassName = ((ValueType.Object) annotValue).getClassName(); - try { - Class generatorClass = Class.forName(generatorClassName, true, classLoader); - generator = (Generator) generatorClass.newInstance(); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { - throw new DecompilationException("Error instantiating generator " + generatorClassName - + " for native method " + method.getOwnerName() + "." + method.getDescriptor()); - } - } - NativeMethodNode methodNode = new NativeMethodNode(method.getReference()); - methodNode.getModifiers().addAll(method.getModifiers()); - methodNode.setGenerator(generator); - methodNode.setAsync(asyncMethods.contains(method.getReference())); - return methodNode; - } - - @PlatformMarker - private static boolean isBootstrap() { - return false; - } - public RegularMethodNode decompileRegular(MethodHolder method) { - if (regularMethodCache == null) { - return decompileRegularCacheMiss(method); - } - RegularMethodNode node = !cacheStatus.isStaleMethod(method.getReference()) - ? regularMethodCache.get(method.getReference(), cacheStatus) - : null; - if (node == null) { - node = decompileRegularCacheMiss(method); - RegularMethodNode finalNode = node; - regularMethodCache.store(method.getReference(), node, () -> astDependencyExtractor.extract(finalNode)); - } - return node; - } - - public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) { RegularMethodNode methodNode = new RegularMethodNode(method.getReference()); Program program = method.getProgram(); int[] targetBlocks = new int[program.basicBlockCount()]; @@ -331,21 +160,6 @@ public class Decompiler { } public AsyncMethodNode decompileAsync(MethodHolder method) { - if (regularMethodCache == null) { - return decompileAsyncCacheMiss(method); - } - AsyncMethodNode node = !cacheStatus.isStaleMethod(method.getReference()) - ? regularMethodCache.getAsync(method.getReference(), cacheStatus) - : null; - if (node == null) { - node = decompileAsyncCacheMiss(method); - AsyncMethodNode finalNode = node; - regularMethodCache.storeAsync(method.getReference(), node, () -> astDependencyExtractor.extract(finalNode)); - } - return node; - } - - private AsyncMethodNode decompileAsyncCacheMiss(MethodHolder method) { AsyncMethodNode node = new AsyncMethodNode(method.getReference()); AsyncProgramSplitter splitter = new AsyncProgramSplitter(classSource, splitMethods); splitter.split(method.getProgram()); @@ -461,7 +275,6 @@ public class Decompiler { block.body.addAll(generator.statements); } - List deferred = new ArrayList<>(); while (block.end == i + 1) { Block oldBlock = block; stack.pop(); @@ -493,9 +306,6 @@ public class Decompiler { oldBlock.tryCatches.clear(); oldBlock.removeFrom(blockMap); } - for (Runnable r : deferred) { - r.run(); - } if (generator.nextBlock != null && !isTrivialBlock(generator.nextBlock)) { closeExpiredBookmarks(generator, generator.nextBlock.getTryCatchBlocks()); diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index ea94f0ff0..575ec5a5f 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -61,7 +61,6 @@ import org.teavm.backend.c.intrinsic.PlatformObjectIntrinsic; import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic; import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic; import org.teavm.backend.c.intrinsic.StructureIntrinsic; -import org.teavm.cache.AlwaysStaleCacheStatus; import org.teavm.dependency.ClassDependency; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyListener; @@ -219,8 +218,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { TagRegistry tagRegistry = new TagRegistry(classes); StringPool stringPool = new StringPool(); - Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), - AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true); + Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true); Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource()); NameProvider nameProvider = new NameProvider(controller.getUnprocessedClassSource()); diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index f902c4a2e..f25635064 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -34,11 +34,11 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; -import org.teavm.ast.ClassNode; -import org.teavm.ast.MethodNode; +import org.teavm.ast.AsyncMethodNode; +import org.teavm.ast.ControlFlowEntry; import org.teavm.ast.RegularMethodNode; -import org.teavm.ast.Statement; import org.teavm.ast.analysis.LocationGraphBuilder; +import org.teavm.ast.decompilation.DecompilationException; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.javascript.codegen.AliasProvider; import org.teavm.backend.javascript.codegen.DefaultAliasProvider; @@ -46,6 +46,8 @@ import org.teavm.backend.javascript.codegen.DefaultNamingStrategy; import org.teavm.backend.javascript.codegen.MinifyingAliasProvider; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.codegen.SourceWriterBuilder; +import org.teavm.backend.javascript.decompile.PreparedClass; +import org.teavm.backend.javascript.decompile.PreparedMethod; import org.teavm.backend.javascript.rendering.Renderer; import org.teavm.backend.javascript.rendering.RenderingContext; import org.teavm.backend.javascript.rendering.RuntimeRenderer; @@ -55,6 +57,9 @@ import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.backend.javascript.spi.Injector; import org.teavm.backend.javascript.spi.VirtualMethodContributor; import org.teavm.backend.javascript.spi.VirtualMethodContributorContext; +import org.teavm.cache.AstCacheEntry; +import org.teavm.cache.AstDependencyExtractor; +import org.teavm.cache.CacheStatus; import org.teavm.cache.EmptyMethodNodeCache; import org.teavm.cache.MethodNodeCache; import org.teavm.debugging.information.DebugInformationEmitter; @@ -68,9 +73,11 @@ import org.teavm.dependency.DependencyType; import org.teavm.dependency.MethodDependency; import org.teavm.interop.PlatformMarker; import org.teavm.interop.PlatformMarkers; +import org.teavm.model.AnnotationHolder; import org.teavm.model.BasicBlock; import org.teavm.model.CallLocation; import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; @@ -120,6 +127,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private ClassInitializerInsertionTransformer clinitInsertionTransformer; private List customVirtualMethods = new ArrayList<>(); private int topLevelNameLimit = 10000; + private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor(); @Override public List getTransformers() { @@ -320,7 +328,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) { - List clsNodes = modelToAst(classes); + List clsNodes = modelToAst(classes); if (controller.wasCancelled()) { return; } @@ -351,16 +359,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { renderer.setMinifying(minifying); renderer.setProgressConsumer(controller::reportProgress); if (debugEmitter != null) { - for (ClassNode classNode : clsNodes) { - ClassHolder cls = classes.get(classNode.getName()); - for (MethodNode methodNode : classNode.getMethods()) { - if (methodNode instanceof RegularMethodNode) { - emitCFG(debugEmitter, ((RegularMethodNode) methodNode).getBody()); - } else { - MethodHolder method = cls.getMethod(methodNode.getReference().getDescriptor()); - if (method != null && method.getProgram() != null) { - emitCFG(debugEmitter, method.getProgram()); - } + for (PreparedClass preparedClass : clsNodes) { + for (PreparedMethod preparedMethod : preparedClass.getMethods()) { + if (preparedMethod.cfg != null) { + emitCFG(debugEmitter, preparedMethod.cfg); } } if (controller.wasCancelled()) { @@ -462,44 +464,155 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { return STATS_NUM_FORMAT.format(size) + " (" + STATS_PERCENT_FORMAT.format((double) size / totalSize) + ")"; } - static class PackageNode { - String name; - Map children = new HashMap<>(); - } - - private List modelToAst(ListableClassHolderSource classes) { + private List modelToAst(ListableClassHolderSource classes) { AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(), controller.getDiagnostics()); asyncFinder.find(classes); asyncMethods.addAll(asyncFinder.getAsyncMethods()); asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods()); + Set splitMethods = new HashSet<>(asyncMethods); + splitMethods.addAll(asyncFamilyMethods); - Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), controller.getCacheStatus(), - asyncMethods, asyncFamilyMethods, controller.isFriendlyToDebugger(), false); - decompiler.setRegularMethodCache(astCache); + Decompiler decompiler = new Decompiler(classes, splitMethods, controller.isFriendlyToDebugger(), false); - for (Map.Entry entry : methodGenerators.entrySet()) { - decompiler.addGenerator(entry.getKey(), entry.getValue()); - } - for (MethodReference injectedMethod : methodInjectors.keySet()) { - decompiler.addMethodToSkip(injectedMethod); - } - List classOrder = decompiler.getClassOrdering(classes.getClassNames()); - List classNodes = new ArrayList<>(); - for (String className : classOrder) { + List classNodes = new ArrayList<>(); + for (String className : getClassOrdering(classes)) { ClassHolder cls = classes.get(className); for (MethodHolder method : cls.getMethods()) { - preprocessNativeMethod(method, decompiler); + preprocessNativeMethod(method); if (controller.wasCancelled()) { break; } } - classNodes.add(decompiler.decompile(cls)); + classNodes.add(decompile(decompiler, cls)); } return classNodes; } - private void preprocessNativeMethod(MethodHolder method, Decompiler decompiler) { + private List getClassOrdering(ListableClassHolderSource classes) { + List sequence = new ArrayList<>(); + Set visited = new HashSet<>(); + for (String className : classes.getClassNames()) { + orderClasses(classes, className, visited, sequence); + } + return sequence; + } + + private void orderClasses(ClassHolderSource classes, String className, Set visited, List order) { + if (!visited.add(className)) { + return; + } + ClassHolder cls = classes.get(className); + if (cls == null) { + return; + } + if (cls.getParent() != null) { + orderClasses(classes, cls.getParent(), visited, order); + } + for (String iface : cls.getInterfaces()) { + orderClasses(classes, iface, visited, order); + } + order.add(className); + } + + private PreparedClass decompile(Decompiler decompiler, ClassHolder cls) { + PreparedClass clsNode = new PreparedClass(cls); + for (MethodHolder method : cls.getMethods()) { + if (method.getModifiers().contains(ElementModifier.ABSTRACT)) { + continue; + } + if ((!isBootstrap() && method.getAnnotations().get(InjectedBy.class.getName()) != null) + || methodInjectors.containsKey(method.getReference())) { + continue; + } + if (!method.hasModifier(ElementModifier.NATIVE) && method.getProgram() == null) { + continue; + } + + PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE) + ? decompileNative(method) + : decompile(decompiler, method); + clsNode.getMethods().add(preparedMethod); + } + return clsNode; + } + + private PreparedMethod decompileNative(MethodHolder method) { + MethodReference reference = method.getReference(); + Generator generator = methodGenerators.get(reference); + if (generator == null && !isBootstrap()) { + AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName()); + if (annotHolder == null) { + throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor() + + " is native, but no " + GeneratedBy.class.getName() + " annotation found"); + } + ValueType annotValue = annotHolder.getValues().get("value").getJavaClass(); + String generatorClassName = ((ValueType.Object) annotValue).getClassName(); + try { + Class generatorClass = Class.forName(generatorClassName, true, controller.getClassLoader()); + generator = (Generator) generatorClass.newInstance(); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + throw new DecompilationException("Error instantiating generator " + generatorClassName + + " for native method " + method.getOwnerName() + "." + method.getDescriptor()); + } + } + + return new PreparedMethod(method, null, generator, asyncMethods.contains(reference), null); + } + + private PreparedMethod decompile(Decompiler decompiler, MethodHolder method) { + MethodReference reference = method.getReference(); + if (asyncMethods.contains(reference)) { + AsyncMethodNode node = decompileAsync(decompiler, method); + ControlFlowEntry[] cfg = ProgramUtils.getLocationCFG(method.getProgram()); + return new PreparedMethod(method, node, null, false, cfg); + } else { + AstCacheEntry entry = decompileRegular(decompiler, method); + return new PreparedMethod(method, entry.method, null, false, entry.cfg); + } + } + + private AstCacheEntry decompileRegular(Decompiler decompiler, MethodHolder method) { + if (astCache == null) { + return decompileRegularCacheMiss(decompiler, method); + } + + CacheStatus cacheStatus = controller.getCacheStatus(); + AstCacheEntry entry = !cacheStatus.isStaleMethod(method.getReference()) + ? astCache.get(method.getReference(), cacheStatus) + : null; + if (entry == null) { + entry = decompileRegularCacheMiss(decompiler, method); + RegularMethodNode finalNode = entry.method; + astCache.store(method.getReference(), entry, () -> dependencyExtractor.extract(finalNode)); + } + return entry; + } + + private AstCacheEntry decompileRegularCacheMiss(Decompiler decompiler, MethodHolder method) { + RegularMethodNode node = decompiler.decompileRegular(method); + ControlFlowEntry[] cfg = LocationGraphBuilder.build(node.getBody()); + return new AstCacheEntry(node, cfg); + } + + private AsyncMethodNode decompileAsync(Decompiler decompiler, MethodHolder method) { + if (astCache == null) { + return decompiler.decompileAsync(method); + } + + CacheStatus cacheStatus = controller.getCacheStatus(); + AsyncMethodNode node = !cacheStatus.isStaleMethod(method.getReference()) + ? astCache.getAsync(method.getReference(), cacheStatus) + : null; + if (node == null) { + node = decompiler.decompileAsync(method); + AsyncMethodNode finalNode = node; + astCache.storeAsync(method.getReference(), node, () -> dependencyExtractor.extract(finalNode)); + } + return node; + } + + private void preprocessNativeMethod(MethodHolder method) { if (!method.getModifiers().contains(ElementModifier.NATIVE) || methodGenerators.get(method.getReference()) != null || methodInjectors.get(method.getReference()) != null) { @@ -512,7 +625,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { Generator generator = provider.apply(context); if (generator != null) { methodGenerators.put(method.getReference(), generator); - decompiler.addGenerator(method.getReference(), generator); found = true; break; } @@ -521,7 +633,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { Injector injector = provider.apply(context); if (injector != null) { methodInjectors.put(method.getReference(), injector); - decompiler.addMethodToSkip(method.getReference()); found = true; break; } @@ -597,20 +708,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { return false; } - private void emitCFG(DebugInformationEmitter emitter, Program program) { - emitCFG(emitter, ProgramUtils.getLocationCFG(program)); - } - - private void emitCFG(DebugInformationEmitter emitter, Statement program) { - emitCFG(emitter, LocationGraphBuilder.build(program)); - } - - private void emitCFG(DebugInformationEmitter emitter, Map cfg) { - for (Map.Entry entry : cfg.entrySet()) { - SourceLocation location = map(entry.getKey()); - SourceLocation[] successors = new SourceLocation[entry.getValue().length]; - for (int i = 0; i < entry.getValue().length; ++i) { - successors[i] = map(entry.getValue()[i]); + private void emitCFG(DebugInformationEmitter emitter, ControlFlowEntry[] cfg) { + for (ControlFlowEntry entry : cfg) { + SourceLocation location = map(entry.from); + SourceLocation[] successors = new SourceLocation[entry.to.length]; + for (int i = 0; i < entry.to.length; ++i) { + successors[i] = map(entry.to[i]); } emitter.addSuccessors(location, successors); } diff --git a/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedClass.java b/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedClass.java new file mode 100644 index 000000000..52c433ff4 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedClass.java @@ -0,0 +1,45 @@ +/* + * Copyright 2019 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.backend.javascript.decompile; + +import java.util.ArrayList; +import java.util.List; +import org.teavm.model.ClassHolder; + +public class PreparedClass { + private ClassHolder classHolder; + private List methods = new ArrayList<>(); + + public PreparedClass(ClassHolder classHolder) { + this.classHolder = classHolder; + } + + public String getName() { + return classHolder.getName(); + } + + public String getParentName() { + return classHolder.getParent(); + } + + public List getMethods() { + return methods; + } + + public ClassHolder getClassHolder() { + return classHolder; + } +} diff --git a/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedMethod.java b/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedMethod.java new file mode 100644 index 000000000..b41ba427f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/decompile/PreparedMethod.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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.backend.javascript.decompile; + +import org.teavm.ast.ControlFlowEntry; +import org.teavm.ast.MethodNode; +import org.teavm.backend.javascript.spi.Generator; +import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReference; + +public class PreparedMethod { + public final MethodHolder methodHolder; + public final MethodReference reference; + public final MethodNode node; + public final Generator generator; + public final boolean async; + public final ControlFlowEntry[] cfg; + + public PreparedMethod(MethodHolder method, MethodNode node, Generator generator, boolean async, + ControlFlowEntry[] cfg) { + this.reference = method.getReference(); + this.methodHolder = method; + this.node = node; + this.generator = generator; + this.async = async; + this.cfg = cfg; + } +} diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java index 90595e885..aedf1b2ba 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java @@ -20,17 +20,13 @@ import org.teavm.ast.AssignmentStatement; import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodPart; import org.teavm.ast.BinaryExpr; -import org.teavm.ast.ClassNode; import org.teavm.ast.ConstantExpr; -import org.teavm.ast.FieldNode; import org.teavm.ast.InitClassStatement; import org.teavm.ast.InstanceOfExpr; import org.teavm.ast.InvocationExpr; -import org.teavm.ast.MethodNode; import org.teavm.ast.MethodNodeVisitor; import org.teavm.ast.MonitorEnterStatement; import org.teavm.ast.MonitorExitStatement; -import org.teavm.ast.NativeMethodNode; import org.teavm.ast.NewArrayExpr; import org.teavm.ast.NewExpr; import org.teavm.ast.NewMultiArrayExpr; @@ -42,9 +38,12 @@ import org.teavm.ast.ThrowStatement; import org.teavm.ast.TryCatchStatement; import org.teavm.ast.UnaryExpr; import org.teavm.backend.javascript.codegen.NameFrequencyConsumer; +import org.teavm.backend.javascript.decompile.PreparedClass; +import org.teavm.backend.javascript.decompile.PreparedMethod; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; +import org.teavm.model.FieldHolder; import org.teavm.model.FieldReference; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; @@ -75,13 +74,13 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit this.asyncFamilyMethods = asyncFamilyMethods; } - public void estimate(ClassNode cls) { + public void estimate(PreparedClass cls) { // Declaration consumer.consume(cls.getName()); if (cls.getParentName() != null) { consumer.consume(cls.getParentName()); } - for (FieldNode field : cls.getFields()) { + for (FieldHolder field : cls.getClassHolder().getFields()) { consumer.consume(new FieldReference(cls.getName(), field.getName())); if (field.getModifiers().contains(ElementModifier.STATIC)) { consumer.consume(cls.getName()); @@ -91,26 +90,29 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit // Methods MethodReader clinit = classSource.get(cls.getName()).getMethod( new MethodDescriptor("", ValueType.VOID)); - for (MethodNode method : cls.getMethods()) { - consumer.consume(method.getReference()); - if (asyncFamilyMethods.contains(method.getReference())) { - consumer.consume(method.getReference()); + for (PreparedMethod method : cls.getMethods()) { + consumer.consume(method.reference); + if (asyncFamilyMethods.contains(method.reference)) { + consumer.consume(method.reference); } - if (clinit != null && (method.getModifiers().contains(ElementModifier.STATIC) - || method.getReference().getName().equals(""))) { - consumer.consume(method.getReference()); + if (clinit != null && (method.methodHolder.getModifiers().contains(ElementModifier.STATIC) + || method.reference.getName().equals(""))) { + consumer.consume(method.reference); } - if (!method.getModifiers().contains(ElementModifier.STATIC)) { - consumer.consume(method.getReference().getDescriptor()); - consumer.consume(method.getReference()); + if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) { + consumer.consume(method.reference.getDescriptor()); + consumer.consume(method.reference); } - if (method.isAsync()) { + if (method.async) { consumer.consumeFunction("$rt_nativeThread"); consumer.consumeFunction("$rt_nativeThread"); consumer.consumeFunction("$rt_resuming"); consumer.consumeFunction("$rt_invalidPointer"); } - method.acceptVisitor(this); + + if (method.node != null) { + method.node.acceptVisitor(this); + } } if (clinit != null) { @@ -123,7 +125,7 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit if (cls.getParentName() != null) { consumer.consume(cls.getParentName()); } - for (String iface : cls.getInterfaces()) { + for (String iface : cls.getClassHolder().getInterfaces()) { consumer.consume(iface); } } @@ -142,10 +144,6 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit } } - @Override - public void visit(NativeMethodNode methodNode) { - } - @Override public void visit(AssignmentStatement statement) { super.visit(statement); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index a68a30968..d46fb0108 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -31,17 +31,16 @@ import java.util.function.IntFunction; import java.util.stream.Collectors; import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodPart; -import org.teavm.ast.ClassNode; -import org.teavm.ast.FieldNode; import org.teavm.ast.MethodNode; import org.teavm.ast.MethodNodeVisitor; -import org.teavm.ast.NativeMethodNode; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.VariableNode; import org.teavm.backend.javascript.codegen.NamingOrderer; import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.codegen.ScopedName; import org.teavm.backend.javascript.codegen.SourceWriter; +import org.teavm.backend.javascript.decompile.PreparedClass; +import org.teavm.backend.javascript.decompile.PreparedMethod; import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.common.ServiceRepository; import org.teavm.debugging.information.DebugInformationEmitter; @@ -52,6 +51,7 @@ import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; +import org.teavm.model.FieldHolder; import org.teavm.model.FieldReference; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodDescriptor; @@ -262,12 +262,12 @@ public class Renderer implements RenderingManager { writer.newLine(); } - public void prepare(List classes) { + public void prepare(List classes) { if (minifying) { NamingOrderer orderer = new NamingOrderer(); NameFrequencyEstimator estimator = new NameFrequencyEstimator(orderer, classSource, asyncMethods, asyncFamilyMethods); - for (ClassNode cls : classes) { + for (PreparedClass cls : classes) { estimator.estimate(cls); } naming.getScopeName(); @@ -275,7 +275,7 @@ public class Renderer implements RenderingManager { } } - public boolean render(List classes) throws RenderingException { + public boolean render(List classes) throws RenderingException { if (minifying) { try { renderRuntimeAliases(); @@ -284,7 +284,7 @@ public class Renderer implements RenderingManager { } } int index = 0; - for (ClassNode cls : classes) { + for (PreparedClass cls : classes) { int start = writer.getOffset(); renderDeclaration(cls); renderMethodBodies(cls); @@ -297,7 +297,7 @@ public class Renderer implements RenderingManager { return true; } - private void renderDeclaration(ClassNode cls) throws RenderingException { + private void renderDeclaration(PreparedClass cls) throws RenderingException { ScopedName jsName = naming.getNameFor(cls.getName()); debugEmitter.addClass(jsName.value, cls.getName(), cls.getParentName()); try { @@ -305,9 +305,9 @@ public class Renderer implements RenderingManager { writer.append("()").ws().append("{") .indent().softNewLine(); boolean thisAliased = false; - List nonStaticFields = new ArrayList<>(); - List staticFields = new ArrayList<>(); - for (FieldNode field : cls.getFields()) { + List nonStaticFields = new ArrayList<>(); + List staticFields = new ArrayList<>(); + for (FieldHolder field : cls.getClassHolder().getFields()) { if (field.getModifiers().contains(ElementModifier.STATIC)) { staticFields.add(field); } else { @@ -318,11 +318,12 @@ public class Renderer implements RenderingManager { thisAliased = true; writer.append("var a").ws().append("=").ws().append("this;").ws(); } - if (!cls.getModifiers().contains(ElementModifier.INTERFACE) && cls.getParentName() != null) { + if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE) + && cls.getParentName() != null) { writer.appendClass(cls.getParentName()).append(".call(").append(thisAliased ? "a" : "this") .append(");").softNewLine(); } - for (FieldNode field : nonStaticFields) { + for (FieldHolder field : nonStaticFields) { Object value = field.getInitialValue(); if (value == null) { value = getDefaultValue(field.getType()); @@ -345,7 +346,7 @@ public class Renderer implements RenderingManager { } writer.newLine(); - for (FieldNode field : staticFields) { + for (FieldHolder field : staticFields) { Object value = field.getInitialValue(); if (value == null) { value = getDefaultValue(field.getType()); @@ -372,7 +373,7 @@ public class Renderer implements RenderingManager { } } - private void renderMethodBodies(ClassNode cls) throws RenderingException { + private void renderMethodBodies(PreparedClass cls) throws RenderingException { debugEmitter.emitClass(cls.getName()); try { MethodReader clinit = classSource.get(cls.getName()).getMethod( @@ -381,17 +382,17 @@ public class Renderer implements RenderingManager { if (clinit != null) { renderCallClinit(clinit, cls); } - if (!cls.getModifiers().contains(ElementModifier.INTERFACE)) { - for (MethodNode method : cls.getMethods()) { - if (!method.getModifiers().contains(ElementModifier.STATIC)) { - if (method.getReference().getName().equals("")) { + if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE)) { + for (PreparedMethod method : cls.getMethods()) { + if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) { + if (method.reference.getName().equals("")) { renderInitializer(method); } } } } - for (MethodNode method : cls.getMethods()) { + for (PreparedMethod method : cls.getMethods()) { renderBody(method); } } catch (IOException e) { @@ -400,7 +401,7 @@ public class Renderer implements RenderingManager { debugEmitter.emitClass(null); } - private void renderCallClinit(MethodReader clinit, ClassNode cls) + private void renderCallClinit(MethodReader clinit, PreparedClass cls) throws IOException { boolean isAsync = asyncMethods.contains(clinit.getReference()); @@ -464,13 +465,13 @@ public class Renderer implements RenderingManager { writer.newLine(); } - private void renderEraseClinit(ClassNode cls) throws IOException { + private void renderEraseClinit(PreparedClass cls) throws IOException { writer.appendClassInit(cls.getName()).ws().append("=").ws() .appendFunction("$rt_eraseClinit").append("(") .appendClass(cls.getName()).append(");").softNewLine(); } - private void renderClassMetadata(List classes) { + private void renderClassMetadata(List classes) { if (classes.isEmpty()) { return; } @@ -495,11 +496,11 @@ public class Renderer implements RenderingManager { metadataSize = writer.getOffset() - start; } - private void renderClassMetadataPortion(List classes, ObjectIntMap packageIndexes, + private void renderClassMetadataPortion(List classes, ObjectIntMap packageIndexes, Set classesRequiringName) throws IOException { writer.append("$rt_metadata(["); boolean first = true; - for (ClassNode cls : classes) { + for (PreparedClass cls : classes) { if (!first) { writer.append(',').softNewLine(); } @@ -526,8 +527,9 @@ public class Renderer implements RenderingManager { } writer.append(',').ws(); writer.append("["); - for (int i = 0; i < cls.getInterfaces().size(); ++i) { - String iface = cls.getInterfaces().get(i); + List interfaces = new ArrayList<>(cls.getClassHolder().getInterfaces()); + for (int i = 0; i < interfaces.size(); ++i) { + String iface = interfaces.get(i); if (i > 0) { writer.append(",").ws(); } @@ -535,8 +537,8 @@ public class Renderer implements RenderingManager { } writer.append("],").ws(); - writer.append(ElementModifier.pack(cls.getModifiers())).append(',').ws(); - writer.append(cls.getAccessLevel().ordinal()).append(',').ws(); + writer.append(ElementModifier.pack(cls.getClassHolder().getModifiers())).append(',').ws(); + writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws(); MethodReader clinit = classSource.get(cls.getName()).getMethod( new MethodDescriptor("", ValueType.VOID)); @@ -548,9 +550,9 @@ public class Renderer implements RenderingManager { writer.append(',').ws(); List virtualMethods = new ArrayList<>(); - for (MethodNode method : cls.getMethods()) { - if (!method.getModifiers().contains(ElementModifier.STATIC)) { - virtualMethods.add(method.getReference()); + for (PreparedMethod method : cls.getMethods()) { + if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) { + virtualMethods.add(method.reference); } } collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods); @@ -561,11 +563,11 @@ public class Renderer implements RenderingManager { writer.append("]);").newLine(); } - private ObjectIntMap generatePackageMetadata(List classes, Set classesRequiringName) + private ObjectIntMap generatePackageMetadata(List classes, Set classesRequiringName) throws IOException { PackageNode root = new PackageNode(null); - for (ClassNode classNode : classes) { + for (PreparedClass classNode : classes) { String className = classNode.getName(); if (!classesRequiringName.contains(className)) { continue; @@ -704,8 +706,8 @@ public class Renderer implements RenderingManager { return null; } - private void renderInitializer(MethodNode method) throws IOException { - MethodReference ref = method.getReference(); + private void renderInitializer(PreparedMethod method) throws IOException { + MethodReference ref = method.reference; debugEmitter.emitMethod(ref.getDescriptor()); ScopedName name = naming.getNameForInit(ref); renderFunctionDeclaration(name); @@ -789,18 +791,17 @@ public class Renderer implements RenderingManager { writer.append(");").ws().append("}"); } - private void renderBody(MethodNode method) throws IOException { + private void renderBody(PreparedMethod method) throws IOException { StatementRenderer statementRenderer = new StatementRenderer(context, writer); - statementRenderer.setCurrentMethod(method); - MethodReference ref = method.getReference(); + MethodReference ref = method.reference; debugEmitter.emitMethod(ref.getDescriptor()); ScopedName name = naming.getFullNameFor(ref); renderFunctionDeclaration(name); writer.append("("); int startParam = 0; - if (method.getModifiers().contains(ElementModifier.STATIC)) { + if (method.methodHolder.getModifiers().contains(ElementModifier.STATIC)) { startParam = 1; } for (int i = startParam; i <= ref.parameterCount(); ++i) { @@ -811,7 +812,14 @@ public class Renderer implements RenderingManager { } writer.append(")").ws().append("{").softNewLine().indent(); - method.acceptVisitor(new MethodBodyRenderer(statementRenderer)); + MethodBodyRenderer renderer = new MethodBodyRenderer(statementRenderer); + statementRenderer.setCurrentMethod(method.node); + if (method.node != null) { + method.node.acceptVisitor(renderer); + } else { + renderer.renderNative(method); + } + writer.outdent().append("}"); if (name.scoped) { writer.append(";"); @@ -858,12 +866,11 @@ public class Renderer implements RenderingManager { return context.getDependencyInfo(); } - @Override - public void visit(NativeMethodNode methodNode) { + public void renderNative(PreparedMethod method) { try { - this.async = methodNode.isAsync(); - statementRenderer.setAsync(methodNode.isAsync()); - methodNode.getGenerator().generate(this, writer, methodNode.getReference()); + this.async = method.async; + statementRenderer.setAsync(method.async); + method.generator.generate(this, writer, method.reference); } catch (IOException e) { throw new RenderingException("IO error occurred", e); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index a69c07cea..65eff3dc8 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -497,7 +497,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { private String generateVariableName(int index) { if (!minifying) { - VariableNode variable = index < currentMethod.getVariables().size() + VariableNode variable = currentMethod != null && index < currentMethod.getVariables().size() ? currentMethod.getVariables().get(index) : null; if (variable != null && variable.getName() != null) { diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index fb8f4df19..477dc9899 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -91,7 +91,6 @@ import org.teavm.backend.wasm.render.WasmCRenderer; import org.teavm.backend.wasm.render.WasmRenderer; import org.teavm.backend.wasm.transformation.IndirectCallTraceTransformation; import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation; -import org.teavm.cache.AlwaysStaleCacheStatus; import org.teavm.common.ServiceRepository; import org.teavm.dependency.ClassDependency; import org.teavm.dependency.DependencyAnalyzer; @@ -325,8 +324,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(), vtableProvider, tagRegistry, binaryWriter, names); - Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), - AlwaysStaleCacheStatus.INSTANCE, new HashSet<>(), new HashSet<>(), false, true); + Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true); WasmStringPool stringPool = classGenerator.getStringPool(); WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(), vtableProvider, tagRegistry, stringPool, names); diff --git a/core/src/main/java/org/teavm/cache/AstCacheEntry.java b/core/src/main/java/org/teavm/cache/AstCacheEntry.java new file mode 100644 index 000000000..384b9624d --- /dev/null +++ b/core/src/main/java/org/teavm/cache/AstCacheEntry.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 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 org.teavm.ast.ControlFlowEntry; +import org.teavm.ast.RegularMethodNode; + +public class AstCacheEntry { + public RegularMethodNode method; + public ControlFlowEntry[] cfg; + + public AstCacheEntry(RegularMethodNode method, ControlFlowEntry[] cfg) { + this.method = method; + this.cfg = cfg; + } +} diff --git a/core/src/main/java/org/teavm/cache/AstIO.java b/core/src/main/java/org/teavm/cache/AstIO.java index 2aba47f03..53e9429b7 100644 --- a/core/src/main/java/org/teavm/cache/AstIO.java +++ b/core/src/main/java/org/teavm/cache/AstIO.java @@ -36,6 +36,7 @@ import org.teavm.ast.ConditionalExpr; import org.teavm.ast.ConditionalStatement; import org.teavm.ast.ConstantExpr; import org.teavm.ast.ContinueStatement; +import org.teavm.ast.ControlFlowEntry; import org.teavm.ast.Expr; import org.teavm.ast.ExprVisitor; import org.teavm.ast.GotoPartStatement; @@ -91,6 +92,23 @@ public class AstIO { this.fileTable = fileTable; } + public void write(DataOutput output, ControlFlowEntry[] cfg) throws IOException { + output.writeShort(cfg.length); + for (ControlFlowEntry entry : cfg) { + writeLocation(output, entry.from); + output.writeShort(entry.to.length); + for (TextLocation loc : entry.to) { + if (loc != null) { + output.writeByte(1); + writeLocation(output, loc); + } else { + output.writeByte(0); + } + + } + } + } + public void write(DataOutput output, RegularMethodNode method) throws IOException { output.writeInt(ElementModifier.pack(method.getModifiers())); output.writeShort(method.getVariables().size()); @@ -110,6 +128,23 @@ public class AstIO { output.writeUTF(variable.getName() != null ? variable.getName() : ""); } + public ControlFlowEntry[] readControlFlow(DataInput input) throws IOException { + short size = input.readShort(); + ControlFlowEntry[] result = new ControlFlowEntry[size]; + for (int i = 0; i < size; ++i) { + TextLocation from = readLocation(input); + int toSize = input.readShort(); + TextLocation[] to = new TextLocation[toSize]; + for (int j = 0; j < toSize; ++j) { + if (input.readByte() != 0) { + to[j] = readLocation(input); + } + } + result[i] = new ControlFlowEntry(from, to); + } + return result; + } + public RegularMethodNode read(DataInput input, MethodReference method) throws IOException { RegularMethodNode node = new RegularMethodNode(method); node.getModifiers().addAll(unpackModifiers(input.readInt())); @@ -177,6 +212,15 @@ public class AstIO { return modifiers; } + private void writeLocation(DataOutput output, TextLocation location) throws IOException { + if (location == null || location.getFileName() == null) { + output.writeShort(-1); + } else { + output.writeShort(fileTable.lookup(location.getFileName())); + output.writeShort(location.getLine()); + } + } + private class NodeWriter implements ExprVisitor, StatementVisitor { private final DataOutput output; @@ -191,12 +235,7 @@ public class AstIO { } private void writeLocation(TextLocation location) throws IOException { - if (location == null || location.getFileName() == null) { - output.writeShort(-1); - } else { - output.writeShort(fileTable.lookup(location.getFileName())); - output.writeShort(location.getLine()); - } + AstIO.this.writeLocation(output, location); } private void writeSequence(List sequence) throws IOException { diff --git a/core/src/main/java/org/teavm/cache/DiskMethodNodeCache.java b/core/src/main/java/org/teavm/cache/DiskMethodNodeCache.java index efcfca36c..430a77933 100644 --- a/core/src/main/java/org/teavm/cache/DiskMethodNodeCache.java +++ b/core/src/main/java/org/teavm/cache/DiskMethodNodeCache.java @@ -31,6 +31,7 @@ import java.util.Map; import java.util.Set; 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; @@ -48,7 +49,7 @@ public class DiskMethodNodeCache implements MethodNodeCache { } @Override - public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) { + public AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus) { Item item = cache.get(methodReference); if (item == null) { item = new Item(); @@ -58,20 +59,22 @@ public class DiskMethodNodeCache implements MethodNodeCache { try (InputStream stream = new BufferedInputStream(new FileInputStream(file))) { DataInput input = new DataInputStream(stream); if (!checkIfDependenciesChanged(input, cacheStatus)) { - item.node = astIO.read(input, methodReference); + RegularMethodNode node = astIO.read(input, methodReference); + ControlFlowEntry[] cfg = astIO.readControlFlow(input); + item.entry = new AstCacheEntry(node, cfg); } } catch (IOException e) { // we could not read program, just leave it empty } } } - return item.node; + return item.entry; } @Override - public void store(MethodReference methodReference, RegularMethodNode node, Supplier dependencies) { + public void store(MethodReference methodReference, AstCacheEntry entry, Supplier dependencies) { Item item = new Item(); - item.node = node; + item.entry = entry; item.dependencies = dependencies.get().clone(); cache.put(methodReference, item); newMethods.add(methodReference); @@ -127,7 +130,8 @@ public class DiskMethodNodeCache implements MethodNodeCache { for (String dependency : item.dependencies) { output.writeUTF(dependency); } - astIO.write(output, item.node); + astIO.write(output, item.entry.method); + astIO.write(output, item.entry.cfg); } } for (MethodReference method : newAsyncMethods) { @@ -150,7 +154,7 @@ public class DiskMethodNodeCache implements MethodNodeCache { } private static class Item { - RegularMethodNode node; + AstCacheEntry entry; String[] dependencies; } diff --git a/core/src/main/java/org/teavm/cache/EmptyMethodNodeCache.java b/core/src/main/java/org/teavm/cache/EmptyMethodNodeCache.java index 68574b5d7..ca5ff37ff 100644 --- a/core/src/main/java/org/teavm/cache/EmptyMethodNodeCache.java +++ b/core/src/main/java/org/teavm/cache/EmptyMethodNodeCache.java @@ -17,7 +17,6 @@ package org.teavm.cache; import java.util.function.Supplier; import org.teavm.ast.AsyncMethodNode; -import org.teavm.ast.RegularMethodNode; import org.teavm.model.MethodReference; public class EmptyMethodNodeCache implements MethodNodeCache { @@ -27,12 +26,12 @@ public class EmptyMethodNodeCache implements MethodNodeCache { } @Override - public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) { + public AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus) { return null; } @Override - public void store(MethodReference methodReference, RegularMethodNode node, Supplier dependencies) { + public void store(MethodReference methodReference, AstCacheEntry node, Supplier dependencies) { } @Override diff --git a/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java b/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java index fff47f76f..20390ca91 100644 --- a/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java +++ b/core/src/main/java/org/teavm/cache/InMemoryMethodNodeCache.java @@ -20,7 +20,6 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; import org.teavm.ast.AsyncMethodNode; -import org.teavm.ast.RegularMethodNode; import org.teavm.model.MethodReference; public class InMemoryMethodNodeCache implements MethodNodeCache { @@ -30,7 +29,7 @@ public class InMemoryMethodNodeCache implements MethodNodeCache { private Map newAsyncItems = new HashMap<>(); @Override - public RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus) { + public AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus) { RegularItem item = cache.get(methodReference); if (item == null) { return null; @@ -40,12 +39,12 @@ public class InMemoryMethodNodeCache implements MethodNodeCache { return null; } - return item.node; + return item.entry; } @Override - public void store(MethodReference methodReference, RegularMethodNode node, Supplier dependencies) { - newItems.put(methodReference, new RegularItem(node, dependencies.get().clone())); + public void store(MethodReference methodReference, AstCacheEntry entry, Supplier dependencies) { + newItems.put(methodReference, new RegularItem(entry, dependencies.get().clone())); } @Override @@ -87,11 +86,11 @@ public class InMemoryMethodNodeCache implements MethodNodeCache { } static final class RegularItem { - final RegularMethodNode node; + final AstCacheEntry entry; final String[] dependencies; - RegularItem(RegularMethodNode node, String[] dependencies) { - this.node = node; + RegularItem(AstCacheEntry entry, String[] dependencies) { + this.entry = entry; this.dependencies = dependencies; } } diff --git a/core/src/main/java/org/teavm/cache/MethodNodeCache.java b/core/src/main/java/org/teavm/cache/MethodNodeCache.java index 232daa0fa..f27753664 100644 --- a/core/src/main/java/org/teavm/cache/MethodNodeCache.java +++ b/core/src/main/java/org/teavm/cache/MethodNodeCache.java @@ -17,13 +17,12 @@ package org.teavm.cache; import java.util.function.Supplier; import org.teavm.ast.AsyncMethodNode; -import org.teavm.ast.RegularMethodNode; import org.teavm.model.MethodReference; public interface MethodNodeCache { - RegularMethodNode get(MethodReference methodReference, CacheStatus cacheStatus); + AstCacheEntry get(MethodReference methodReference, CacheStatus cacheStatus); - void store(MethodReference methodReference, RegularMethodNode node, Supplier dependencies); + void store(MethodReference methodReference, AstCacheEntry entry, Supplier dependencies); AsyncMethodNode getAsync(MethodReference methodReference, CacheStatus cacheStatus); diff --git a/core/src/main/java/org/teavm/model/util/LocationGraphBuilder.java b/core/src/main/java/org/teavm/model/util/LocationGraphBuilder.java index 913b50d5a..50e5bcd50 100644 --- a/core/src/main/java/org/teavm/model/util/LocationGraphBuilder.java +++ b/core/src/main/java/org/teavm/model/util/LocationGraphBuilder.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import org.teavm.ast.ControlFlowEntry; import org.teavm.common.Graph; import org.teavm.model.BasicBlock; import org.teavm.model.Instruction; @@ -38,7 +39,7 @@ class LocationGraphBuilder { private List> startLocations; private List additionalConnections; - public Map build(Program program) { + public ControlFlowEntry[] build(Program program) { graphBuilder = new HashMap<>(); Graph graph = ProgramUtils.buildControlFlowGraph(program); dfs(graph, program); @@ -100,13 +101,14 @@ class LocationGraphBuilder { } } - private Map assemble() { + private ControlFlowEntry[] assemble() { for (AdditionalConnection additionalConn : additionalConnections) { for (TextLocation succ : additionalConn.successors) { addEdge(additionalConn.location, succ); } } - Map locationGraph = new HashMap<>(); + ControlFlowEntry[] locationGraph = new ControlFlowEntry[graphBuilder.size()]; + int index = 0; for (Map.Entry> entry : graphBuilder.entrySet()) { TextLocation[] successors = entry.getValue().toArray(new TextLocation[0]); for (int i = 0; i < successors.length; ++i) { @@ -114,7 +116,7 @@ class LocationGraphBuilder { successors[i] = null; } } - locationGraph.put(entry.getKey(), successors); + locationGraph[index++] = new ControlFlowEntry(entry.getKey(), successors); } return locationGraph; } @@ -132,7 +134,7 @@ class LocationGraphBuilder { TextLocation location; Set startLocations; int block; - public Step(TextLocation location, Set startLocations, int block) { + Step(TextLocation location, Set startLocations, int block) { this.location = location; this.startLocations = startLocations; this.block = block; @@ -142,7 +144,7 @@ class LocationGraphBuilder { static class AdditionalConnection { TextLocation location; Set successors; - public AdditionalConnection(TextLocation location, Set successors) { + AdditionalConnection(TextLocation location, Set successors) { this.location = location; this.successors = successors; } diff --git a/core/src/main/java/org/teavm/model/util/ProgramUtils.java b/core/src/main/java/org/teavm/model/util/ProgramUtils.java index 41906e792..34ae0600a 100644 --- a/core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -19,8 +19,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; +import org.teavm.ast.ControlFlowEntry; import org.teavm.common.Graph; import org.teavm.common.GraphBuilder; import org.teavm.model.BasicBlock; @@ -72,7 +72,7 @@ public final class ProgramUtils { return graphBuilder.build(); } - public static Map getLocationCFG(Program program) { + public static ControlFlowEntry[] getLocationCFG(Program program) { return new LocationGraphBuilder().build(program); } diff --git a/tests/src/test/java/org/teavm/incremental/IncrementalTest.java b/tests/src/test/java/org/teavm/incremental/IncrementalTest.java index 4f00b576f..9a4126f9a 100644 --- a/tests/src/test/java/org/teavm/incremental/IncrementalTest.java +++ b/tests/src/test/java/org/teavm/incremental/IncrementalTest.java @@ -42,9 +42,9 @@ import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.Undefined; import org.mozilla.javascript.typedarrays.NativeUint16Array; import org.teavm.ast.AsyncMethodNode; -import org.teavm.ast.RegularMethodNode; import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.cache.AlwaysStaleCacheStatus; +import org.teavm.cache.AstCacheEntry; import org.teavm.cache.CacheStatus; import org.teavm.cache.InMemoryMethodNodeCache; import org.teavm.cache.InMemoryProgramCache; @@ -237,7 +237,7 @@ public class IncrementalTest { boolean capturing; @Override - public void store(MethodReference methodReference, RegularMethodNode node, Supplier dependencies) { + public void store(MethodReference methodReference, AstCacheEntry node, Supplier dependencies) { super.store(methodReference, node, dependencies); if (capturing) { updatedMethods.add(methodReference);