mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
JS: rewrite more native generators with templates, fix issues in template engine
This commit is contained in:
parent
7c4aa522d3
commit
a1cc817504
|
@ -26,25 +26,24 @@ public class LongNativeGenerator implements Generator {
|
|||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||
switch (methodRef.getName()) {
|
||||
case "compare":
|
||||
writer.append("return Long_compare(").append(context.getParameterName(1)).append(", ")
|
||||
.append(context.getParameterName(2)).append(");").softNewLine();
|
||||
context.useLongLibrary();
|
||||
generateRuntimeCall(context, writer, "Long_compare");
|
||||
break;
|
||||
case "compareUnsigned":
|
||||
writer.append("return Long_ucompare(").append(context.getParameterName(1)).append(", ")
|
||||
.append(context.getParameterName(2)).append(");").softNewLine();
|
||||
context.useLongLibrary();
|
||||
generateRuntimeCall(context, writer, "Long_ucompare");
|
||||
break;
|
||||
case "divideUnsigned":
|
||||
writer.append("return Long_udiv(").append(context.getParameterName(1)).append(", ")
|
||||
.append(context.getParameterName(2)).append(");").softNewLine();
|
||||
context.useLongLibrary();
|
||||
generateRuntimeCall(context, writer, "Long_udiv");
|
||||
break;
|
||||
case "remainderUnsigned":
|
||||
writer.append("return Long_urem(").append(context.getParameterName(1)).append(", ")
|
||||
.append(context.getParameterName(2)).append(");").softNewLine();
|
||||
context.useLongLibrary();
|
||||
generateRuntimeCall(context, writer, "Long_urem");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void generateRuntimeCall(GeneratorContext context, SourceWriter writer, String name) throws IOException {
|
||||
writer.append("return ").appendFunction(name).append("(").append(context.getParameterName(1))
|
||||
.append(",").ws()
|
||||
.append(context.getParameterName(2)).append(");").softNewLine();
|
||||
context.useLongLibrary();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ public final class AstUtil {
|
|||
var env = new CompilerEnvirons();
|
||||
env.setRecoverFromErrors(true);
|
||||
env.setLanguageVersion(Context.VERSION_1_8);
|
||||
env.setIdeMode(true);
|
||||
var factory = new JSParser(env);
|
||||
|
||||
return factory.parse(string, null, 0);
|
||||
|
|
|
@ -15,7 +15,11 @@
|
|||
*/
|
||||
package org.teavm.backend.javascript.ast;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -70,6 +74,7 @@ import org.mozilla.javascript.ast.WhileLoop;
|
|||
public class AstVisitor {
|
||||
protected AstNode replacement;
|
||||
protected boolean hasReplacement;
|
||||
protected final Map<String, Scope> currentScopes = new HashMap<>();
|
||||
|
||||
public final void visit(AstNode node) {
|
||||
switch (node.getType()) {
|
||||
|
@ -261,7 +266,9 @@ public class AstVisitor {
|
|||
}
|
||||
|
||||
public void visit(Scope node) {
|
||||
var scope = enterScope(node);
|
||||
visitChildren(node);
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(LabeledStatement node) {
|
||||
|
@ -284,26 +291,34 @@ public class AstVisitor {
|
|||
}
|
||||
|
||||
public void visit(DoLoop node) {
|
||||
var scope = enterScope(node);
|
||||
visitProperty(node, DoLoop::getBody, DoLoop::setBody, EMPTY_DEFAULT);
|
||||
visitProperty(node, DoLoop::getCondition, DoLoop::setCondition, NULL_DEFAULT);
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(ForInLoop node) {
|
||||
var scope = enterScope(node);
|
||||
visitProperty(node, ForInLoop::getIterator, ForInLoop::setIterator, NULL_DEFAULT);
|
||||
visitProperty(node, ForInLoop::getIteratedObject, ForInLoop::setIteratedObject, NULL_DEFAULT);
|
||||
visitProperty(node, ForInLoop::getBody, ForInLoop::setBody, EMPTY_DEFAULT);
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(ForLoop node) {
|
||||
var scope = enterScope(node);
|
||||
visitProperty(node, ForLoop::getInitializer, ForLoop::setInitializer, EMPTY_EXPR_DEFAULT);
|
||||
visitProperty(node, ForLoop::getCondition, ForLoop::setCondition, EMPTY_EXPR_DEFAULT);
|
||||
visitProperty(node, ForLoop::getIncrement, ForLoop::setIncrement, EMPTY_EXPR_DEFAULT);
|
||||
visitProperty(node, ForLoop::getBody, ForLoop::setBody, EMPTY_DEFAULT);
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(WhileLoop node) {
|
||||
var scope = enterScope(node);
|
||||
visitProperty(node, WhileLoop::getCondition, WhileLoop::setCondition, NULL_DEFAULT);
|
||||
visitProperty(node, WhileLoop::getBody, WhileLoop::setBody, EMPTY_DEFAULT);
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(IfStatement node) {
|
||||
|
@ -375,15 +390,18 @@ public class AstVisitor {
|
|||
}
|
||||
|
||||
public void visit(ArrayComprehension node) {
|
||||
var scope = enterScope(node);
|
||||
for (var loop : node.getLoops()) {
|
||||
visitProperty(loop, ArrayComprehensionLoop::getIterator, ArrayComprehensionLoop::setIterator);
|
||||
visitProperty(loop, ArrayComprehensionLoop::getIteratedObject, ArrayComprehensionLoop::setIteratedObject);
|
||||
}
|
||||
visitProperty(node, ArrayComprehension::getFilter, ArrayComprehension::setFilter);
|
||||
visitProperty(node, ArrayComprehension::getResult, ArrayComprehension::setResult);
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(GeneratorExpression node) {
|
||||
var scope = enterScope(node);
|
||||
for (var loop : node.getLoops()) {
|
||||
visitProperty(loop, GeneratorExpressionLoop::getIterator, GeneratorExpressionLoop::setIterator);
|
||||
visitProperty(loop, GeneratorExpressionLoop::getIteratedObject,
|
||||
|
@ -391,6 +409,7 @@ public class AstVisitor {
|
|||
}
|
||||
visitProperty(node, GeneratorExpression::getFilter, GeneratorExpression::setFilter);
|
||||
visitProperty(node, GeneratorExpression::getResult, GeneratorExpression::setResult);
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(NumberLiteral node) {
|
||||
|
@ -426,14 +445,21 @@ public class AstVisitor {
|
|||
}
|
||||
|
||||
public void visit(FunctionNode node) {
|
||||
var scope = enterScope(node);
|
||||
if (node.getFunctionType() != FunctionNode.ARROW_FUNCTION) {
|
||||
currentScopes.put("arguments", node);
|
||||
}
|
||||
visitProperty(node, FunctionNode::getFunctionName, FunctionNode::setFunctionName);
|
||||
visitMany(node.getParams());
|
||||
visitChildren(node.getBody());
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(LetNode node) {
|
||||
var scope = enterScope(node);
|
||||
visitProperty(node, LetNode::getVariables, LetNode::setVariables);
|
||||
visitProperty(node, LetNode::getBody, LetNode::setBody);
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
public void visit(ParenthesizedExpression node) {
|
||||
|
@ -464,6 +490,33 @@ public class AstVisitor {
|
|||
replacement = node;
|
||||
}
|
||||
|
||||
|
||||
private Map<String, Scope> enterScope(Scope scope) {
|
||||
if (scope.getSymbolTable() == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
var map = new LinkedHashMap<String, Scope>();
|
||||
for (var name : scope.getSymbolTable().keySet()) {
|
||||
map.put(name, currentScopes.get(name));
|
||||
currentScopes.put(name, scope);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void leaveScope(Map<String, Scope> backup) {
|
||||
for (var entry : backup.entrySet()) {
|
||||
if (entry.getValue() == null) {
|
||||
currentScopes.remove(entry.getKey());
|
||||
} else {
|
||||
currentScopes.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Scope scopeOfId(String id) {
|
||||
return currentScopes.get(id);
|
||||
}
|
||||
|
||||
private static final Supplier<AstNode> NULL_DEFAULT = () -> new KeywordLiteral(0, 0, Token.NULL);
|
||||
private static final Supplier<AstNode> EMPTY_DEFAULT = () -> new EmptyStatement(0, 0);
|
||||
private static final Supplier<AstNode> EMPTY_EXPR_DEFAULT = () -> new EmptyExpression(0, 0);
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
package org.teavm.backend.javascript.rendering;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -93,9 +95,10 @@ public class AstWriter {
|
|||
public static final int PRECEDENCE_COMMA = 18;
|
||||
protected final SourceWriter writer;
|
||||
private Map<String, NameEmitter> nameMap = new HashMap<>();
|
||||
private boolean rootScope = true;
|
||||
protected boolean rootScope = true;
|
||||
private Set<String> aliases = new HashSet<>();
|
||||
private Function<String, NameEmitter> globalNameWriter;
|
||||
public final Map<String, Scope> currentScopes = new HashMap<>();
|
||||
|
||||
public AstWriter(SourceWriter writer, Function<String, NameEmitter> globalNameWriter) {
|
||||
this.writer = writer;
|
||||
|
@ -319,12 +322,14 @@ public class AstWriter {
|
|||
}
|
||||
|
||||
private void print(Scope node) throws IOException {
|
||||
var scope = enterScope(node);
|
||||
writer.append('{').softNewLine().indent();
|
||||
for (Node child = node.getFirstChild(); child != null; child = child.getNext()) {
|
||||
print((AstNode) child);
|
||||
writer.softNewLine();
|
||||
}
|
||||
writer.outdent().append('}');
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(LabeledStatement node) throws IOException {
|
||||
|
@ -366,14 +371,17 @@ public class AstWriter {
|
|||
}
|
||||
|
||||
private void print(DoLoop node) throws IOException {
|
||||
var scope = enterScope(node);
|
||||
writer.append("do ").ws();
|
||||
print(node.getBody());
|
||||
writer.append("while").ws().append('(');
|
||||
print(node.getCondition());
|
||||
writer.append(");");
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(ForInLoop node) throws IOException {
|
||||
var scope = enterScope(node);
|
||||
writer.append("for");
|
||||
if (node.isForEach()) {
|
||||
writer.append(" each");
|
||||
|
@ -384,9 +392,11 @@ public class AstWriter {
|
|||
print(node.getIteratedObject());
|
||||
writer.append(')').ws();
|
||||
print(node.getBody());
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(ForLoop node) throws IOException {
|
||||
var scope = enterScope(node);
|
||||
writer.append("for").ws().append('(');
|
||||
print(node.getInitializer());
|
||||
writer.append(';');
|
||||
|
@ -395,13 +405,16 @@ public class AstWriter {
|
|||
print(node.getIncrement());
|
||||
writer.append(')').ws();
|
||||
print(node.getBody());
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(WhileLoop node) throws IOException {
|
||||
var scope = enterScope(node);
|
||||
writer.append("while").ws().append('(');
|
||||
print(node.getCondition());
|
||||
writer.append(')').ws();
|
||||
print(node.getBody());
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(IfStatement node) throws IOException {
|
||||
|
@ -606,6 +619,7 @@ public class AstWriter {
|
|||
}
|
||||
|
||||
private void print(ArrayComprehension node) throws IOException {
|
||||
var scope = enterScope(node);
|
||||
writer.append("[");
|
||||
for (ArrayComprehensionLoop loop : node.getLoops()) {
|
||||
writer.append("for").ws().append("(");
|
||||
|
@ -621,9 +635,11 @@ public class AstWriter {
|
|||
}
|
||||
print(node.getResult());
|
||||
writer.append(']');
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(GeneratorExpression node) throws IOException {
|
||||
var scope = enterScope(node);
|
||||
writer.append("(");
|
||||
for (GeneratorExpressionLoop loop : node.getLoops()) {
|
||||
writer.append("for").ws().append("(");
|
||||
|
@ -639,6 +655,7 @@ public class AstWriter {
|
|||
}
|
||||
print(node.getResult());
|
||||
writer.append(')');
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(NumberLiteral node) throws IOException {
|
||||
|
@ -652,7 +669,8 @@ public class AstWriter {
|
|||
}
|
||||
|
||||
public void print(Name node, int precedence) throws IOException {
|
||||
if (rootScope && node.getDefiningScope() == null) {
|
||||
var definingScope = scopeOfId(node.getIdentifier());
|
||||
if (rootScope && definingScope == null) {
|
||||
var alias = nameMap.get(node.getIdentifier());
|
||||
if (alias == null) {
|
||||
if (globalNameWriter != null) {
|
||||
|
@ -667,10 +685,6 @@ public class AstWriter {
|
|||
}
|
||||
}
|
||||
|
||||
protected final boolean isRootScope() {
|
||||
return rootScope;
|
||||
}
|
||||
|
||||
private void print(RegExpLiteral node) throws IOException {
|
||||
writer.append('/').append(node.getValue()).append('/').append(node.getFlags());
|
||||
}
|
||||
|
@ -710,16 +724,28 @@ public class AstWriter {
|
|||
}
|
||||
|
||||
private void print(FunctionNode node) throws IOException {
|
||||
if (!node.isMethod()) {
|
||||
var scope = enterScope(node);
|
||||
var isArrow = node.getFunctionType() == FunctionNode.ARROW_FUNCTION;
|
||||
if (!isArrow) {
|
||||
currentScopes.put("arguments", node);
|
||||
}
|
||||
if (!node.isMethod() && !isArrow) {
|
||||
writer.append("function");
|
||||
}
|
||||
if (node.getFunctionName() != null) {
|
||||
writer.append(' ');
|
||||
print(node.getFunctionName());
|
||||
}
|
||||
writer.append('(');
|
||||
printList(node.getParams());
|
||||
writer.append(')').ws();
|
||||
if (!isArrow || node.getParams().size() != 1) {
|
||||
writer.append('(');
|
||||
printList(node.getParams());
|
||||
writer.append(')').ws();
|
||||
} else {
|
||||
print(node.getParams().get(0));
|
||||
}
|
||||
if (isArrow) {
|
||||
writer.append("=>").ws();
|
||||
}
|
||||
|
||||
if (node.isExpressionClosure()) {
|
||||
if (node.getBody().getLastChild() instanceof ReturnStatement) {
|
||||
|
@ -731,13 +757,17 @@ public class AstWriter {
|
|||
} else {
|
||||
print(node.getBody());
|
||||
}
|
||||
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(LetNode node) throws IOException {
|
||||
var scope = enterScope(node);
|
||||
writer.append("let").ws().append('(');
|
||||
printList(node.getVariables().getVariables());
|
||||
writer.append(')');
|
||||
print(node.getBody());
|
||||
leaveScope(scope);
|
||||
}
|
||||
|
||||
private void print(ParenthesizedExpression node, int precedence) throws IOException {
|
||||
|
@ -934,4 +964,30 @@ public class AstWriter {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Scope> enterScope(Scope scope) {
|
||||
if (scope.getSymbolTable() == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
var map = new LinkedHashMap<String, Scope>();
|
||||
for (var name : scope.getSymbolTable().keySet()) {
|
||||
map.put(name, currentScopes.get(name));
|
||||
currentScopes.put(name, scope);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void leaveScope(Map<String, Scope> backup) {
|
||||
for (var entry : backup.entrySet()) {
|
||||
if (entry.getValue() == null) {
|
||||
currentScopes.remove(entry.getKey());
|
||||
} else {
|
||||
currentScopes.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Scope scopeOfId(String id) {
|
||||
return currentScopes.get(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -264,7 +264,7 @@ public class Renderer implements RenderingManager {
|
|||
"$rt_createFloatArray", "$rt_createDoubleArray", "$rt_compare",
|
||||
"$rt_castToClass", "$rt_castToInterface", "$rt_equalDoubles",
|
||||
"$rt_str", "Long_toNumber", "Long_fromInt", "Long_fromNumber", "Long_create", "Long_ZERO",
|
||||
"$rt_intern", "$rt_substring",
|
||||
"$rt_intern", "$rt_substring", "$rt_ustr",
|
||||
"Long_hi", "Long_lo");
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package org.teavm.backend.javascript.templating;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
import org.mozilla.javascript.ast.FunctionNode;
|
||||
|
@ -59,21 +58,22 @@ public class JavaScriptTemplate {
|
|||
|
||||
public SourceFragment build() {
|
||||
var intParameters = parameters;
|
||||
var paramNameToIndex = new HashMap<String, Integer>();
|
||||
var nameParameters = new HashMap<String, SourceFragment>();
|
||||
for (var i = 0; i < node.getParams().size(); ++i) {
|
||||
var param = node.getParams().get(i);
|
||||
if (param instanceof Name) {
|
||||
paramNameToIndex.put(((Name) param).getIdentifier(), i);
|
||||
nameParameters.put(((Name) param).getIdentifier(), intParameters.apply(i));
|
||||
}
|
||||
}
|
||||
Function<String, SourceFragment> nameParameters = name -> {
|
||||
var index = paramNameToIndex.get(name);
|
||||
return index != null ? intParameters.apply(index) : null;
|
||||
};
|
||||
var thisFragment = parameters.apply(0);
|
||||
var body = node.getBody();
|
||||
return (writer, precedence) -> {
|
||||
var astWriter = new TemplatingAstWriter(writer, nameParameters, node);
|
||||
if (node.getSymbolTable() != null) {
|
||||
for (var name : node.getSymbolTable().keySet()) {
|
||||
astWriter.currentScopes.put(name, node);
|
||||
}
|
||||
}
|
||||
if (thisFragment != null) {
|
||||
astWriter.declareNameEmitter("this", thisPrecedence -> thisFragment.write(writer, thisPrecedence));
|
||||
}
|
||||
|
|
|
@ -112,7 +112,8 @@ public class TemplatingAstTransformer extends AstVisitor {
|
|||
super.visit(node);
|
||||
if (node.getTarget() instanceof Name) {
|
||||
var name = (Name) node.getTarget();
|
||||
if (name.getDefiningScope() == null) {
|
||||
var scope = scopeOfId(name.getIdentifier());
|
||||
if (scope == null) {
|
||||
tryIntrinsicName(node, name.getIdentifier());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
package org.teavm.backend.javascript.templating;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
import java.util.Map;
|
||||
import org.mozilla.javascript.ast.ElementGet;
|
||||
import org.mozilla.javascript.ast.FunctionCall;
|
||||
import org.mozilla.javascript.ast.FunctionNode;
|
||||
import org.mozilla.javascript.ast.Name;
|
||||
import org.mozilla.javascript.ast.PropertyGet;
|
||||
import org.mozilla.javascript.ast.Scope;
|
||||
|
@ -31,20 +32,28 @@ import org.teavm.model.MethodDescriptor;
|
|||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class TemplatingAstWriter extends AstWriter {
|
||||
private Function<String, SourceFragment> names;
|
||||
private Map<String, SourceFragment> names;
|
||||
private Scope scope;
|
||||
|
||||
public TemplatingAstWriter(SourceWriter writer, Function<String, SourceFragment> names, Scope scope) {
|
||||
public TemplatingAstWriter(SourceWriter writer, Map<String, SourceFragment> names, Scope scope) {
|
||||
super(writer, new DefaultGlobalNameWriter(writer));
|
||||
this.names = names;
|
||||
this.scope = scope;
|
||||
if (names != null) {
|
||||
for (var name : names.keySet()) {
|
||||
currentScopes.put(name, scope);
|
||||
}
|
||||
}
|
||||
if (scope instanceof FunctionNode) {
|
||||
currentScopes.put("arguments", scope);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean intrinsic(FunctionCall node, int precedence) throws IOException {
|
||||
if (node.getTarget() instanceof Name) {
|
||||
var name = (Name) node.getTarget();
|
||||
if (name.getDefiningScope() == null) {
|
||||
if (scopeOfId(name.getIdentifier()) == null) {
|
||||
return tryIntrinsicName(node, name.getIdentifier());
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +135,7 @@ public class TemplatingAstWriter extends AstWriter {
|
|||
var call = (FunctionCall) node.getElement();
|
||||
if (call.getTarget() instanceof Name) {
|
||||
var name = (Name) call.getTarget();
|
||||
if (name.getDefiningScope() == null) {
|
||||
if (scopeOfId(name.getIdentifier()) == null) {
|
||||
switch (name.getIdentifier()) {
|
||||
case "teavm_javaVirtualMethod":
|
||||
if (writeJavaVirtualMethod(node, call)) {
|
||||
|
@ -149,9 +158,13 @@ public class TemplatingAstWriter extends AstWriter {
|
|||
public void print(PropertyGet node) throws IOException {
|
||||
if (node.getTarget() instanceof Name) {
|
||||
var name = (Name) node.getTarget();
|
||||
if (name.getDefiningScope() == null && name.getIdentifier().equals("teavm_globals")) {
|
||||
var scope = scopeOfId(name.getIdentifier());
|
||||
if (scope == null && name.getIdentifier().equals("teavm_globals")) {
|
||||
var oldRootScope = rootScope;
|
||||
rootScope = false;
|
||||
writer.append("$rt_globals").append(".");
|
||||
print(node.getProperty());
|
||||
rootScope = oldRootScope;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -187,15 +200,16 @@ public class TemplatingAstWriter extends AstWriter {
|
|||
|
||||
@Override
|
||||
public void print(Name node, int precedence) throws IOException {
|
||||
if (isRootScope()) {
|
||||
if (names != null && node.getDefiningScope() == scope) {
|
||||
var fragment = names.apply(node.getIdentifier());
|
||||
var definingScope = scopeOfId(node.getIdentifier());
|
||||
if (rootScope) {
|
||||
if (names != null && definingScope == scope) {
|
||||
var fragment = names.get(node.getIdentifier());
|
||||
if (fragment != null) {
|
||||
fragment.write(writer, precedence);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (node.getDefiningScope() == null && scope != null) {
|
||||
if (definingScope == null && scope != null) {
|
||||
writer.appendFunction(node.getIdentifier());
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -32,107 +32,107 @@ final class JS {
|
|||
private JS() {
|
||||
}
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject arrayData(Object array);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native byte[] dataToByteArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native char[] dataToCharArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native short[] dataToShortArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native int[] dataToIntArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native float[] dataToFloatArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native double[] dataToDoubleArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject[] dataToArray(JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject wrap(byte value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject wrap(short value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject wrap(int value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject wrap(char value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject wrap(float value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject wrap(double value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject wrap(boolean value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native JSObject wrap(String value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native byte unwrapByte(JSObject value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native char unwrapCharacter(JSObject value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native short unwrapShort(JSObject value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native int unwrapInt(JSObject value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native float unwrapFloat(JSObject value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native double unwrapDouble(JSObject value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native boolean unwrapBoolean(JSObject value);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
@NoSideEffects
|
||||
public static native String unwrapString(JSObject value);
|
||||
|
||||
|
@ -448,97 +448,97 @@ final class JS {
|
|||
return JS::unwrapStringArray;
|
||||
}
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e, JSObject f);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e, JSObject f, JSObject g);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k,
|
||||
JSObject l);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject invoke(JSObject instance, JSObject method, JSObject a, JSObject b, JSObject c,
|
||||
JSObject d, JSObject e, JSObject f, JSObject g, JSObject h, JSObject i, JSObject j, JSObject k,
|
||||
JSObject l, JSObject m);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@JSBody(params = { "instance", "index" }, script = "return instance[index];")
|
||||
public static native JSObject get(JSObject instance, JSObject index);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@JSBody(params = { "instance", "index" }, script = "return instance[index];")
|
||||
@NoSideEffects
|
||||
public static native JSObject getPure(JSObject instance, JSObject index);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@JSBody(params = { "instance", "index", "obj" }, script = "instance[index] = obj;")
|
||||
public static native void set(JSObject instance, JSObject index, JSObject obj);
|
||||
|
||||
@InjectedBy(JSNativeGenerator.class)
|
||||
@InjectedBy(JSNativeInjector.class)
|
||||
@JSBody(params = { "instance", "index", "obj" }, script = "instance[index] = obj;")
|
||||
@NoSideEffects
|
||||
public static native void setPure(JSObject instance, JSObject index, JSObject obj);
|
||||
|
||||
@GeneratedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject function(JSObject instance, JSObject property);
|
||||
|
||||
@GeneratedBy(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeGenerator.class)
|
||||
@PluggableDependency(JSNativeInjector.class)
|
||||
public static native JSObject functionAsObject(JSObject instance, JSObject property);
|
||||
}
|
||||
|
|
|
@ -15,321 +15,31 @@
|
|||
*/
|
||||
package org.teavm.jso.impl;
|
||||
|
||||
import static org.teavm.backend.javascript.rendering.RenderingUtil.escapeString;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.rendering.Precedence;
|
||||
import org.teavm.backend.javascript.spi.Generator;
|
||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||
import org.teavm.backend.javascript.spi.Injector;
|
||||
import org.teavm.backend.javascript.spi.InjectorContext;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.backend.javascript.templating.JavaScriptTemplate;
|
||||
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class JSNativeGenerator implements Injector, DependencyPlugin, Generator {
|
||||
private Set<MethodReference> reachedFunctorMethods = new HashSet<>();
|
||||
private Set<DependencyNode> functorParamNodes = new HashSet<>();
|
||||
public class JSNativeGenerator implements Generator {
|
||||
private JavaScriptTemplate template;
|
||||
|
||||
public JSNativeGenerator(JavaScriptTemplateFactory templateFactory) throws IOException {
|
||||
template = templateFactory.createFromResource("org/teavm/jso/impl/JS.js");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
|
||||
throws IOException {
|
||||
switch (methodRef.getName()) {
|
||||
case "function":
|
||||
writeFunction(context, writer);
|
||||
template.builder("jsFunction").withContext(context).build().write(writer, 0);
|
||||
break;
|
||||
case "functionAsObject":
|
||||
writeFunctionAsObject(context, writer);
|
||||
template.builder("jsFunctionAsObject").withContext(context).build().write(writer, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFunction(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||
String thisName = context.getParameterName(1);
|
||||
String methodName = context.getParameterName(2);
|
||||
writer.append("var name").ws().append('=').ws().append("'jso$functor$'").ws().append('+').ws()
|
||||
.append(methodName).append(';').softNewLine();
|
||||
writer.append("if").ws().append("(!").append(thisName).append("[name])").ws().append('{')
|
||||
.indent().softNewLine();
|
||||
|
||||
writer.append("var fn").ws().append('=').ws().append("function()").ws().append('{')
|
||||
.indent().softNewLine();
|
||||
writer.append("return ").append(thisName).append('[').append(methodName).append(']')
|
||||
.append(".apply(").append(thisName).append(',').ws().append("arguments);").softNewLine();
|
||||
writer.outdent().append("};").softNewLine();
|
||||
writer.append(thisName).append("[name]").ws().append('=').ws().append("function()").ws().append('{')
|
||||
.indent().softNewLine();
|
||||
writer.append("return fn;").softNewLine();
|
||||
writer.outdent().append("};").softNewLine();
|
||||
|
||||
writer.outdent().append('}').softNewLine();
|
||||
writer.append("return ").append(thisName).append("[name]();").softNewLine();
|
||||
}
|
||||
|
||||
private void writeFunctionAsObject(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||
String thisName = context.getParameterName(1);
|
||||
String methodName = context.getParameterName(2);
|
||||
|
||||
writer.append("if").ws().append("(typeof ").append(thisName).ws().append("!==").ws().append("\"function\")")
|
||||
.ws().append("return ").append(thisName).append(";").softNewLine();
|
||||
writer.append("var result").ws().append("=").ws().append("{};").softNewLine();
|
||||
writer.append("result[").append(methodName).append("]").ws().append("=").ws().append(thisName)
|
||||
.append(";").softNewLine();
|
||||
writer.append("return result;").softNewLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||
SourceWriter writer = context.getWriter();
|
||||
switch (methodRef.getName()) {
|
||||
case "arrayData":
|
||||
context.writeExpr(context.getArgument(0));
|
||||
writer.append(".data");
|
||||
break;
|
||||
case "get":
|
||||
case "getPure":
|
||||
context.writeExpr(context.getArgument(0), Precedence.MEMBER_ACCESS);
|
||||
renderProperty(context.getArgument(1), context);
|
||||
break;
|
||||
case "set":
|
||||
case "setPure":
|
||||
context.writeExpr(context.getArgument(0), Precedence.ASSIGNMENT.next());
|
||||
renderProperty(context.getArgument(1), context);
|
||||
writer.ws().append('=').ws();
|
||||
context.writeExpr(context.getArgument(2), Precedence.ASSIGNMENT.next());
|
||||
break;
|
||||
case "invoke":
|
||||
context.writeExpr(context.getArgument(0), Precedence.GROUPING);
|
||||
renderProperty(context.getArgument(1), context);
|
||||
writer.append('(');
|
||||
for (int i = 2; i < context.argumentCount(); ++i) {
|
||||
if (i > 2) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
context.writeExpr(context.getArgument(i), Precedence.min());
|
||||
}
|
||||
writer.append(')');
|
||||
break;
|
||||
case "instantiate":
|
||||
if (context.getPrecedence().ordinal() >= Precedence.FUNCTION_CALL.ordinal()) {
|
||||
writer.append("(");
|
||||
}
|
||||
writer.append("new ");
|
||||
context.writeExpr(context.getArgument(0), Precedence.GROUPING);
|
||||
renderProperty(context.getArgument(1), context);
|
||||
writer.append("(");
|
||||
for (int i = 2; i < context.argumentCount(); ++i) {
|
||||
if (i > 2) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
context.writeExpr(context.getArgument(i), Precedence.min());
|
||||
}
|
||||
writer.append(")");
|
||||
if (context.getPrecedence().ordinal() >= Precedence.FUNCTION_CALL.ordinal()) {
|
||||
writer.append(")");
|
||||
}
|
||||
break;
|
||||
case "wrap":
|
||||
if (methodRef.getDescriptor().parameterType(0).isObject("java.lang.String")) {
|
||||
if (context.getArgument(0) instanceof ConstantExpr) {
|
||||
ConstantExpr constant = (ConstantExpr) context.getArgument(0);
|
||||
if (constant.getValue() instanceof String) {
|
||||
writer.append('"').append(escapeString((String) constant.getValue())).append('"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
writer.append("$rt_ustr(");
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
} else if (methodRef.getDescriptor().parameterType(0) == ValueType.BOOLEAN) {
|
||||
if (context.getPrecedence().ordinal() >= Precedence.UNARY.ordinal()) {
|
||||
writer.append("(");
|
||||
}
|
||||
writer.append("!!");
|
||||
context.writeExpr(context.getArgument(0), Precedence.UNARY);
|
||||
if (context.getPrecedence().ordinal() >= Precedence.UNARY.ordinal()) {
|
||||
writer.append(")");
|
||||
}
|
||||
} else {
|
||||
context.writeExpr(context.getArgument(0), context.getPrecedence());
|
||||
}
|
||||
break;
|
||||
case "unwrapString":
|
||||
writer.appendFunction("$rt_str").append("(");
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "unwrapBoolean":
|
||||
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
|
||||
writer.append("(");
|
||||
}
|
||||
context.writeExpr(context.getArgument(0), Precedence.CONDITIONAL.next());
|
||||
writer.ws().append("?").ws().append("1").ws().append(":").ws().append("0");
|
||||
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
|
||||
writer.append(")");
|
||||
}
|
||||
break;
|
||||
|
||||
case "dataToByteArray":
|
||||
writer.append("$rt_wrapArray($rt_bytecls,").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToShortArray":
|
||||
writer.append("$rt_wrapArray($rt_shortcls,").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToCharArray":
|
||||
writer.append("$rt_wrapArray($rt_charcls,").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToIntArray":
|
||||
writer.append("$rt_wrapArray($rt_intcls,").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToFloatArray":
|
||||
writer.append("$rt_wrapArray($rt_floatcls,").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToDoubleArray":
|
||||
writer.append("$rt_wrapArray($rt_doublecls,").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "dataToArray":
|
||||
writer.append("$rt_wrapArray($rt_objcls,").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (methodRef.getName().startsWith("unwrap")) {
|
||||
context.writeExpr(context.getArgument(0), context.getPrecedence());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "invoke":
|
||||
case "instantiate":
|
||||
case "function":
|
||||
if (reachedFunctorMethods.add(method.getReference()) && !method.isMissing()) {
|
||||
for (int i = 0; i < method.getReference().parameterCount(); ++i) {
|
||||
DependencyNode node = method.getVariable(i);
|
||||
if (functorParamNodes.add(node)) {
|
||||
node.addConsumer(type -> {
|
||||
if (agent.getClassHierarchy().isSuperType(method.getMethod().getOwnerName(),
|
||||
type.getName(), false)) {
|
||||
reachFunctorMethods(agent, type.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "unwrapString":
|
||||
method.getResult().propagate(agent.getType("java.lang.String"));
|
||||
break;
|
||||
|
||||
case "dataToByteArray":
|
||||
method.getResult().propagate(agent.getType("[B"));
|
||||
break;
|
||||
case "dataToShortArray":
|
||||
method.getResult().propagate(agent.getType("[S"));
|
||||
break;
|
||||
case "dataToCharArray":
|
||||
method.getResult().propagate(agent.getType("[C"));
|
||||
break;
|
||||
case "dataToIntArray":
|
||||
method.getResult().propagate(agent.getType("[I"));
|
||||
break;
|
||||
case "dataToFloatArray":
|
||||
method.getResult().propagate(agent.getType("[F"));
|
||||
break;
|
||||
case "dataToDoubleArray":
|
||||
method.getResult().propagate(agent.getType("[D"));
|
||||
break;
|
||||
case "dataToArray":
|
||||
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void reachFunctorMethods(DependencyAgent agent, String type) {
|
||||
ClassReader cls = agent.getClassSource().get(type);
|
||||
if (cls != null) {
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||
agent.linkMethod(method.getReference()).use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void renderProperty(Expr property, InjectorContext context) throws IOException {
|
||||
SourceWriter writer = context.getWriter();
|
||||
String name = extractPropertyName(property);
|
||||
if (name == null) {
|
||||
writer.append('[');
|
||||
context.writeExpr(property, Precedence.min());
|
||||
writer.append(']');
|
||||
} else if (!isIdentifier(name)) {
|
||||
writer.append("[\"");
|
||||
context.writeEscaped(name);
|
||||
writer.append("\"]");
|
||||
} else {
|
||||
writer.append(".").append(name);
|
||||
}
|
||||
}
|
||||
|
||||
private String extractPropertyName(Expr propertyName) {
|
||||
if (!(propertyName instanceof InvocationExpr)) {
|
||||
return null;
|
||||
}
|
||||
InvocationExpr invoke = (InvocationExpr) propertyName;
|
||||
if (!invoke.getMethod().getClassName().equals(JS.class.getName())) {
|
||||
return null;
|
||||
}
|
||||
if (!invoke.getMethod().getName().equals("wrap")
|
||||
|| !invoke.getMethod().getDescriptor().parameterType(0).isObject("java.lang.String")) {
|
||||
return null;
|
||||
}
|
||||
Expr arg = invoke.getArguments().get(0);
|
||||
if (!(arg instanceof ConstantExpr)) {
|
||||
return null;
|
||||
}
|
||||
ConstantExpr constant = (ConstantExpr) arg;
|
||||
return constant.getValue() instanceof String ? (String) constant.getValue() : null;
|
||||
}
|
||||
|
||||
private boolean isIdentifier(String name) {
|
||||
if (name.isEmpty() || !Character.isJavaIdentifierStart(name.charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < name.length(); ++i) {
|
||||
if (!Character.isJavaIdentifierPart(name.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
279
jso/impl/src/main/java/org/teavm/jso/impl/JSNativeInjector.java
Normal file
279
jso/impl/src/main/java/org/teavm/jso/impl/JSNativeInjector.java
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Copyright 2023 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.jso.impl;
|
||||
|
||||
import static org.teavm.backend.javascript.rendering.RenderingUtil.escapeString;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.rendering.Precedence;
|
||||
import org.teavm.backend.javascript.spi.Injector;
|
||||
import org.teavm.backend.javascript.spi.InjectorContext;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class JSNativeInjector implements Injector, DependencyPlugin {
|
||||
private Set<MethodReference> reachedFunctorMethods = new HashSet<>();
|
||||
private Set<DependencyNode> functorParamNodes = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||
SourceWriter writer = context.getWriter();
|
||||
switch (methodRef.getName()) {
|
||||
case "arrayData":
|
||||
context.writeExpr(context.getArgument(0));
|
||||
writer.append(".data");
|
||||
break;
|
||||
case "get":
|
||||
case "getPure":
|
||||
context.writeExpr(context.getArgument(0), Precedence.MEMBER_ACCESS);
|
||||
renderProperty(context.getArgument(1), context);
|
||||
break;
|
||||
case "set":
|
||||
case "setPure":
|
||||
context.writeExpr(context.getArgument(0), Precedence.ASSIGNMENT.next());
|
||||
renderProperty(context.getArgument(1), context);
|
||||
writer.ws().append('=').ws();
|
||||
context.writeExpr(context.getArgument(2), Precedence.ASSIGNMENT.next());
|
||||
break;
|
||||
case "invoke":
|
||||
context.writeExpr(context.getArgument(0), Precedence.GROUPING);
|
||||
renderProperty(context.getArgument(1), context);
|
||||
writer.append('(');
|
||||
for (int i = 2; i < context.argumentCount(); ++i) {
|
||||
if (i > 2) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
context.writeExpr(context.getArgument(i), Precedence.min());
|
||||
}
|
||||
writer.append(')');
|
||||
break;
|
||||
case "instantiate":
|
||||
if (context.getPrecedence().ordinal() >= Precedence.FUNCTION_CALL.ordinal()) {
|
||||
writer.append("(");
|
||||
}
|
||||
writer.append("new ");
|
||||
context.writeExpr(context.getArgument(0), Precedence.GROUPING);
|
||||
renderProperty(context.getArgument(1), context);
|
||||
writer.append("(");
|
||||
for (int i = 2; i < context.argumentCount(); ++i) {
|
||||
if (i > 2) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
context.writeExpr(context.getArgument(i), Precedence.min());
|
||||
}
|
||||
writer.append(")");
|
||||
if (context.getPrecedence().ordinal() >= Precedence.FUNCTION_CALL.ordinal()) {
|
||||
writer.append(")");
|
||||
}
|
||||
break;
|
||||
case "wrap":
|
||||
if (methodRef.getDescriptor().parameterType(0).isObject("java.lang.String")) {
|
||||
if (context.getArgument(0) instanceof ConstantExpr) {
|
||||
ConstantExpr constant = (ConstantExpr) context.getArgument(0);
|
||||
if (constant.getValue() instanceof String) {
|
||||
writer.append('"').append(escapeString((String) constant.getValue())).append('"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
writer.appendFunction("$rt_ustr").append("(");
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
} else if (methodRef.getDescriptor().parameterType(0) == ValueType.BOOLEAN) {
|
||||
if (context.getPrecedence().ordinal() >= Precedence.UNARY.ordinal()) {
|
||||
writer.append("(");
|
||||
}
|
||||
writer.append("!!");
|
||||
context.writeExpr(context.getArgument(0), Precedence.UNARY);
|
||||
if (context.getPrecedence().ordinal() >= Precedence.UNARY.ordinal()) {
|
||||
writer.append(")");
|
||||
}
|
||||
} else {
|
||||
context.writeExpr(context.getArgument(0), context.getPrecedence());
|
||||
}
|
||||
break;
|
||||
case "unwrapString":
|
||||
writer.appendFunction("$rt_str").append("(");
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
break;
|
||||
case "unwrapBoolean":
|
||||
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
|
||||
writer.append("(");
|
||||
}
|
||||
context.writeExpr(context.getArgument(0), Precedence.CONDITIONAL.next());
|
||||
writer.ws().append("?").ws().append("1").ws().append(":").ws().append("0");
|
||||
if (context.getPrecedence().ordinal() >= Precedence.CONDITIONAL.ordinal()) {
|
||||
writer.append(")");
|
||||
}
|
||||
break;
|
||||
|
||||
case "dataToByteArray":
|
||||
dataToArray(context, "$rt_bytecls");
|
||||
break;
|
||||
case "dataToShortArray":
|
||||
dataToArray(context, "$rt_shortcls");
|
||||
break;
|
||||
case "dataToCharArray":
|
||||
dataToArray(context, "$rt_charcls");
|
||||
break;
|
||||
case "dataToIntArray":
|
||||
dataToArray(context, "$rt_intcls");
|
||||
break;
|
||||
case "dataToFloatArray":
|
||||
dataToArray(context, "$rt_floatcls");
|
||||
break;
|
||||
case "dataToDoubleArray":
|
||||
dataToArray(context, "$rt_doublecls");
|
||||
break;
|
||||
case "dataToArray":
|
||||
dataToArray(context, "$rt_objcls");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (methodRef.getName().startsWith("unwrap")) {
|
||||
context.writeExpr(context.getArgument(0), context.getPrecedence());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void dataToArray(InjectorContext context, String className) throws IOException {
|
||||
var writer = context.getWriter();
|
||||
writer.appendFunction("$rt_wrapArray").append("(").appendFunction(className).append(",").ws();
|
||||
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||
writer.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getReference().getName()) {
|
||||
case "invoke":
|
||||
case "instantiate":
|
||||
case "function":
|
||||
if (reachedFunctorMethods.add(method.getReference()) && !method.isMissing()) {
|
||||
for (int i = 0; i < method.getReference().parameterCount(); ++i) {
|
||||
DependencyNode node = method.getVariable(i);
|
||||
if (functorParamNodes.add(node)) {
|
||||
node.addConsumer(type -> {
|
||||
if (agent.getClassHierarchy().isSuperType(method.getMethod().getOwnerName(),
|
||||
type.getName(), false)) {
|
||||
reachFunctorMethods(agent, type.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "unwrapString":
|
||||
method.getResult().propagate(agent.getType("java.lang.String"));
|
||||
break;
|
||||
|
||||
case "dataToByteArray":
|
||||
method.getResult().propagate(agent.getType("[B"));
|
||||
break;
|
||||
case "dataToShortArray":
|
||||
method.getResult().propagate(agent.getType("[S"));
|
||||
break;
|
||||
case "dataToCharArray":
|
||||
method.getResult().propagate(agent.getType("[C"));
|
||||
break;
|
||||
case "dataToIntArray":
|
||||
method.getResult().propagate(agent.getType("[I"));
|
||||
break;
|
||||
case "dataToFloatArray":
|
||||
method.getResult().propagate(agent.getType("[F"));
|
||||
break;
|
||||
case "dataToDoubleArray":
|
||||
method.getResult().propagate(agent.getType("[D"));
|
||||
break;
|
||||
case "dataToArray":
|
||||
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void reachFunctorMethods(DependencyAgent agent, String type) {
|
||||
ClassReader cls = agent.getClassSource().get(type);
|
||||
if (cls != null) {
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||
agent.linkMethod(method.getReference()).use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void renderProperty(Expr property, InjectorContext context) throws IOException {
|
||||
SourceWriter writer = context.getWriter();
|
||||
String name = extractPropertyName(property);
|
||||
if (name == null) {
|
||||
writer.append('[');
|
||||
context.writeExpr(property, Precedence.min());
|
||||
writer.append(']');
|
||||
} else if (!isIdentifier(name)) {
|
||||
writer.append("[\"");
|
||||
context.writeEscaped(name);
|
||||
writer.append("\"]");
|
||||
} else {
|
||||
writer.append(".").append(name);
|
||||
}
|
||||
}
|
||||
|
||||
private String extractPropertyName(Expr propertyName) {
|
||||
if (!(propertyName instanceof InvocationExpr)) {
|
||||
return null;
|
||||
}
|
||||
InvocationExpr invoke = (InvocationExpr) propertyName;
|
||||
if (!invoke.getMethod().getClassName().equals(JS.class.getName())) {
|
||||
return null;
|
||||
}
|
||||
if (!invoke.getMethod().getName().equals("wrap")
|
||||
|| !invoke.getMethod().getDescriptor().parameterType(0).isObject("java.lang.String")) {
|
||||
return null;
|
||||
}
|
||||
Expr arg = invoke.getArguments().get(0);
|
||||
if (!(arg instanceof ConstantExpr)) {
|
||||
return null;
|
||||
}
|
||||
ConstantExpr constant = (ConstantExpr) arg;
|
||||
return constant.getValue() instanceof String ? (String) constant.getValue() : null;
|
||||
}
|
||||
|
||||
private boolean isIdentifier(String name) {
|
||||
if (name.isEmpty() || !Character.isJavaIdentifierStart(name.charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < name.length(); ++i) {
|
||||
if (!Character.isJavaIdentifierPart(name.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
34
jso/impl/src/main/resources/org/teavm/jso/impl/JS.js
Normal file
34
jso/impl/src/main/resources/org/teavm/jso/impl/JS.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
function jsFunction(self, method) {
|
||||
let name = 'jso$functor$' + method;
|
||||
let result = self[name];
|
||||
if (typeof result !== 'function') {
|
||||
let fn = function() {
|
||||
return self[method].apply(self, arguments);
|
||||
}
|
||||
result = () => fn;
|
||||
self[name] = result;
|
||||
}
|
||||
return result();
|
||||
}
|
||||
function jsFunctionAsObject(self, method) {
|
||||
if (typeof self !== 'function') return self;
|
||||
let result = {};
|
||||
result[method] = self;
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue
Block a user