One step toward further minification

This commit is contained in:
Alexey Andreev 2015-02-12 19:08:44 +04:00
parent 727c02381a
commit 54ecf6e00f
9 changed files with 261 additions and 150 deletions

View File

@ -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 {
}
}

View File

@ -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) {

View File

@ -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<? extends VariableReader> 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();
}
};

View File

@ -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<ClassNode> 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<MethodNode> nonInitMethods = new ArrayList<>();
List<MethodNode> virtualMethods = new ArrayList<>();
MethodHolder clinit = classSource.get(cls.getName()).getMethod(
new MethodDescriptor("<clinit>", ValueType.VOID));
List<String> stubNames = new ArrayList<>();
List<MethodNode> clinitMethods = new ArrayList<>();
for (MethodNode method : cls.getMethods()) {
if (!method.getModifiers().contains(NodeModifier.STATIC) &&
!method.getReference().getName().equals("<init>")) {
if (clinit == null || (!method.getModifiers().contains(NodeModifier.STATIC) &&
!method.getReference().getName().equals("<init>"))) {
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<ClassNode> 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("<clinit>", ValueType.VOID));
if (clinit == null) {
continue;
}
List<String> stubNames = new ArrayList<>();
for (MethodNode method : cls.getMethods()) {
if (method.getModifiers().contains(NodeModifier.STATIC) ||
method.getReference().getName().equals("<init>")) {
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<ClassNode> 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("<clinit>", ValueType.VOID));
if (clinit != null) {
writer.appendClass(cls.getName()).append("_$clinit");
} else {
writer.append('0');
}
writer.append(',').ws();
List<String> stubNames = new ArrayList<>();
List<MethodNode> virtualMethods = new ArrayList<>();
for (MethodNode method : cls.getMethods()) {
if (clinit != null && (method.getModifiers().contains(NodeModifier.STATIC) ||
method.getReference().getName().equals("<init>"))) {
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<MethodNode> methods)
throws NamingException, IOException {
if (methods.isEmpty()) {
return;
}
for (MethodNode method : methods) {
MethodReference ref = method.getReference();
if (ref.getDescriptor().getName().equals("<init>")) {
renderInitializer(method);
}
}
writer.append("$rt_virtualMethods(").appendClass(className).indent();
private void renderVirtualDeclarations(List<MethodNode> 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 {

View File

@ -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<String, TeaVMEntryPoint> entry : entryPoints.entrySet()) {
sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws();

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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()) {