From e32da9316f342430ad1d2688b52711053166f451 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 27 Nov 2013 17:30:16 +0400 Subject: [PATCH] Improves dependency checking. Adds some core runtime functions --- .../teavm/codegen/DefaultNamingStrategy.java | 3 + .../teavm/dependency/DependencyChecker.java | 30 ++- .../dependency/DependencyGraphBuilder.java | 3 + .../java/org/teavm/javascript/Renderer.java | 15 ++ .../main/java/org/teavm/model/ValueType.java | 2 +- .../resources/org/teavm/javascript/runtime.js | 218 ++++++++++-------- 6 files changed, 168 insertions(+), 103 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java b/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java index bb58b7361..751f26c13 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java +++ b/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java @@ -53,6 +53,9 @@ public class DefaultNamingStrategy implements NamingStrategy { } ClassHolder clsHolder = classSource.getClassHolder(method.getClassName()); MethodHolder methodHolder = clsHolder.getMethod(method.getDescriptor()); + if (methodHolder == null) { + throw new RuntimeException("Method not found: " + method); + } if (methodHolder.getModifiers().contains(ElementModifier.STATIC) || method.getDescriptor().getName().equals("") || methodHolder.getLevel() == AccessLevel.PRIVATE) { diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index e5a454ebd..c1cc353e0 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -33,6 +33,7 @@ public class DependencyChecker { static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true"); private ClassHolderSource classSource; private ScheduledThreadPoolExecutor executor; + private ConcurrentMap abstractMethods = new ConcurrentHashMap<>(); private ConcurrentCachedMapper methodCache; private ConcurrentCachedMapper fieldCache; private ConcurrentMap achievableClasses = new ConcurrentHashMap<>(); @@ -210,6 +211,10 @@ public class DependencyChecker { return methodCache.caches(methodRef); } + public boolean isAbstractMethodAchievable(MethodReference methodRef) { + return abstractMethods.containsKey(methodRef); + } + public Collection getAchievableMethods() { return methodCache.getCachedPreimages(); } @@ -262,6 +267,24 @@ public class DependencyChecker { plugin.methodAchieved(this, methodRef); } + public void addAbstractMethod(MethodReference methodRef) { + if (abstractMethods.putIfAbsent(methodRef, methodRef) == null) { + String className = methodRef.getClassName(); + while (className != null) { + ClassHolder cls = classSource.getClassHolder(className); + if (cls == null) { + return; + } + MethodHolder method = cls.getMethod(methodRef.getDescriptor()); + if (method != null) { + abstractMethods.put(methodRef, methodRef); + return; + } + className = cls.getParent(); + } + } + } + public ListableClassHolderSource cutUnachievableClasses() { MutableClassHolderSource cutClasses = new MutableClassHolderSource(); for (String className : achievableClasses.keySet()) { @@ -269,7 +292,12 @@ public class DependencyChecker { for (MethodHolder method : classHolder.getMethods().toArray(new MethodHolder[0])) { MethodReference methodRef = new MethodReference(className, method.getDescriptor()); if (!methodCache.getCachedPreimages().contains(methodRef)) { - classHolder.removeMethod(method); + if (abstractMethods.containsKey(methodRef)) { + method.getModifiers().add(ElementModifier.ABSTRACT); + method.setProgram(null); + } else { + classHolder.removeMethod(method); + } } } for (FieldHolder field : classHolder.getFields().toArray(new FieldHolder[0])) { diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 297308b66..7634c3476 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -156,6 +156,7 @@ class DependencyGraphBuilder { DependencyConsumer listener = new VirtualCallPropagationListener(nodes[insn.getInstance().getIndex()], insn.getMethod().getDescriptor(), dependencyChecker, actualArgs, insn.getReceiver() != null ? nodes[insn.getReceiver().getIndex()] : null); + dependencyChecker.addAbstractMethod(insn.getMethod()); nodes[insn.getInstance().getIndex()].addConsumer(listener); } @@ -280,6 +281,8 @@ class DependencyGraphBuilder { @Override public void visit(StringConstantInstruction insn) { nodes[insn.getReceiver().getIndex()].propagate("java.lang.String"); + dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor( + "", ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); } @Override diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index b951c186a..e3ddc3f3b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -47,6 +47,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { public void renderRuntime() { renderRuntimeCls(); + renderRuntimeString(); } private void renderRuntimeCls() { @@ -65,6 +66,20 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); } + private void renderRuntimeString() { + String stringClass = "java.lang.String"; + MethodReference stringCons = new MethodReference(stringClass, new MethodDescriptor("", + ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)); + writer.append("$rt_str = function(str) {").indent().newLine(); + writer.append("var characters = $rt_createNumericArray($rt_charcls(), str.length);").newLine(); + writer.append("for (var i = 0; i < str.length; i = (i + 1) | 0) {").indent().newLine(); + writer.append("characters[i] = str.charCodeAt(i);").newLine(); + writer.outdent().append("}").newLine(); + writer.append("return $rt_init(").appendClass("java.lang.String").append(", '") + .appendMethod(stringCons).append("', characters);").newLine(); + writer.outdent().append("}").newLine(); + } + public void render(ClassNode cls) { writer.appendClass(cls.getName()).append(" = function() {").indent().newLine(); for (FieldNode field : cls.getFields()) { diff --git a/teavm-core/src/main/java/org/teavm/model/ValueType.java b/teavm-core/src/main/java/org/teavm/model/ValueType.java index 1a36f8bc2..86d0b9a13 100644 --- a/teavm-core/src/main/java/org/teavm/model/ValueType.java +++ b/teavm-core/src/main/java/org/teavm/model/ValueType.java @@ -116,7 +116,7 @@ public abstract class ValueType { @Override public String toString() { if (reprCache == null) { - reprCache = itemType.toString(); + reprCache = "[" + itemType.toString(); } return reprCache; } diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js index 9b0a00376..d311efe86 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -29,6 +29,20 @@ $rt_createArray = function(cls, sz) { } return arr; } +$rt_createNumericArray = function(cls, sz) { + var arr = $rt_createArray(cls, sz); + for (var i = 0; i < sz; i = (i + 1) | 0) { + arr[i] = 0; + } + return arr; +} +$rt_createLongArray = function(sz) { + var arr = $rt.createArray($rt_longcls(), sz); + for (var i = 0; i < sz; i = (i + 1) | 0) { + arr[i] = Long.ZERO; + } + return arr; +}, $rt_arraycls = function(cls) { if (cls.$array == undefined) { cls.$array = { @@ -40,6 +54,109 @@ $rt_arraycls = function(cls) { } return cls.$array; } +$rt_createcls = function() { + return { + $meta : { + supertypes : [] + } + }; +} +$rt_booleanclsCache = null; +$rt_booleancls = function() { + if ($rt_booleanclsCache == null) { + $rt_booleanclsCache = $rt_createcls(); + } + return $rt_booleanclsCache; +} +$rt_booleanclsCache = null; +$rt_booleancls = function() { + if ($rt_booleanclsCache == null) { + $rt_booleanclsCache = $rt_createcls(); + } + return $rt_booleanclsCache; +} +$rt_charclsCache = null; +$rt_charcls = function() { + if ($rt_charclsCache == null) { + $rt_charclsCache = $rt_createcls(); + } + return $rt_charclsCache; +} +$rt_byteclsCache = null; +$rt_bytecls = function() { + if ($rt_byteclsCache == null) { + $rt_byteclsCache = $rt_createcls(); + } + return $rt_byteclsCache; +} +$rt_shortclsCache = null; +$rt_shortcls = function() { + if ($rt_shortclsCache == null) { + $rt_shortclsCache = $rt_createcls(); + } + return $rt_shortclsCache; +} +$rt_intclsCache = null; +$rt_intcls = function() { + if ($rt_intclsCache == null) { + $rt_intclsCache = $rt_createcls(); + } + return $rt_intclsCache; +} +$rt_longclsCache = null; +$rt_longcls = function() { + if ($rt_longclsCache == null) { + $rt_longclsCache = $rt_createcls(); + } + return $rt_longclsCache; +} +$rt_floatclsCache = null; +$rt_floatcls = function() { + if ($rt_floatclsCache == null) { + $rt_floatclsCache = $rt_createcls(); + } + return $rt_floatclsCache; +} +$rt_doubleclsCache = null; +$rt_doublecls = function() { + if ($rt_doubleclsCache == null) { + $rt_doubleclsCache = $rt_createcls(); + } + return $rt_doubleclsCache; +} +$rt_voidclsCache = null; +$rt_voidcls = function() { + if ($rt_voidclsCache == null) { + $rt_voidclsCache = $rt_createcls(); + } + return $rt_voidclsCache; +} +$rt_equals = function(a, b) { + if (a === b) { + return true; + } + if (a === null || b === null) { + return false; + } + if (typeof(a) == 'object') { + return a.equals(b); + } else { + return false; + } +} +$rt_clinit = function(cls) { + if (cls.$clinit) { + var f = cls.$clinit; + delete cls.$clinit; + f(); + } + return cls; +} +$rt_init = function(cls, constructor, args) { + var obj = new cls(); + cls.prototype[constructor].apply(obj, args); + return obj; +} $rt = { createBooleanArray : function(cls, sz) { @@ -49,20 +166,6 @@ $rt = { } return arr; }, - createNumericArray : function(cls, sz) { - var arr = $rt.createArray(cls, sz); - for (var i = 0; i < sz; i = (i + 1) | 0) { - arr[i] = 0; - } - return arr; - }, - createLongArray : function(sz) { - var arr = $rt.createArray($rt.longcls(), sz); - for (var i = 0; i < sz; i = (i + 1) | 0) { - arr[i] = Long.ZERO; - } - return arr; - }, createMultiArray : function(cls, dimensions) { for (var i = 1; i < dimensions.length; i = (i + 1) | 0) { cls = $rt.arraycls(cls); @@ -86,93 +189,6 @@ $rt = { $rt.setId(arr, $rt.lastObjectId++); return arr; }, - createcls : function() { - return { - $meta : { - supertypes : [] - } - }; - }, - booleancls : function() { - if ($rt.booleanclsCache == null) { - $rt.booleanclsCache = $rt.createcls(); - } - return $rt.booleanclsCache; - }, - charcls : function() { - if ($rt.charclsCache == null) { - $rt.charclsCache = $rt.createcls(); - } - return $rt.charclsCache; - }, - bytecls : function() { - if ($rt.byteclsCache == null) { - $rt.byteclsCache = $rt.createcls(); - } - return $rt.byteclsCache; - }, - shortcls : function() { - if ($rt.shortclsCache == null) { - $rt.shortclsCache = $rt.createcls(); - } - return $rt.shortclsCache; - }, - intcls : function() { - if ($rt.intclsCache == null) { - $rt.intclsCache = $rt.createcls(); - } - return $rt.intclsCache; - }, - longcls : function() { - if ($rt.longclsCache == null) { - $rt.longclsCache = $rt.createcls(); - } - return $rt.longclsCache; - }, - floatcls : function() { - if ($rt.floatclsCache == null) { - $rt.floatclsCache = $rt.createcls(); - } - return $rt.floatclsCache; - }, - doublecls : function() { - if ($rt.doubleclsCache == null) { - $rt.doubleclsCache = $rt.createcls(); - } - return $rt.doubleclsCache; - }, - voidcls : function() { - if ($rt.voidclsCache == null) { - $rt.voidclsCache = $rt.createcls(); - } - return $rt.voidclsCache; - }, - equals : function(a, b) { - if (a === b) { - return true; - } - if (a === null || b === null) { - return false; - } - if (typeof(a) == 'object') { - return a.equals(b); - } else { - return false; - } - }, - clinit : function(cls) { - if (cls.$clinit) { - var f = cls.$clinit; - delete cls.$clinit; - f(); - } - return cls; - }, - init : function(cls, constructor, args) { - var obj = new cls(); - cls.prototype[constructor].apply(obj, args); - return obj; - }, assertNotNaN : function(value) { if (typeof value == 'number' && isNaN(value)) { throw "NaN";