mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
JS: store global object in a variable to avoid name clashes between generated declarations (in minified mode) and global declarations
This commit is contained in:
parent
68efdddddf
commit
64ae44ee01
|
@ -464,11 +464,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
for (String key : controller.getEntryPoints().keySet()) {
|
for (String key : controller.getEntryPoints().keySet()) {
|
||||||
writer.append("var ").append(key).append(";").softNewLine();
|
writer.append("var ").append(key).append(";").softNewLine();
|
||||||
}
|
}
|
||||||
writer.append("(function()").ws().append("{").newLine();
|
writer.append("(function($rt_globals)").ws().append("{").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printWrapperEnd(SourceWriter writer) throws IOException {
|
private void printWrapperEnd(SourceWriter writer) throws IOException {
|
||||||
writer.append("})();").newLine();
|
writer.append("})(this);").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printStats(Renderer renderer, int totalSize) {
|
private void printStats(Renderer renderer, int totalSize) {
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
package org.teavm.backend.javascript.rendering;
|
package org.teavm.backend.javascript.rendering;
|
||||||
|
|
||||||
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;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import org.mozilla.javascript.Node;
|
import org.mozilla.javascript.Node;
|
||||||
import org.mozilla.javascript.ScriptRuntime;
|
import org.mozilla.javascript.ScriptRuntime;
|
||||||
import org.mozilla.javascript.Token;
|
import org.mozilla.javascript.Token;
|
||||||
|
@ -92,13 +92,16 @@ public class AstWriter {
|
||||||
public static final int PRECEDENCE_COMMA = 18;
|
public static final int PRECEDENCE_COMMA = 18;
|
||||||
private SourceWriter writer;
|
private SourceWriter writer;
|
||||||
private Map<String, NameEmitter> nameMap = new HashMap<>();
|
private Map<String, NameEmitter> nameMap = new HashMap<>();
|
||||||
|
private boolean rootScope = true;
|
||||||
private Set<String> aliases = new HashSet<>();
|
private Set<String> aliases = new HashSet<>();
|
||||||
|
private Function<String, NameEmitter> globalNameWriter;
|
||||||
|
|
||||||
public AstWriter(SourceWriter writer) {
|
public AstWriter(SourceWriter writer, Function<String, NameEmitter> globalNameWriter) {
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
|
this.globalNameWriter = globalNameWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void declareName(String name) {
|
public void declareName(String name) {
|
||||||
if (nameMap.containsKey(name)) {
|
if (nameMap.containsKey(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -124,14 +127,25 @@ public class AstWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hoist(AstNode node) {
|
public void hoist(AstNode node) {
|
||||||
|
declareName("arguments");
|
||||||
node.visit(n -> {
|
node.visit(n -> {
|
||||||
if (n instanceof Scope) {
|
if (n instanceof Scope) {
|
||||||
Scope scope = (Scope) n;
|
var scope = (Scope) n;
|
||||||
if (scope.getSymbolTable() != null) {
|
if (scope.getSymbolTable() != null) {
|
||||||
for (String name : scope.getSymbolTable().keySet()) {
|
for (var name : scope.getSymbolTable().keySet()) {
|
||||||
declareName(name);
|
declareName(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (n instanceof CatchClause) {
|
||||||
|
var clause = (CatchClause) n;
|
||||||
|
var name = clause.getVarName().getIdentifier();
|
||||||
|
declareName(name);
|
||||||
|
} else if (n instanceof VariableInitializer) {
|
||||||
|
var initializer = (VariableInitializer) n;
|
||||||
|
if (initializer.getTarget() instanceof Name) {
|
||||||
|
var id = ((Name) initializer.getTarget()).getIdentifier();
|
||||||
|
declareName(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -488,10 +502,10 @@ 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.append('.');
|
writer.append('.');
|
||||||
Map<String, NameEmitter> oldNameMap = nameMap;
|
var oldRootScope = rootScope;
|
||||||
nameMap = Collections.emptyMap();
|
rootScope = false;
|
||||||
print(node.getRight());
|
print(node.getRight());
|
||||||
nameMap = oldNameMap;
|
rootScope = oldRootScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void print(FunctionCall node, int precedence) throws IOException {
|
private void print(FunctionCall node, int precedence) throws IOException {
|
||||||
|
@ -627,11 +641,19 @@ public class AstWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void print(Name node, int precedence) throws IOException {
|
private void print(Name node, int precedence) throws IOException {
|
||||||
NameEmitter alias = nameMap.get(node.getIdentifier());
|
if (rootScope) {
|
||||||
if (alias == null) {
|
var alias = nameMap.get(node.getIdentifier());
|
||||||
alias = prec -> writer.append(node.getIdentifier());
|
if (alias == null) {
|
||||||
|
if (globalNameWriter != null) {
|
||||||
|
alias = globalNameWriter.apply(node.getIdentifier());
|
||||||
|
} else {
|
||||||
|
alias = prec -> writer.append(node.getIdentifier());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alias.emit(precedence);
|
||||||
|
} else {
|
||||||
|
writer.append(node.getIdentifier());
|
||||||
}
|
}
|
||||||
alias.emit(precedence);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void print(RegExpLiteral node) throws IOException {
|
private void print(RegExpLiteral node) throws IOException {
|
||||||
|
@ -662,10 +684,10 @@ public class AstWriter {
|
||||||
} else if (node.isSetterMethod()) {
|
} else if (node.isSetterMethod()) {
|
||||||
writer.append("set ");
|
writer.append("set ");
|
||||||
}
|
}
|
||||||
Map<String, NameEmitter> oldNameMap = nameMap;
|
var oldRootScope = rootScope;
|
||||||
nameMap = Collections.emptyMap();
|
rootScope = false;
|
||||||
print(node.getLeft());
|
print(node.getLeft());
|
||||||
nameMap = oldNameMap;
|
rootScope = oldRootScope;
|
||||||
if (!node.isMethod()) {
|
if (!node.isMethod()) {
|
||||||
writer.ws().append(':').ws();
|
writer.ws().append(':').ws();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.javascript.rendering;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
|
|
||||||
|
public class DefaultGlobalNameWriter implements Function<String, NameEmitter> {
|
||||||
|
private SourceWriter writer;
|
||||||
|
|
||||||
|
public DefaultGlobalNameWriter(SourceWriter writer) {
|
||||||
|
this.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NameEmitter apply(String s) {
|
||||||
|
if (s.startsWith("$rt_") || s.startsWith("Long_") || s.equals("Long")) {
|
||||||
|
return prec -> writer.append(s);
|
||||||
|
}
|
||||||
|
return prec -> writer.append("$rt_globals").append('.').append(s);
|
||||||
|
}
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ public class RuntimeRenderer {
|
||||||
AstRoot ast = parseRuntime(name);
|
AstRoot ast = parseRuntime(name);
|
||||||
ast.visit(new StringConstantElimination());
|
ast.visit(new StringConstantElimination());
|
||||||
new RuntimeAstTransformer(writer.getNaming()).accept(ast);
|
new RuntimeAstTransformer(writer.getNaming()).accept(ast);
|
||||||
AstWriter astWriter = new AstWriter(writer);
|
var astWriter = new AstWriter(writer, new DefaultGlobalNameWriter(writer));
|
||||||
astWriter.hoist(ast);
|
astWriter.hoist(ast);
|
||||||
astWriter.print(ast);
|
astWriter.print(ast);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.javascript.rendering;
|
package org.teavm.backend.javascript.rendering;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mozilla.javascript.CompilerEnvirons;
|
import org.mozilla.javascript.CompilerEnvirons;
|
||||||
import org.mozilla.javascript.Context;
|
import org.mozilla.javascript.Context;
|
||||||
import org.mozilla.javascript.ast.AstRoot;
|
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriterBuilder;
|
import org.teavm.backend.javascript.codegen.SourceWriterBuilder;
|
||||||
|
|
||||||
|
@ -30,227 +28,269 @@ public class AstWriterTest {
|
||||||
private StringBuilder sb = new StringBuilder();
|
private StringBuilder sb = new StringBuilder();
|
||||||
private SourceWriter sourceWriter;
|
private SourceWriter sourceWriter;
|
||||||
private AstWriter writer;
|
private AstWriter writer;
|
||||||
|
private AstWriter writerWithGlobals;
|
||||||
|
|
||||||
public AstWriterTest() {
|
public AstWriterTest() {
|
||||||
SourceWriterBuilder builder = new SourceWriterBuilder(null);
|
var builder = new SourceWriterBuilder(null);
|
||||||
builder.setMinified(true);
|
builder.setMinified(true);
|
||||||
sourceWriter = builder.build(sb);
|
sourceWriter = builder.build(sb);
|
||||||
writer = new AstWriter(sourceWriter);
|
writer = new AstWriter(sourceWriter, null);
|
||||||
|
writerWithGlobals = new AstWriter(sourceWriter, name -> prec -> sourceWriter.append("globals.").append(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesReturn() throws IOException {
|
public void writesReturn() throws IOException {
|
||||||
assertThat(transform("return x;"), is("return x;"));
|
assertEquals("return x;", transform("return x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesEmptyReturn() throws IOException {
|
public void writesEmptyReturn() throws IOException {
|
||||||
assertThat(transform("return;"), is("return;"));
|
assertEquals("return;", transform("return;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesThrow() throws IOException {
|
public void writesThrow() throws IOException {
|
||||||
assertThat(transform("throw x;"), is("throw x;"));
|
assertEquals("throw x;", transform("throw x;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesBreak() throws IOException {
|
public void writesBreak() throws IOException {
|
||||||
assertThat(transform("a: while (true) { break a; }"), is("a:while(true){break a;}"));
|
assertEquals("a:while(true){break a;}", transform("a: while (true) { break a; }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesEmptyBreak() throws IOException {
|
public void writesEmptyBreak() throws IOException {
|
||||||
assertThat(transform("while(true) { break; }"), is("while(true){break;}"));
|
assertEquals("while(true){break;}", transform("while(true) { break; }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesContinue() throws IOException {
|
public void writesContinue() throws IOException {
|
||||||
assertThat(transform("a: while (true) { continue a; }"), is("a:while(true){continue a;}"));
|
assertEquals("a:while(true){continue a;}", transform("a: while (true) { continue a; }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesEmptyContinue() throws IOException {
|
public void writesEmptyContinue() throws IOException {
|
||||||
assertThat(transform("while(true) { continue; }"), is("while(true){continue;}"));
|
assertEquals("while(true){continue;}", transform("while(true) { continue; }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesBlock() throws IOException {
|
public void writesBlock() throws IOException {
|
||||||
assertThat(transform("{ foo(); bar(); }"), is("{foo();bar();}"));
|
assertEquals("{foo();bar();}", transform("{ foo(); bar(); }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesTryCatch() throws IOException {
|
public void writesTryCatch() throws IOException {
|
||||||
assertThat(transform("try { foo(); } catch (e) { alert(e); }"), is("try {foo();}catch(e){alert(e);}"));
|
assertEquals("try {foo();}catch(e){alert(e);}", transform("try { foo(); } catch (e) { alert(e); }"));
|
||||||
assertThat(transform("try { foo(); } finally { close(); }"), is("try {foo();}finally {close();}"));
|
assertEquals("try {foo();}finally {close();}", transform("try { foo(); } finally { close(); }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesFor() throws IOException {
|
public void writesFor() throws IOException {
|
||||||
assertThat(transform("for (var i = 0; i < array.length; ++i,++j) foo(array[i]);"),
|
assertEquals(
|
||||||
is("for(var i=0;i<array.length;++i,++j)foo(array[i]);"));
|
"for(var i=0;i<array.length;++i,++j)foo(array[i]);",
|
||||||
|
transform("for (var i = 0; i < array.length; ++i,++j) foo(array[i]);")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesEmptyFor() throws IOException {
|
public void writesEmptyFor() throws IOException {
|
||||||
assertThat(transform("for (;;) foo();"), is("for(;;)foo();"));
|
assertEquals("for(;;)foo();", transform("for (;;) foo();"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesForIn() throws IOException {
|
public void writesForIn() throws IOException {
|
||||||
assertThat(transform("for (var property in window) alert(property);"),
|
assertEquals(
|
||||||
is("for(var property in window)alert(property);"));
|
"for(var property in window)alert(property);",
|
||||||
|
transform("for (var property in window) alert(property);")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesWhile() throws IOException {
|
public void writesWhile() throws IOException {
|
||||||
assertThat(transform("while (shouldProceed()) proceed();"), is("while(shouldProceed())proceed();"));
|
assertEquals("while(shouldProceed())proceed();", transform("while (shouldProceed()) proceed();"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesDoWhile() throws IOException {
|
public void writesDoWhile() throws IOException {
|
||||||
assertThat(transform("do proceed(); while(shouldRepeat());"), is("do proceed();while(shouldRepeat());"));
|
assertEquals("do proceed();while(shouldRepeat());", transform("do proceed(); while(shouldRepeat());"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesIfElse() throws IOException {
|
public void writesIfElse() throws IOException {
|
||||||
assertThat(transform("if (test()) performTrue(); else performFalse();"),
|
assertEquals(
|
||||||
is("if(test())performTrue();else performFalse();"));
|
"if(test())performTrue();else performFalse();",
|
||||||
|
transform("if (test()) performTrue(); else performFalse();")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesIf() throws IOException {
|
public void writesIf() throws IOException {
|
||||||
assertThat(transform("if (shouldPerform()) perform();"), is("if(shouldPerform())perform();"));
|
assertEquals("if(shouldPerform())perform();", transform("if (shouldPerform()) perform();"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesSwitch() throws IOException {
|
public void writesSwitch() throws IOException {
|
||||||
assertThat(transform("switch (c) { "
|
assertEquals(
|
||||||
|
"switch(c){case '.':case '?':matchAny();break;case '*':matchSequence();break;"
|
||||||
|
+ "default:matchChar(c);break;}",
|
||||||
|
transform("switch (c) { "
|
||||||
+ "case '.': case '?': matchAny(); break; "
|
+ "case '.': case '?': matchAny(); break; "
|
||||||
+ "case '*': matchSequence(); break;"
|
+ "case '*': matchSequence(); break;"
|
||||||
+ "default: matchChar(c); break; } "),
|
+ "default: matchChar(c); break; } ")
|
||||||
is("switch(c){case '.':case '?':matchAny();break;case '*':matchSequence();break;"
|
);
|
||||||
+ "default:matchChar(c);break;}"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesLet() throws IOException {
|
public void writesLet() throws IOException {
|
||||||
assertThat(transform("let x = 1; alert(x);"), is("let x=1;alert(x);"));
|
assertEquals("let x=1;alert(x);", transform("let x = 1; alert(x);"));
|
||||||
assertThat(transform("let x = 1, y; alert(x,y);"), is("let x=1,y;alert(x,y);"));
|
assertEquals("let x=1,y;alert(x,y);", transform("let x = 1, y; alert(x,y);"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesConst() throws IOException {
|
public void writesConst() throws IOException {
|
||||||
assertThat(transform("const x = 1,y = 2; alert(x,y);"), is("const x=1,y=2;alert(x,y);"));
|
assertEquals("const xx=1,yy=2;alert(xx,yy);", transform("const xx = 1,yy = 2; alert(xx,yy);"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesElementGet() throws IOException {
|
public void writesElementGet() throws IOException {
|
||||||
assertThat(transform("return array[i];"), is("return array[i];"));
|
assertEquals("return array[i];", transform("return array[i];"));
|
||||||
assertThat(transform("return array[i][j];"), is("return array[i][j];"));
|
assertEquals("return array[i][j];", transform("return array[i][j];"));
|
||||||
assertThat(transform("return (array[i])[j];"), is("return array[i][j];"));
|
assertEquals("return array[i][j];", transform("return (array[i])[j];"));
|
||||||
assertThat(transform("return (a + b)[i];"), is("return (a+b)[i];"));
|
assertEquals("return (a+b)[i];", transform("return (a + b)[i];"));
|
||||||
assertThat(transform("return a + b[i];"), is("return a+b[i];"));
|
assertEquals("return a+b[i];", transform("return a + b[i];"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesPropertyGet() throws IOException {
|
public void writesPropertyGet() throws IOException {
|
||||||
assertThat(transform("return array.length;"), is("return array.length;"));
|
assertEquals("return array.length;", transform("return array.length;"));
|
||||||
assertThat(transform("return (array).length;"), is("return array.length;"));
|
assertEquals("return array.length;", transform("return (array).length;"));
|
||||||
assertThat(transform("return (x + y).toString();"), is("return (x+y).toString();"));
|
assertEquals("return (x+y).toString();", transform("return (x + y).toString();"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesFunctionCall() throws IOException {
|
public void writesFunctionCall() throws IOException {
|
||||||
assertThat(transform("return f(x);"), is("return f(x);"));
|
assertEquals("return f(x);", transform("return f(x);"));
|
||||||
assertThat(transform("return (f)(x);"), is("return f(x);"));
|
assertEquals("return f(x);", transform("return (f)(x);"));
|
||||||
assertThat(transform("return (f + g)(x);"), is("return (f+g)(x);"));
|
assertEquals("return (f+g)(x);", transform("return (f + g)(x);"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesConstructorCall() throws IOException {
|
public void writesConstructorCall() throws IOException {
|
||||||
assertThat(transform("return new (f + g)(x);"), is("return new (f+g)(x);"));
|
assertEquals("return new (f+g)(x);", transform("return new (f + g)(x);"));
|
||||||
assertThat(transform("return new f + g(x);"), is("return new f()+g(x);"));
|
assertEquals("return new f()+g(x);", transform("return new f + g(x);"));
|
||||||
assertThat(transform("return new f()(x);"), is("return new f()(x);"));
|
assertEquals("return new f()(x);", transform("return new f()(x);"));
|
||||||
assertThat(transform("return (new f())(x);"), is("return new f()(x);"));
|
assertEquals("return new f()(x);", transform("return (new f())(x);"));
|
||||||
assertThat(transform("return new (f())(x);"), is("return new (f())(x);"));
|
assertEquals("return new (f())(x);", transform("return new (f())(x);"));
|
||||||
assertThat(transform("return new f[0](x);"), is("return new f[0](x);"));
|
assertEquals("return new f[0](x);", transform("return new f[0](x);"));
|
||||||
assertThat(transform("return (new f[0](x));"), is("return new f[0](x);"));
|
assertEquals("return new f[0](x);", transform("return (new f[0](x));"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesConditionalExpr() throws IOException {
|
public void writesConditionalExpr() throws IOException {
|
||||||
assertThat(transform("return cond ? 1 : 0;"), is("return cond?1:0;"));
|
assertEquals("return cond?1:0;", transform("return cond ? 1 : 0;"));
|
||||||
assertThat(transform("return a < b ? -1 : a > b ? 1 : 0;"), is("return a<b? -1:a>b?1:0;"));
|
assertEquals("return a<b? -1:a>b?1:0;", transform("return a < b ? -1 : a > b ? 1 : 0;"));
|
||||||
assertThat(transform("return a < b ? -1 : (a > b ? 1 : 0);"), is("return a<b? -1:a>b?1:0;"));
|
assertEquals("return a<b? -1:a>b?1:0;", transform("return a < b ? -1 : (a > b ? 1 : 0);"));
|
||||||
assertThat(transform("return (a < b ? x == y : x != y) ? 1 : 0;"), is("return (a<b?x==y:x!=y)?1:0;"));
|
assertEquals("return (a<b?x==y:x!=y)?1:0;", transform("return (a < b ? x == y : x != y) ? 1 : 0;"));
|
||||||
assertThat(transform("return a < b ? (x > y ? x : y) : z"), is("return a<b?(x>y?x:y):z;"));
|
assertEquals("return a<b?(x>y?x:y):z;", transform("return a < b ? (x > y ? x : y) : z"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesRegExp() throws IOException {
|
public void writesRegExp() throws IOException {
|
||||||
assertThat(transform("return /[a-z]+/.match(text);"), is("return /[a-z]+/.match(text);"));
|
assertEquals("return /[a-z]+/.match(text);", transform("return /[a-z]+/.match(text);"));
|
||||||
assertThat(transform("return /[a-z]+/ig.match(text);"), is("return /[a-z]+/ig.match(text);"));
|
assertEquals("return /[a-z]+/ig.match(text);", transform("return /[a-z]+/ig.match(text);"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesArrayLiteral() throws IOException {
|
public void writesArrayLiteral() throws IOException {
|
||||||
assertThat(transform("return [];"), is("return [];"));
|
assertEquals("return [];", transform("return [];"));
|
||||||
assertThat(transform("return [a, b + c];"), is("return [a,b+c];"));
|
assertEquals("return [a,b+c];", transform("return [a, b + c];"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesObjectLiteral() throws IOException {
|
public void writesObjectLiteral() throws IOException {
|
||||||
assertThat(transform("return {};"), is("return {};"));
|
assertEquals("return {};", transform("return {};"));
|
||||||
assertThat(transform("return { foo : bar };"), is("return {foo:bar};"));
|
assertEquals("return {foo:bar};", transform("return { foo : bar };"));
|
||||||
assertThat(transform("return { foo : bar };"), is("return {foo:bar};"));
|
assertEquals("return {foo:bar};", transform("return { foo : bar };"));
|
||||||
assertThat(transform("return { _foo : bar, get foo() { return this._foo; } };"),
|
assertEquals(
|
||||||
is("return {_foo:bar,get foo(){return this._foo;}};"));
|
"return {_foo:bar,get foo(){return this._foo;}};",
|
||||||
|
transform("return { _foo : bar, get foo() { return this._foo; } };")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesFunction() throws IOException {
|
public void writesFunction() throws IOException {
|
||||||
assertThat(transform("return function f(x, y) { return x + y; };"),
|
assertEquals(
|
||||||
is("return function f(x,y){return x+y;};"));
|
"return function f(x,y){return x+y;};",
|
||||||
|
transform("return function f(x, y) { return x + y; };")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesUnary() throws IOException {
|
public void writesUnary() throws IOException {
|
||||||
assertThat(transform("return -a;"), is("return -a;"));
|
assertEquals("return -a;", transform("return -a;"));
|
||||||
assertThat(transform("return -(a + b);"), is("return -(a+b);"));
|
assertEquals("return -(a+b);", transform("return -(a + b);"));
|
||||||
assertThat(transform("return -a + b;"), is("return -a+b;"));
|
assertEquals("return -a+b;", transform("return -a + b;"));
|
||||||
assertThat(transform("return (-a) + b;"), is("return -a+b;"));
|
assertEquals("return -a+b;", transform("return (-a) + b;"));
|
||||||
assertThat(transform("return (-f)(x);"), is("return ( -f)(x);"));
|
assertEquals("return ( -f)(x);", transform("return (-f)(x);"));
|
||||||
assertThat(transform("return typeof a;"), is("return typeof a;"));
|
assertEquals("return typeof a;", transform("return typeof a;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesPostfix() throws IOException {
|
public void writesPostfix() throws IOException {
|
||||||
assertThat(transform("return a++;"), is("return a++;"));
|
assertEquals("return a++;", transform("return a++;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void respectsPrecedence() throws IOException {
|
public void respectsPrecedence() throws IOException {
|
||||||
assertThat(transform("return a + b + c;"), is("return a+b+c;"));
|
assertEquals("return a+b+c;", transform("return a + b + c;"));
|
||||||
assertThat(transform("return (a + b) + c;"), is("return a+b+c;"));
|
assertEquals("return a+b+c;", transform("return (a + b) + c;"));
|
||||||
assertThat(transform("return a + (b + c);"), is("return a+b+c;"));
|
assertEquals("return a+b+c;", transform("return a + (b + c);"));
|
||||||
assertThat(transform("return a - b + c;"), is("return a -b+c;"));
|
assertEquals("return a -b+c;", transform("return a - b + c;"));
|
||||||
assertThat(transform("return (a - b) + c;"), is("return a -b+c;"));
|
assertEquals("return a -b+c;", transform("return (a - b) + c;"));
|
||||||
assertThat(transform("return a - (b + c);"), is("return a -(b+c);"));
|
assertEquals("return a -(b+c);", transform("return a - (b + c);"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writesDelete() throws IOException {
|
public void writesDelete() throws IOException {
|
||||||
assertThat(transform("delete a.b;"), is("delete a.b;"));
|
assertEquals("delete a.b;", transform("delete a.b;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writesGlobalRef() throws IOException {
|
||||||
|
assertEquals(
|
||||||
|
"function(x){return x+globals.y;}",
|
||||||
|
transformGlobals("function(x) { return x + y; }")
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"try {globals.foo();}catch(x){globals.foo(x);}",
|
||||||
|
transformGlobals("try { foo(); } catch (x) { foo(x); }")
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"for(var i=0;i<10;++i){globals.foo(i,globals.j);}",
|
||||||
|
transformGlobals("for (var i = 0; i < 10; ++i) { foo(i, j); }")
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"function(){var x=0;globals.foo(x,globals.y);}",
|
||||||
|
transformGlobals("function() { var x = 0; foo(x, y); }")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String transform(String text) throws IOException {
|
private String transform(String text) throws IOException {
|
||||||
|
return transform(text, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String transformGlobals(String text) throws IOException {
|
||||||
|
return transform(text, writerWithGlobals);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String transform(String text, AstWriter writer) throws IOException {
|
||||||
sb.setLength(0);
|
sb.setLength(0);
|
||||||
CompilerEnvirons env = new CompilerEnvirons();
|
var env = new CompilerEnvirons();
|
||||||
env.setRecoverFromErrors(true);
|
env.setRecoverFromErrors(true);
|
||||||
env.setLanguageVersion(Context.VERSION_1_8);
|
env.setLanguageVersion(Context.VERSION_1_8);
|
||||||
JSParser factory = new JSParser(env);
|
var factory = new JSParser(env);
|
||||||
factory.enterFunction();
|
factory.enterFunction();
|
||||||
AstRoot rootNode = factory.parse(new StringReader(text), null, 0);
|
var rootNode = factory.parse(new StringReader(text), null, 0);
|
||||||
factory.exitFunction();
|
factory.exitFunction();
|
||||||
writer.hoist(rootNode);
|
writer.hoist(rootNode);
|
||||||
writer.print(rootNode);
|
writer.print(rootNode);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.mozilla.javascript.ast.AstNode;
|
||||||
import org.mozilla.javascript.ast.Block;
|
import org.mozilla.javascript.ast.Block;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.rendering.AstWriter;
|
import org.teavm.backend.javascript.rendering.AstWriter;
|
||||||
|
import org.teavm.backend.javascript.rendering.DefaultGlobalNameWriter;
|
||||||
import org.teavm.backend.javascript.rendering.Precedence;
|
import org.teavm.backend.javascript.rendering.Precedence;
|
||||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||||
import org.teavm.backend.javascript.spi.InjectorContext;
|
import org.teavm.backend.javascript.spi.InjectorContext;
|
||||||
|
@ -29,17 +30,19 @@ import org.teavm.model.MethodReference;
|
||||||
class JSBodyAstEmitter implements JSBodyEmitter {
|
class JSBodyAstEmitter implements JSBodyEmitter {
|
||||||
private boolean isStatic;
|
private boolean isStatic;
|
||||||
private AstNode ast;
|
private AstNode ast;
|
||||||
|
private AstNode rootAst;
|
||||||
private String[] parameterNames;
|
private String[] parameterNames;
|
||||||
|
|
||||||
JSBodyAstEmitter(boolean isStatic, AstNode ast, String[] parameterNames) {
|
JSBodyAstEmitter(boolean isStatic, AstNode ast, AstNode rootAst, String[] parameterNames) {
|
||||||
this.isStatic = isStatic;
|
this.isStatic = isStatic;
|
||||||
this.ast = ast;
|
this.ast = ast;
|
||||||
|
this.rootAst = rootAst;
|
||||||
this.parameterNames = parameterNames;
|
this.parameterNames = parameterNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emit(InjectorContext context) throws IOException {
|
public void emit(InjectorContext context) throws IOException {
|
||||||
AstWriter astWriter = new AstWriter(context.getWriter());
|
var astWriter = new AstWriter(context.getWriter(), new DefaultGlobalNameWriter(context.getWriter()));
|
||||||
int paramIndex = 0;
|
int paramIndex = 0;
|
||||||
if (!isStatic) {
|
if (!isStatic) {
|
||||||
int index = paramIndex++;
|
int index = paramIndex++;
|
||||||
|
@ -51,7 +54,7 @@ class JSBodyAstEmitter implements JSBodyEmitter {
|
||||||
astWriter.declareNameEmitter(parameterNames[i],
|
astWriter.declareNameEmitter(parameterNames[i],
|
||||||
prec -> context.writeExpr(context.getArgument(index), convert(prec)));
|
prec -> context.writeExpr(context.getArgument(index), convert(prec)));
|
||||||
}
|
}
|
||||||
astWriter.hoist(ast);
|
astWriter.hoist(rootAst);
|
||||||
astWriter.print(ast, convert(context.getPrecedence()));
|
astWriter.print(ast, convert(context.getPrecedence()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +142,7 @@ class JSBodyAstEmitter implements JSBodyEmitter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
public void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||||
AstWriter astWriter = new AstWriter(writer);
|
var astWriter = new AstWriter(writer, new DefaultGlobalNameWriter(writer));
|
||||||
int paramIndex = 1;
|
int paramIndex = 1;
|
||||||
if (!isStatic) {
|
if (!isStatic) {
|
||||||
int index = paramIndex++;
|
int index = paramIndex++;
|
||||||
|
@ -149,7 +152,7 @@ class JSBodyAstEmitter implements JSBodyEmitter {
|
||||||
int index = paramIndex++;
|
int index = paramIndex++;
|
||||||
astWriter.declareNameEmitter(parameterNames[i], prec -> writer.append(context.getParameterName(index)));
|
astWriter.declareNameEmitter(parameterNames[i], prec -> writer.append(context.getParameterName(index)));
|
||||||
}
|
}
|
||||||
astWriter.hoist(ast);
|
astWriter.hoist(rootAst);
|
||||||
if (ast instanceof Block) {
|
if (ast instanceof Block) {
|
||||||
for (Node child = ast.getFirstChild(); child != null; child = child.getNext()) {
|
for (Node child = ast.getFirstChild(); child != null; child = child.getNext()) {
|
||||||
astWriter.print((AstNode) child);
|
astWriter.print((AstNode) child);
|
||||||
|
|
|
@ -26,7 +26,6 @@ import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.mozilla.javascript.CompilerEnvirons;
|
import org.mozilla.javascript.CompilerEnvirons;
|
||||||
import org.mozilla.javascript.Context;
|
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.FunctionNode;
|
import org.mozilla.javascript.ast.FunctionNode;
|
||||||
import org.teavm.backend.javascript.rendering.JSParser;
|
import org.teavm.backend.javascript.rendering.JSParser;
|
||||||
|
@ -556,27 +555,26 @@ class JSClassProcessor {
|
||||||
.toArray(String[]::new) : new String[0];
|
.toArray(String[]::new) : new String[0];
|
||||||
|
|
||||||
// Parse JS script
|
// Parse JS script
|
||||||
TeaVMErrorReporter errorReporter = new TeaVMErrorReporter(diagnostics,
|
var errorReporter = new TeaVMErrorReporter(diagnostics, new CallLocation(methodToProcess.getReference()));
|
||||||
new CallLocation(methodToProcess.getReference()));
|
var env = new CompilerEnvirons();
|
||||||
CompilerEnvirons env = new CompilerEnvirons();
|
|
||||||
env.setRecoverFromErrors(true);
|
env.setRecoverFromErrors(true);
|
||||||
env.setLanguageVersion(Context.VERSION_1_8);
|
env.setLanguageVersion(Context.VERSION_1_8);
|
||||||
env.setIdeMode(true);
|
env.setIdeMode(true);
|
||||||
JSParser parser = new JSParser(env, errorReporter);
|
var parser = new JSParser(env, errorReporter);
|
||||||
AstRoot rootNode;
|
AstRoot rootNode;
|
||||||
try {
|
try {
|
||||||
rootNode = (AstRoot) parser.parseAsObject(new StringReader("function(){" + script + "}"), null, 0);
|
rootNode = (AstRoot) parser.parseAsObject(new StringReader("function(){" + script + "}"), null, 0);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("IO Error occurred", e);
|
throw new RuntimeException("IO Error occurred", e);
|
||||||
}
|
}
|
||||||
AstNode body = ((FunctionNode) rootNode.getFirstChild()).getBody();
|
var body = ((FunctionNode) rootNode.getFirstChild()).getBody();
|
||||||
|
|
||||||
repository.methodMap.put(methodToProcess.getReference(), proxyMethod);
|
repository.methodMap.put(methodToProcess.getReference(), proxyMethod);
|
||||||
if (errorReporter.hasErrors()) {
|
if (errorReporter.hasErrors()) {
|
||||||
repository.emitters.put(proxyMethod, new JSBodyBloatedEmitter(isStatic, proxyMethod,
|
repository.emitters.put(proxyMethod, new JSBodyBloatedEmitter(isStatic, proxyMethod,
|
||||||
script, parameterNames));
|
script, parameterNames));
|
||||||
} else {
|
} else {
|
||||||
AstNode expr = JSBodyInlineUtil.isSuitableForInlining(methodToProcess.getReference(),
|
var expr = JSBodyInlineUtil.isSuitableForInlining(methodToProcess.getReference(),
|
||||||
parameterNames, body);
|
parameterNames, body);
|
||||||
if (expr != null) {
|
if (expr != null) {
|
||||||
repository.inlineMethods.add(methodToProcess.getReference());
|
repository.inlineMethods.add(methodToProcess.getReference());
|
||||||
|
@ -584,7 +582,7 @@ class JSClassProcessor {
|
||||||
expr = body;
|
expr = body;
|
||||||
}
|
}
|
||||||
javaInvocationProcessor.process(location, expr);
|
javaInvocationProcessor.process(location, expr);
|
||||||
repository.emitters.put(proxyMethod, new JSBodyAstEmitter(isStatic, expr, parameterNames));
|
repository.emitters.put(proxyMethod, new JSBodyAstEmitter(isStatic, expr, rootNode, parameterNames));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,7 @@ public class IncrementalTest {
|
||||||
private String runScript(String script, String fileName) {
|
private String runScript(String script, String fileName) {
|
||||||
Scriptable scope = new NativeObject();
|
Scriptable scope = new NativeObject();
|
||||||
scope.setParentScope(rhinoRootScope);
|
scope.setParentScope(rhinoRootScope);
|
||||||
|
scope.setPrototype(rhinoRootScope);
|
||||||
rhinoContext.evaluateString(scope, script, fileName, 1, null);
|
rhinoContext.evaluateString(scope, script, fileName, 1, null);
|
||||||
Function main = (Function) scope.get("main", scope);
|
Function main = (Function) scope.get("main", scope);
|
||||||
ScriptRuntime.doTopCall(main, rhinoContext, scope, scope,
|
ScriptRuntime.doTopCall(main, rhinoContext, scope, scope,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user