diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ObjectEnrichRenderer.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ObjectEnrichRenderer.java index 97d66f1b4..35495b3b5 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ObjectEnrichRenderer.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ObjectEnrichRenderer.java @@ -36,27 +36,18 @@ public class ObjectEnrichRenderer implements RendererListener { } @Override - public void beforeClass(ClassReader cls) throws IOException { - } - - @Override - public void afterClass(ClassReader cls) throws IOException { - if (cls.getName().equals("java.lang.Object")) { - MethodReader toString = cls.getMethod(new MethodDescriptor("toString", String.class)); - if (toString != null) { - String clsName = context.getNaming().getNameFor(cls.getName()); - String toStringName = context.getNaming().getNameFor(toString.getReference()); - context.getWriter().append(clsName).append(".prototype.toString").ws().append('=').ws() - .append("function()").ws().append('{').indent().softNewLine(); - context.getWriter().append("return this.").append(toStringName).ws().append('?').ws() - .append("$rt_ustr(this.").append(toStringName).append("())").ws().append(':') - .append("Object.prototype.toString.call(this);").softNewLine(); - context.getWriter().outdent().append("}").newLine(); - } + public void complete() throws IOException { + ClassReader cls = context.getClassSource().get("java.lang.Object"); + MethodReader toString = cls.getMethod(new MethodDescriptor("toString", String.class)); + if (toString != null) { + String clsName = context.getNaming().getNameFor(cls.getName()); + String toStringName = context.getNaming().getNameFor(toString.getReference()); + context.getWriter().append(clsName).append(".prototype.toString").ws().append('=').ws() + .append("function()").ws().append('{').indent().softNewLine(); + context.getWriter().append("return this.").append(toStringName).ws().append('?').ws() + .append("$rt_ustr(this.").append(toStringName).append("())").ws().append(':') + .append("Object.prototype.toString.call(this);").softNewLine(); + context.getWriter().outdent().append("}").newLine(); } } - - @Override - public void complete() throws IOException { - } } 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 31a99e82e..8ca881bd5 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -205,7 +205,9 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { boolean added = true; if (callLocation != null && callLocation.getMethod() != null) { DefaultCallGraphNode callGraphNode = callGraph.getNode(callLocation.getMethod()); - added = addClassAccess(callGraphNode, className, callLocation.getSourceLocation()); + if (!addClassAccess(callGraphNode, className, callLocation.getSourceLocation())) { + added = false; + } } if (!dep.isMissing() && added) { for (DependencyListener listener : listeners) { @@ -222,13 +224,13 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { ClassReader cls = classSource.get(className); if (cls != null) { if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) { - return addClassAccess(node, cls.getParent(), loc); + addClassAccess(node, cls.getParent(), loc); } for (String iface : cls.getInterfaces()) { - return addClassAccess(node, iface, loc); + addClassAccess(node, iface, loc); } } - return false; + return true; } private ClassDependency createClassDependency(String className) { 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 5e13cca03..7a817708a 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -349,6 +349,7 @@ class DependencyGraphBuilder { @Override public void create(VariableReader receiver, String type) { + dependencyChecker.linkClass(type, new CallLocation(caller.getMethod(), currentLocation)); nodes[receiver.getIndex()].propagate(dependencyChecker.getType(type)); } @@ -427,8 +428,9 @@ class DependencyGraphBuilder { private void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method, List arguments) { - MethodDependency methodDep = dependencyChecker.linkMethod(method, - new CallLocation(caller.getMethod(), currentLocation)); + CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation); + dependencyChecker.linkClass(method.getClassName(), callLocation).initClass(callLocation); + MethodDependency methodDep = dependencyChecker.linkMethod(method, callLocation); if (methodDep.isMissing()) { return; } @@ -494,14 +496,14 @@ class DependencyGraphBuilder { @Override public void monitorEnter(VariableReader objectRef) { dependencyChecker.linkMethod( - new MethodReference(Object.class, "monitorEnter", Object.class, void.class), + new MethodReference(Object.class, "monitorEnter", Object.class, void.class), new CallLocation(caller.getMethod(), currentLocation)).use(); } @Override public void monitorExit(VariableReader objectRef) { dependencyChecker.linkMethod( - new MethodReference(Object.class, "monitorExit", Object.class, void.class), + new MethodReference(Object.class, "monitorExit", Object.class, void.class), new CallLocation(caller.getMethod(), currentLocation)).use(); } }; 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 b71e0c25e..c0674b09f 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -270,8 +270,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append("function $rt_objcls() { return ").appendClass("java.lang.Object").append("; }").newLine(); } - public void render(ClassNode cls) throws RenderingException { - debugEmitter.emitClass(cls.getName()); + public void render(List classes) throws RenderingException { + for (ClassNode cls : classes) { + renderDeclaration(cls); + } + for (ClassNode cls : classes) { + renderMethodBodies(cls); + } + renderClassMetadata(classes); + renderMethodStubs(classes); + } + + private void renderDeclaration(ClassNode cls) throws RenderingException { debugEmitter.addClass(cls.getName(), cls.getParentName()); try { writer.append("function ").appendClass(cls.getName()).append("()").ws().append("{") @@ -306,52 +316,30 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.appendClass(cls.getName()).append('.').appendField(fieldRef).ws().append("=").ws() .append(constantToString(value)).append(";").softNewLine(); } + } catch (NamingException e) { + throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } + } + private void renderMethodBodies(ClassNode cls) throws RenderingException { + debugEmitter.emitClass(cls.getName()); + try { List nonInitMethods = new ArrayList<>(); List virtualMethods = new ArrayList<>(); MethodHolder clinit = classSource.get(cls.getName()).getMethod( new MethodDescriptor("", ValueType.VOID)); - List stubNames = new ArrayList<>(); List clinitMethods = new ArrayList<>(); for (MethodNode method : cls.getMethods()) { - if (!method.getModifiers().contains(NodeModifier.STATIC) && - !method.getReference().getName().equals("")) { + if (clinit == null || (!method.getModifiers().contains(NodeModifier.STATIC) && + !method.getReference().getName().equals(""))) { nonInitMethods.add(method); } else { clinitMethods.add(method); - stubNames.add(naming.getFullNameFor(method.getReference())); } } - boolean needsClinit = clinit != null || !clinitMethods.isEmpty(); - - writer.append("$rt_declClass(").appendClass(cls.getName()).append(",").ws().append("{") - .indent().softNewLine(); - writer.append("name").ws().append(":").ws().append("\"").append(escapeString(cls.getName())) - .append("\""); - if (cls.getModifiers().contains(NodeModifier.ENUM)) { - writer.append(",").softNewLine().append("enum").ws().append(":").ws().append("true"); - } - if (!cls.getInterfaces().isEmpty()) { - writer.append(",").softNewLine().append("interfaces").ws().append(":").ws().append("["); - for (int i = 0; i < cls.getInterfaces().size(); ++i) { - String iface = cls.getInterfaces().get(i); - if (i > 0) { - writer.append(",").ws(); - } - writer.appendClass(iface); - } - writer.append("]"); - } - if (cls.getParentName() != null) { - writer.append(",").softNewLine(); - writer.append("superclass").ws().append(":").ws().appendClass(cls.getParentName()); - } - if (!cls.getModifiers().contains(NodeModifier.INTERFACE) && needsClinit) { - writer.append(",").softNewLine().append("clinit").ws().append(":").ws() - .append("function()").ws().append("{").ws() - .appendClass(cls.getName()).append("_$clinit();").ws().append("}"); - } - writer.ws().append("});").newLine().outdent(); + boolean needsClinit = clinit != null; if (needsClinit) { writer.append("function ").appendClass(cls.getName()).append("_$clinit()").ws() @@ -380,23 +368,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext renderStaticDeclaration(method); } } - if (stubNames.size() > 0) { - writer.append("$rt_methodStubs(").appendClass(cls.getName()).append("_$clinit") - .append(",").ws().append("["); - for (int i = 0; i < stubNames.size(); ++i) { - if (i > 0) { - writer.append(",").ws(); - } - writer.append("'").append(stubNames.get(i)).append("'"); - } - writer.append("]);").newLine(); - } } for (MethodNode method : nonInitMethods) { renderBody(method, false); } - renderVirtualDeclarations(cls.getName(), virtualMethods); } catch (NamingException e) { throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e); } catch (IOException e) { @@ -405,6 +381,128 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext debugEmitter.emitClass(null); } + private void renderMethodStubs(List classes) { + try { + boolean first = true; + writer.append("$rt_methodStubs(["); + for (int i = 0; i < classes.size(); ++i) { + ClassNode cls = classes.get(i); + MethodHolder clinit = classSource.get(cls.getName()).getMethod( + new MethodDescriptor("", ValueType.VOID)); + if (clinit == null) { + continue; + } + List stubNames = new ArrayList<>(); + for (MethodNode method : cls.getMethods()) { + if (method.getModifiers().contains(NodeModifier.STATIC) || + method.getReference().getName().equals("")) { + stubNames.add(naming.getFullNameFor(method.getReference())); + } + } + if (stubNames.isEmpty()) { + continue; + } + if (!first) { + writer.append(',').softNewLine(); + } + first = false; + writer.appendClass(cls.getName()).append("_$clinit").append(",").ws(); + if (stubNames.size() == 1) { + writer.append("'").append(stubNames.get(0)).append("'"); + } else { + writer.append('['); + for (int j = 0; j < stubNames.size(); ++j) { + if (j > 0) { + writer.append(",").ws(); + } + writer.append("'").append(stubNames.get(j)).append("'"); + } + writer.append(']'); + } + } + writer.append("]);").newLine(); + } catch (NamingException e) { + throw new RenderingException("Error rendering method stubs. See a cause for details", e); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } + } + + private void renderClassMetadata(List classes) { + try { + writer.append("$rt_declClasses(["); + boolean first = true; + for (ClassNode cls : classes) { + if (!first) { + writer.append(',').softNewLine(); + } + first = false; + writer.appendClass(cls.getName()).append(",").ws(); + writer.append("\"").append(escapeString(cls.getName())).append("\",").ws(); + if (cls.getParentName() != null) { + writer.appendClass(cls.getParentName()); + } else { + writer.append("0"); + } + writer.append(',').ws(); + writer.append("["); + for (int i = 0; i < cls.getInterfaces().size(); ++i) { + String iface = cls.getInterfaces().get(i); + if (i > 0) { + writer.append(",").ws(); + } + writer.appendClass(iface); + } + writer.append("],").ws(); + int flags = 0; + if (cls.getModifiers().contains(NodeModifier.ENUM)) { + flags &= 1; + } + writer.append(flags).append(',').ws(); + MethodHolder clinit = classSource.get(cls.getName()).getMethod( + new MethodDescriptor("", ValueType.VOID)); + if (clinit != null) { + writer.appendClass(cls.getName()).append("_$clinit"); + } else { + writer.append('0'); + } + writer.append(',').ws(); + + List stubNames = new ArrayList<>(); + List virtualMethods = new ArrayList<>(); + for (MethodNode method : cls.getMethods()) { + if (clinit != null && (method.getModifiers().contains(NodeModifier.STATIC) || + method.getReference().getName().equals(""))) { + stubNames.add(naming.getFullNameFor(method.getReference())); + } + if (!method.getModifiers().contains(NodeModifier.STATIC)) { + virtualMethods.add(method); + } + } + if (stubNames.size() == 1) { + writer.append("'").append(stubNames.get(0)).append("'"); + } else { + writer.append('['); + for (int j = 0; j < stubNames.size(); ++j) { + if (j > 0) { + writer.append(",").ws(); + } + writer.append("'").append(stubNames.get(j)).append("'"); + } + writer.append(']'); + } + writer.append(',').ws(); + + renderVirtualDeclarations(virtualMethods); + } + writer.append("]);").newLine(); + } catch (NamingException e) { + throw new RenderingException("Error rendering class metadata. See a cause for details", e); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } + } + private static Object getDefaultValue(ValueType type) { if (type instanceof ValueType.Primitive) { ValueType.Primitive primitive = (ValueType.Primitive)type; @@ -454,22 +552,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext debugEmitter.emitMethod(null); } - private void renderVirtualDeclarations(String className, List methods) - throws NamingException, IOException { - if (methods.isEmpty()) { - return; - } - for (MethodNode method : methods) { - MethodReference ref = method.getReference(); - if (ref.getDescriptor().getName().equals("")) { - renderInitializer(method); - } - } - writer.append("$rt_virtualMethods(").appendClass(className).indent(); + private void renderVirtualDeclarations(List methods) throws NamingException, IOException { + writer.append("["); + boolean first = true; for (MethodNode method : methods) { debugEmitter.emitMethod(method.getReference().getDescriptor()); MethodReference ref = method.getReference(); - writer.append(",").newLine(); + if (!first) { + writer.append(",").ws(); + } + first = false; String methodName = method.isAsync() ? naming.getNameForAsync(ref) : naming.getNameFor(ref); if (method.isOriginalNamePreserved()) { writer.append("[\"").append(methodName).append("\",").ws().append("\"").append(ref.getName()) @@ -509,7 +601,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append("$rt_asyncAdapter(").appendMethodBody(ref).append(')'); } } - writer.append(");").newLine().outdent(); + writer.append("]"); } private void renderStaticDeclaration(MethodNode method) throws NamingException, IOException { 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 0c44ba736..9f442f5d9 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -454,16 +454,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { // END Thread stuff renderer.renderRuntime(); - for (ClassNode clsNode : clsNodes) { - ClassReader cls = classSet.get(clsNode.getName()); - for (RendererListener listener : rendererListeners) { - listener.beforeClass(cls); - } - renderer.render(clsNode); - for (RendererListener listener : rendererListeners) { - listener.afterClass(cls); - } - } + renderer.render(clsNodes); renderer.renderStringPool(); for (Map.Entry entry : entryPoints.entrySet()) { sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws(); diff --git a/teavm-core/src/main/java/org/teavm/vm/spi/AbstractRendererListener.java b/teavm-core/src/main/java/org/teavm/vm/spi/AbstractRendererListener.java index 393d45292..b94971fb7 100644 --- a/teavm-core/src/main/java/org/teavm/vm/spi/AbstractRendererListener.java +++ b/teavm-core/src/main/java/org/teavm/vm/spi/AbstractRendererListener.java @@ -17,7 +17,6 @@ package org.teavm.vm.spi; import java.io.IOException; import org.teavm.javascript.RenderingContext; -import org.teavm.model.ClassReader; import org.teavm.vm.BuildTarget; /** @@ -29,14 +28,6 @@ public abstract class AbstractRendererListener implements RendererListener { public void begin(RenderingContext context, BuildTarget buildTarget) throws IOException { } - @Override - public void beforeClass(ClassReader cls) throws IOException { - } - - @Override - public void afterClass(ClassReader cls) throws IOException { - } - @Override public void complete() throws IOException { } diff --git a/teavm-core/src/main/java/org/teavm/vm/spi/RendererListener.java b/teavm-core/src/main/java/org/teavm/vm/spi/RendererListener.java index f7c04d4ac..6c4e935d7 100644 --- a/teavm-core/src/main/java/org/teavm/vm/spi/RendererListener.java +++ b/teavm-core/src/main/java/org/teavm/vm/spi/RendererListener.java @@ -17,7 +17,6 @@ package org.teavm.vm.spi; import java.io.IOException; import org.teavm.javascript.RenderingContext; -import org.teavm.model.ClassReader; import org.teavm.vm.BuildTarget; /** @@ -27,9 +26,5 @@ import org.teavm.vm.BuildTarget; public interface RendererListener { void begin(RenderingContext context, BuildTarget buildTarget) throws IOException; - void beforeClass(ClassReader cls) throws IOException; - - void afterClass(ClassReader cls) throws IOException; - void complete() throws IOException; } 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 1c78893bf..48b4806aa 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -336,14 +336,21 @@ function $rt_assertNotNaN(value) { } return value; } -function $rt_methodStubs(clinit, names) { - for (var i = 0; i < names.length; i = (i + 1) | 0) { - window[names[i]] = (function(name) { - return function() { - clinit(); - return window[name].apply(window, arguments); - } - })(names[i]); +function $rt_methodStubs(data) { + for (var i = 0; i < data.length; i += 2) { + var clinit = data[i + 0]; + var names = data[i + 1]; + if (!(names instanceof Array)) { + names = [names]; + } + for (var j = 0; j < names.length; j = (j + 1) | 0) { + window[names[j]] = (function(name, clinit) { + return function() { + clinit(); + return window[name].apply(window, arguments); + } + })(names[j], clinit); + } } } var $rt_stdoutBuffer = ""; @@ -368,25 +375,58 @@ function $rt_putStderr(ch) { $rt_stderrBuffer += String.fromCharCode(ch); } } -function $rt_declClass(cls, data) { - cls.$meta = {}; - var m = cls.$meta - m.superclass = typeof(data.superclass) !== 'undefined' ? data.superclass : null; - m.supertypes = data.interfaces ? data.interfaces.slice() : []; - if (data.superclass) { - m.supertypes.push(data.superclass); - cls.prototype = new data.superclass(); - } else { - cls.prototype = new Object(); +function $rt_metadata(data) { + for (var i = 0; i < data.length; i += 8) { + var cls = data[i + 0]; + cls.$meta = {}; + var m = cls.$meta; + m.name = data[i + 1]; + m.binaryName = "L" + m.name + ";"; + var superclass = data[i + 2]; + m.superclass = superclass !== 0 ? superclass : null; + m.supertypes = data[i + 3]; + if (m.superclass) { + m.supertypes.push(m.superclass); + cls.prototype = new m.superclass(); + } else { + cls.prototype = new Object(); + } + var flags = data[i + 4]; + m.enum = (flags & 1) != 0; + m.primitive = false; + m.item = null; + cls.prototype.constructor = cls; + cls.classObject = null; + var clinit = data[i + 5]; + cls.$clinit = clinit !== 0 ? clinit : function() {}; + + var names = data[i + 6]; + if (!(names instanceof Array)) { + names = [names]; + } + for (var j = 0; j < names.length; j = (j + 1) | 0) { + window[names[j]] = (function(cls, name) { + return function() { + var clinit = cls.$clinit; + cls.$clinit = function() {}; + cls.$clinit(); + return window[name].apply(window, arguments); + } + })(cls, names[j]); + } + + var virtualMethods = data[i + 7]; + for (var j = 0; j < virtualMethods.length; j += 2) { + name = virtualMethods[j + 0]; + var func = virtualMethods[j + 1]; + if (typeof name === 'string') { + name = [name]; + } + for (var k = 0; k < name.length; ++k) { + cls.prototype[name[k]] = func; + } + } } - m.name = data.name; - m.binaryName = "L" + data.name + ";"; - m.enum = data.enum; - m.item = null; - m.primitive = false; - cls.prototype.constructor = cls; - cls.classObject = null; - cls.$clinit = data.clinit ? data.clinit : function() {}; } function $rt_virtualMethods(cls) { for (var i = 1; i < arguments.length; i += 2) { @@ -401,6 +441,22 @@ function $rt_virtualMethods(cls) { } } } +function $rt_virtualMethods(data) { + for (var i = 0; i < data.length; i += 2) { + var cls = data[i + 0]; + var methods = data[i + 1]; + for (var j = 0; j < methods.length; j += 2) { + var name = methods[j + 0]; + var func = methods[j + 1]; + if (typeof name === 'string') { + name = [name]; + } + for (var k = 0; k < name.length; ++k) { + cls.prototype[name[k]] = func; + } + } + } +} function $rt_asyncResult(value) { return function() { return value; diff --git a/teavm-jso/src/main/java/org/teavm/jso/plugin/JSOAliasRenderer.java b/teavm-jso/src/main/java/org/teavm/jso/plugin/JSOAliasRenderer.java index 432f042bf..ed60cf003 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/plugin/JSOAliasRenderer.java +++ b/teavm-jso/src/main/java/org/teavm/jso/plugin/JSOAliasRenderer.java @@ -20,7 +20,6 @@ import java.util.Map; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.RenderingContext; import org.teavm.jso.plugin.JSODependencyListener.ExposedClass; -import org.teavm.model.ClassReader; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; import org.teavm.vm.BuildTarget; @@ -43,14 +42,6 @@ class JSOAliasRenderer implements RendererListener { writer = context.getWriter(); } - @Override - public void beforeClass(ClassReader cls) throws IOException { - } - - @Override - public void afterClass(ClassReader cls) throws IOException { - } - @Override public void complete() throws IOException { if (!dependencyListener.isAnyAliasExists()) {