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;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -479,8 +480,11 @@ public class AstWriter {
private void print(PropertyGet node) throws IOException {
print(node.getLeft(), PRECEDENCE_MEMBER);
writer.ws().append('.').ws();
writer.append('.');
Map<String, String> oldNameMap = nameMap;
nameMap = Collections.emptyMap();
print(node.getRight());
nameMap = oldNameMap;
}
private void print(FunctionCall node, int precedence) throws IOException {
@ -597,7 +601,7 @@ public class AstWriter {
}
private void print(ObjectLiteral node) throws IOException {
writer.append('{');
writer.append('{').ws();
if (node.getElements() != null && !node.getElements().isEmpty()) {
print(node.getElements().get(0));
for (int i = 1; i < node.getElements().size(); ++i) {
@ -605,7 +609,7 @@ public class AstWriter {
print(node.getElements().get(i));
}
}
writer.append('}');
writer.ws().append('}');
}
private void print(ObjectProperty node) throws IOException {
@ -614,7 +618,10 @@ public class AstWriter {
} else if (node.isSetterMethod()) {
writer.append("set ");
}
Map<String, String> oldNameMap = nameMap;
nameMap = Collections.emptyMap();
print(node.getLeft());
nameMap = oldNameMap;
if (!node.isMethod()) {
writer.ws().append(':').ws();
}
@ -631,7 +638,7 @@ public class AstWriter {
}
writer.append('(');
printList(node.getParams());
writer.append(')');
writer.append(')').ws();
if (node.isExpressionClosure()) {
if (node.getBody().getLastChild() instanceof ReturnStatement) {
@ -642,10 +649,6 @@ public class AstWriter {
}
} else {
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.util.List;
import org.mozilla.javascript.CompilerEnvirons;
import org.mozilla.javascript.IRFactory;
import org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.NodeVisitor;
import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.spi.Generator;
import org.teavm.javascript.spi.GeneratorContext;
@ -45,29 +43,86 @@ public class JSBodyGenerator implements Generator {
AnnotationReader annot = method.getAnnotations().get(JSBodyImpl.class.getName());
boolean isStatic = annot.getValue("isStatic").getBoolean();
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();
env.setRecoverFromErrors(true);
IRFactory factory = new IRFactory(env, new TeaVMErrorReporter(context.getDiagnostics(),
new CallLocation(methodRef)));
String script = annot.getValue("script").getString();
AstRoot rootNode;
try {
rootNode = factory.parse(new StringReader(script), null, 0);
} catch (IOException e) {
context.getDiagnostics().error(new CallLocation(methodRef), "IO error parsing JSBody script");
env.setLanguageVersion(Context.VERSION_1_8);
env.setIdeMode(true);
JSParser parser = new JSParser(env, errorReporter);
parser.enterFunction();
AstRoot rootNode = parser.parse(new StringReader(script), null, 0);
parser.exitFunction();
if (errorReporter.hasErrors()) {
generateBloated(context, writer, methodRef, method, isStatic, paramNames, script);
return;
}
rootNode.visit(new NodeVisitor() {
@Override
public boolean visit(AstNode node) {
return false;
}
});
AstWriter astWriter = new AstWriter(writer);
int paramIndex = 1;
if (!isStatic) {
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 {
private Diagnostics diagnostics;
private CallLocation location;
private boolean hasErrors;
public TeaVMErrorReporter(Diagnostics diagnostics, CallLocation location) {
this.diagnostics = diagnostics;
this.location = location;
}
public boolean hasErrors() {
return hasErrors;
}
@Override
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);
}
@Override
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);
hasErrors = true;
}
@Override

View File

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

View File

@ -22,9 +22,9 @@ public final class Performance implements JSObject {
private Performance() {
}
@JSBody(params = {}, script = "return Performance.now();")
@JSBody(params = {}, script = "return window.performance.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();
}

View File

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

View File

@ -1,15 +1,14 @@
package org.teavm.samples.kotlin
import org.teavm.jso.*
import org.teavm.dom.browser.*
import org.teavm.dom.html.*
import org.teavm.dom.events.*
import org.teavm.jso.browser.*
import org.teavm.jso.dom.html.*
import org.teavm.jso.dom.events.*
fun main(args : Array<String>) {
var window = JS.getGlobal() as Window;
var document = window.getDocument();
var document = Window.current().getDocument();
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>
<artifactId>teavm-jso</artifactId>
<version>${project.version}</version>
<classifier>tck</classifier>
<classifier>tests</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
@ -83,6 +84,12 @@
<phase>process-test-classes</phase>
<configuration>
<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>
<java.util.Locale.available>en, en_US, en_GB, ru, ru_RU</java.util.Locale.available>
</properties>