New JSBody generator allowing to get smaller JavaScript code

This commit is contained in:
Alexey Andreev 2015-09-24 16:58:37 +03:00
parent 792294296d
commit d4309bb564
8 changed files with 116 additions and 42 deletions

View File

@ -16,6 +16,7 @@
package org.teavm.jso.plugin; package org.teavm.jso.plugin;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -479,8 +480,11 @@ public class AstWriter {
private void print(PropertyGet node) throws IOException { private void print(PropertyGet node) throws IOException {
print(node.getLeft(), PRECEDENCE_MEMBER); print(node.getLeft(), PRECEDENCE_MEMBER);
writer.ws().append('.').ws(); writer.append('.');
Map<String, String> oldNameMap = nameMap;
nameMap = Collections.emptyMap();
print(node.getRight()); print(node.getRight());
nameMap = oldNameMap;
} }
private void print(FunctionCall node, int precedence) throws IOException { private void print(FunctionCall node, int precedence) throws IOException {
@ -597,7 +601,7 @@ public class AstWriter {
} }
private void print(ObjectLiteral node) throws IOException { private void print(ObjectLiteral node) throws IOException {
writer.append('{'); writer.append('{').ws();
if (node.getElements() != null && !node.getElements().isEmpty()) { if (node.getElements() != null && !node.getElements().isEmpty()) {
print(node.getElements().get(0)); print(node.getElements().get(0));
for (int i = 1; i < node.getElements().size(); ++i) { for (int i = 1; i < node.getElements().size(); ++i) {
@ -605,7 +609,7 @@ public class AstWriter {
print(node.getElements().get(i)); print(node.getElements().get(i));
} }
} }
writer.append('}'); writer.ws().append('}');
} }
private void print(ObjectProperty node) throws IOException { private void print(ObjectProperty node) throws IOException {
@ -614,7 +618,10 @@ public class AstWriter {
} else if (node.isSetterMethod()) { } else if (node.isSetterMethod()) {
writer.append("set "); writer.append("set ");
} }
Map<String, String> oldNameMap = nameMap;
nameMap = Collections.emptyMap();
print(node.getLeft()); print(node.getLeft());
nameMap = oldNameMap;
if (!node.isMethod()) { if (!node.isMethod()) {
writer.ws().append(':').ws(); writer.ws().append(':').ws();
} }
@ -631,7 +638,7 @@ public class AstWriter {
} }
writer.append('('); writer.append('(');
printList(node.getParams()); printList(node.getParams());
writer.append(')'); writer.append(')').ws();
if (node.isExpressionClosure()) { if (node.isExpressionClosure()) {
if (node.getBody().getLastChild() instanceof ReturnStatement) { if (node.getBody().getLastChild() instanceof ReturnStatement) {
@ -642,10 +649,6 @@ public class AstWriter {
} }
} else { } else {
print(node.getBody()); print(node.getBody());
writer.softNewLine();
}
if (node.getFunctionType() == FunctionNode.FUNCTION_STATEMENT || node.isMethod()) {
writer.softNewLine();
} }
} }

View File

@ -19,10 +19,8 @@ import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.List; import java.util.List;
import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.CompilerEnvirons;
import org.mozilla.javascript.IRFactory; import org.mozilla.javascript.Context;
import org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.NodeVisitor;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.spi.Generator; import org.teavm.javascript.spi.Generator;
import org.teavm.javascript.spi.GeneratorContext; import org.teavm.javascript.spi.GeneratorContext;
@ -45,29 +43,86 @@ public class JSBodyGenerator implements Generator {
AnnotationReader annot = method.getAnnotations().get(JSBodyImpl.class.getName()); AnnotationReader annot = method.getAnnotations().get(JSBodyImpl.class.getName());
boolean isStatic = annot.getValue("isStatic").getBoolean(); boolean isStatic = annot.getValue("isStatic").getBoolean();
List<AnnotationValue> paramNames = annot.getValue("params").getList(); List<AnnotationValue> paramNames = annot.getValue("params").getList();
String script = annot.getValue("script").getString();
int bodyParamCount = isStatic ? method.parameterCount() : method.parameterCount() - 1; TeaVMErrorReporter errorReporter = new TeaVMErrorReporter(context.getDiagnostics(),
new CallLocation(methodRef));
CompilerEnvirons env = new CompilerEnvirons(); CompilerEnvirons env = new CompilerEnvirons();
env.setRecoverFromErrors(true); env.setRecoverFromErrors(true);
IRFactory factory = new IRFactory(env, new TeaVMErrorReporter(context.getDiagnostics(), env.setLanguageVersion(Context.VERSION_1_8);
new CallLocation(methodRef))); env.setIdeMode(true);
String script = annot.getValue("script").getString(); JSParser parser = new JSParser(env, errorReporter);
AstRoot rootNode; parser.enterFunction();
try { AstRoot rootNode = parser.parse(new StringReader(script), null, 0);
rootNode = factory.parse(new StringReader(script), null, 0); parser.exitFunction();
} catch (IOException e) { if (errorReporter.hasErrors()) {
context.getDiagnostics().error(new CallLocation(methodRef), "IO error parsing JSBody script"); generateBloated(context, writer, methodRef, method, isStatic, paramNames, script);
return; return;
} }
rootNode.visit(new NodeVisitor() { AstWriter astWriter = new AstWriter(writer);
@Override int paramIndex = 1;
public boolean visit(AstNode node) { if (!isStatic) {
return false; astWriter.declareAlias("this", context.getParameterName(paramIndex++));
} }
}); for (int i = 0; i < paramNames.size(); ++i) {
astWriter.declareAlias(paramNames.get(i).getString(), context.getParameterName(paramIndex++));
}
astWriter.hoist(rootNode);
astWriter.print(rootNode);
writer.softNewLine();
} }
private void generateBloated(GeneratorContext context, SourceWriter writer, MethodReference methodRef,
MethodReader method, boolean isStatic, List<AnnotationValue> paramNames, String script)
throws IOException {
int bodyParamCount = isStatic ? method.parameterCount() : method.parameterCount() - 1;
writer.append("if (!").appendMethodBody(methodRef).append(".$native)").ws().append('{').indent().newLine();
writer.appendMethodBody(methodRef).append(".$native").ws().append('=').ws().append("function(");
int count = method.parameterCount();
for (int i = 0; i < count; ++i) {
if (i > 0) {
writer.append(',').ws();
}
writer.append('_').append(context.getParameterName(i + 1));
}
writer.append(')').ws().append('{').softNewLine().indent();
writer.append("return (function(");
for (int i = 0; i < bodyParamCount; ++i) {
if (i > 0) {
writer.append(',').ws();
}
String name = paramNames.get(i).getString();
writer.append(name);
}
writer.append(')').ws().append('{').softNewLine().indent();
writer.append(script).softNewLine();
writer.outdent().append("})");
if (!isStatic) {
writer.append(".call");
}
writer.append('(');
for (int i = 0; i < count; ++i) {
if (i > 0) {
writer.append(',').ws();
}
writer.append('_').append(context.getParameterName(i + 1));
}
writer.append(");").softNewLine();
writer.outdent().append("};").softNewLine();
writer.appendMethodBody(methodRef).ws().append('=').ws().appendMethodBody(methodRef).append(".$native;")
.softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return ").appendMethodBody(methodRef).append('(');
for (int i = 0; i < count; ++i) {
if (i > 0) {
writer.append(',').ws();
}
writer.append(context.getParameterName(i + 1));
}
writer.append(");").softNewLine();
}
} }

View File

@ -27,22 +27,28 @@ import org.teavm.model.CallLocation;
public class TeaVMErrorReporter implements ErrorReporter { public class TeaVMErrorReporter implements ErrorReporter {
private Diagnostics diagnostics; private Diagnostics diagnostics;
private CallLocation location; private CallLocation location;
private boolean hasErrors;
public TeaVMErrorReporter(Diagnostics diagnostics, CallLocation location) { public TeaVMErrorReporter(Diagnostics diagnostics, CallLocation location) {
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.location = location; this.location = location;
} }
public boolean hasErrors() {
return hasErrors;
}
@Override @Override
public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) {
diagnostics.warning(location, "Error in @JSBody script line " + line + ", char " + lineOffset diagnostics.warning(location, "Warning in @JSBody script line " + line + ", char " + lineOffset
+ ": " + message); + ": " + message);
} }
@Override @Override
public void error(String message, String sourceName, int line, String lineSource, int lineOffset) { public void error(String message, String sourceName, int line, String lineSource, int lineOffset) {
diagnostics.error(location, "Error in @JSBody script line " + line + ", char " + lineOffset diagnostics.warning(location, "Error in @JSBody script line " + line + ", char " + lineOffset
+ ": " + message); + ": " + message);
hasErrors = true;
} }
@Override @Override

View File

@ -45,9 +45,6 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
<goals> <goals>
<goal>test-jar</goal> <goal>test-jar</goal>
</goals> </goals>
<configuration>
<classifier>tck</classifier>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
@ -67,6 +64,13 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -22,9 +22,9 @@ public final class Performance implements JSObject {
private Performance() { private Performance() {
} }
@JSBody(params = {}, script = "return Performance.now();") @JSBody(params = {}, script = "return window.performance.now();")
public static native double now(); public static native double now();
@JSBody(params = {}, script = "return typeof(Performance) !== 'undefined';") @JSBody(params = {}, script = "return typeof(window.performance) !== 'undefined';")
public static native boolean isSupported(); public static native boolean isSupported();
} }

View File

@ -86,7 +86,7 @@ public class AnnotationsTest {
@JSBody(params = "value", script = "" @JSBody(params = "value", script = ""
+ "return {" + "return {"
+ "value : value, " + "'value' : value, "
+ "testMethod : function(num) { return this.value + num + 1; }, " + "testMethod : function(num) { return this.value + num + 1; }, "
+ "renamedJSMethod : function(num) { return this.value + num + 2; }" + "renamedJSMethod : function(num) { return this.value + num + 2; }"
+ "};") + "};")

View File

@ -1,15 +1,14 @@
package org.teavm.samples.kotlin package org.teavm.samples.kotlin
import org.teavm.jso.* import org.teavm.jso.*
import org.teavm.dom.browser.* import org.teavm.jso.browser.*
import org.teavm.dom.html.* import org.teavm.jso.dom.html.*
import org.teavm.dom.events.* import org.teavm.jso.dom.events.*
fun main(args : Array<String>) { fun main(args : Array<String>) {
var window = JS.getGlobal() as Window; var document = Window.current().getDocument();
var document = window.getDocument();
document.getElementById("hello-kotlin").addEventListener("click", EventListener<MouseEvent>() { document.getElementById("hello-kotlin").addEventListener("click", EventListener<MouseEvent>() {
window.alert("Hello, developer!"); Window.alert("Hello, developer!");
}) })
} }

View File

@ -52,7 +52,8 @@
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-jso</artifactId> <artifactId>teavm-jso</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<classifier>tck</classifier> <classifier>tests</classifier>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
@ -83,6 +84,12 @@
<phase>process-test-classes</phase> <phase>process-test-classes</phase>
<configuration> <configuration>
<minifying>false</minifying> <minifying>false</minifying>
<scanDependencies>true</scanDependencies>
<wildcards>
<wildcard>org.teavm.classlib.**.*Test</wildcard>
<wildcard>org.teavm.jso.**.*Test</wildcard>
<wildcard>org.teavm.platform.metadata.*Test</wildcard>
</wildcards>
<properties> <properties>
<java.util.Locale.available>en, en_US, en_GB, ru, ru_RU</java.util.Locale.available> <java.util.Locale.available>en, en_US, en_GB, ru, ru_RU</java.util.Locale.available>
</properties> </properties>