mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-03 05:44:10 -08:00
JS: add conditional intrinsic to JS template engine, use it to get rid of remaining generated methods in runtime
This commit is contained in:
parent
c5768e07bc
commit
5d5fb47ca8
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* 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.backend.javascript.ast;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import org.mozilla.javascript.Token;
|
||||
import org.mozilla.javascript.ast.ArrayComprehension;
|
||||
import org.mozilla.javascript.ast.ArrayComprehensionLoop;
|
||||
import org.mozilla.javascript.ast.ArrayLiteral;
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
import org.mozilla.javascript.ast.Block;
|
||||
import org.mozilla.javascript.ast.BreakStatement;
|
||||
import org.mozilla.javascript.ast.CatchClause;
|
||||
import org.mozilla.javascript.ast.ConditionalExpression;
|
||||
import org.mozilla.javascript.ast.ContinueStatement;
|
||||
import org.mozilla.javascript.ast.DoLoop;
|
||||
import org.mozilla.javascript.ast.ElementGet;
|
||||
import org.mozilla.javascript.ast.EmptyExpression;
|
||||
import org.mozilla.javascript.ast.EmptyStatement;
|
||||
import org.mozilla.javascript.ast.ExpressionStatement;
|
||||
import org.mozilla.javascript.ast.ForInLoop;
|
||||
import org.mozilla.javascript.ast.ForLoop;
|
||||
import org.mozilla.javascript.ast.FunctionCall;
|
||||
import org.mozilla.javascript.ast.FunctionNode;
|
||||
import org.mozilla.javascript.ast.GeneratorExpression;
|
||||
import org.mozilla.javascript.ast.GeneratorExpressionLoop;
|
||||
import org.mozilla.javascript.ast.IfStatement;
|
||||
import org.mozilla.javascript.ast.InfixExpression;
|
||||
import org.mozilla.javascript.ast.KeywordLiteral;
|
||||
import org.mozilla.javascript.ast.LabeledStatement;
|
||||
import org.mozilla.javascript.ast.LetNode;
|
||||
import org.mozilla.javascript.ast.Name;
|
||||
import org.mozilla.javascript.ast.NewExpression;
|
||||
import org.mozilla.javascript.ast.NumberLiteral;
|
||||
import org.mozilla.javascript.ast.ObjectLiteral;
|
||||
import org.mozilla.javascript.ast.ObjectProperty;
|
||||
import org.mozilla.javascript.ast.ParenthesizedExpression;
|
||||
import org.mozilla.javascript.ast.PropertyGet;
|
||||
import org.mozilla.javascript.ast.RegExpLiteral;
|
||||
import org.mozilla.javascript.ast.ReturnStatement;
|
||||
import org.mozilla.javascript.ast.Scope;
|
||||
import org.mozilla.javascript.ast.StringLiteral;
|
||||
import org.mozilla.javascript.ast.SwitchCase;
|
||||
import org.mozilla.javascript.ast.SwitchStatement;
|
||||
import org.mozilla.javascript.ast.ThrowStatement;
|
||||
import org.mozilla.javascript.ast.TryStatement;
|
||||
import org.mozilla.javascript.ast.UnaryExpression;
|
||||
import org.mozilla.javascript.ast.UpdateExpression;
|
||||
import org.mozilla.javascript.ast.VariableDeclaration;
|
||||
import org.mozilla.javascript.ast.VariableInitializer;
|
||||
import org.mozilla.javascript.ast.WhileLoop;
|
||||
|
||||
public class AstVisitor {
|
||||
protected AstNode replacement;
|
||||
protected boolean hasReplacement;
|
||||
|
||||
public final void visit(AstNode node) {
|
||||
switch (node.getType()) {
|
||||
case Token.SCRIPT:
|
||||
visit((AstRoot) node);
|
||||
break;
|
||||
case Token.CALL:
|
||||
case Token.NEW:
|
||||
visit((FunctionCall) node);
|
||||
break;
|
||||
case Token.FUNCTION:
|
||||
visit((FunctionNode) node);
|
||||
break;
|
||||
case Token.ARRAYCOMP:
|
||||
visit((ArrayComprehension) node);
|
||||
break;
|
||||
case Token.GETPROP:
|
||||
visit((PropertyGet) node);
|
||||
break;
|
||||
case Token.GENEXPR:
|
||||
visit((GeneratorExpression) node);
|
||||
break;
|
||||
case Token.NUMBER:
|
||||
visit((NumberLiteral) node);
|
||||
break;
|
||||
case Token.STRING:
|
||||
visit((StringLiteral) node);
|
||||
break;
|
||||
case Token.TRUE:
|
||||
case Token.FALSE:
|
||||
case Token.THIS:
|
||||
case Token.NULL:
|
||||
visit((KeywordLiteral) node);
|
||||
break;
|
||||
case Token.NAME:
|
||||
visit((Name) node);
|
||||
break;
|
||||
case Token.REGEXP:
|
||||
visit((RegExpLiteral) node);
|
||||
break;
|
||||
case Token.OBJECTLIT:
|
||||
visit((ObjectLiteral) node);
|
||||
break;
|
||||
case Token.ARRAYLIT:
|
||||
visit((ArrayLiteral) node);
|
||||
break;
|
||||
case Token.BLOCK:
|
||||
if (node instanceof Block) {
|
||||
visit((Block) node);
|
||||
} else if (node instanceof Scope) {
|
||||
visit((Scope) node);
|
||||
}
|
||||
break;
|
||||
case Token.HOOK:
|
||||
visit((ConditionalExpression) node);
|
||||
break;
|
||||
case Token.GETELEM:
|
||||
visit((ElementGet) node);
|
||||
break;
|
||||
case Token.LETEXPR:
|
||||
visit((LetNode) node);
|
||||
break;
|
||||
case Token.LP:
|
||||
visit((ParenthesizedExpression) node);
|
||||
break;
|
||||
case Token.EMPTY:
|
||||
if (node instanceof EmptyExpression) {
|
||||
visit((EmptyExpression) node);
|
||||
} else if (node instanceof EmptyStatement) {
|
||||
visit((EmptyStatement) node);
|
||||
}
|
||||
break;
|
||||
case Token.EXPR_VOID:
|
||||
case Token.EXPR_RESULT:
|
||||
if (node instanceof ExpressionStatement) {
|
||||
visit((ExpressionStatement) node);
|
||||
} else if (node instanceof LabeledStatement) {
|
||||
visit((LabeledStatement) node);
|
||||
}
|
||||
break;
|
||||
case Token.BREAK:
|
||||
visit((BreakStatement) node);
|
||||
break;
|
||||
case Token.CONTINUE:
|
||||
visit((ContinueStatement) node);
|
||||
break;
|
||||
case Token.RETURN:
|
||||
visit((ReturnStatement) node);
|
||||
break;
|
||||
case Token.DO:
|
||||
visit((DoLoop) node);
|
||||
break;
|
||||
case Token.FOR:
|
||||
if (node instanceof ForInLoop) {
|
||||
visit((ForInLoop) node);
|
||||
} else if (node instanceof ForLoop) {
|
||||
visit((ForLoop) node);
|
||||
}
|
||||
break;
|
||||
case Token.IF:
|
||||
visit((IfStatement) node);
|
||||
break;
|
||||
case Token.SWITCH:
|
||||
visit((SwitchStatement) node);
|
||||
break;
|
||||
case Token.THROW:
|
||||
visit((ThrowStatement) node);
|
||||
break;
|
||||
case Token.TRY:
|
||||
visit((TryStatement) node);
|
||||
break;
|
||||
case Token.CONST:
|
||||
case Token.VAR:
|
||||
case Token.LET:
|
||||
visit((VariableDeclaration) node);
|
||||
break;
|
||||
case Token.WHILE:
|
||||
visit((WhileLoop) node);
|
||||
break;
|
||||
default:
|
||||
if (node instanceof InfixExpression) {
|
||||
visit((InfixExpression) node);
|
||||
} else if (node instanceof UnaryExpression) {
|
||||
visit((UnaryExpression) node);
|
||||
} else if (node instanceof UpdateExpression) {
|
||||
visit((UpdateExpression) node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected final void visitMany(List<AstNode> nodes) {
|
||||
for (var i = 0; i < nodes.size(); ++i) {
|
||||
var node = nodes.get(i);
|
||||
visit(node);
|
||||
if (hasReplacement) {
|
||||
nodes.set(i, replacement);
|
||||
}
|
||||
hasReplacement = false;
|
||||
replacement = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected final void visitChildren(AstNode node) {
|
||||
for (var child = node.getFirstChild(); child != null;) {
|
||||
var next = child.getNext();
|
||||
visit((AstNode) child);
|
||||
if (hasReplacement) {
|
||||
if (replacement != null) {
|
||||
node.replaceChild(child, replacement);
|
||||
} else {
|
||||
node.removeChild(child);
|
||||
}
|
||||
replacement = null;
|
||||
hasReplacement = false;
|
||||
}
|
||||
child = next;
|
||||
}
|
||||
}
|
||||
|
||||
protected final <T extends AstNode, S extends AstNode> void visitProperty(T owner, Function<T, S> getter,
|
||||
BiConsumer<T, S> setter) {
|
||||
visitProperty(owner, getter, setter, null);
|
||||
}
|
||||
|
||||
protected final <T extends AstNode, S extends AstNode> void visitProperty(T owner, Function<T, S> getter,
|
||||
BiConsumer<T, S> setter, Supplier<S> defaultValue) {
|
||||
var node = getter.apply(owner);
|
||||
if (node != null) {
|
||||
visit(node);
|
||||
if (hasReplacement) {
|
||||
if (replacement == null && defaultValue != null) {
|
||||
replacement = defaultValue.get();
|
||||
}
|
||||
//noinspection unchecked
|
||||
setter.accept(owner, (S) replacement);
|
||||
}
|
||||
replacement = null;
|
||||
hasReplacement = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(AstRoot node) {
|
||||
visitChildren(node);
|
||||
}
|
||||
|
||||
public void visit(Block node) {
|
||||
visitChildren(node);
|
||||
}
|
||||
|
||||
public void visit(Scope node) {
|
||||
visitChildren(node);
|
||||
}
|
||||
|
||||
public void visit(LabeledStatement node) {
|
||||
visitProperty(node, LabeledStatement::getStatement, LabeledStatement::setStatement, EMPTY_DEFAULT);
|
||||
}
|
||||
|
||||
public void visit(BreakStatement node) {
|
||||
}
|
||||
|
||||
public void visit(ContinueStatement node) {
|
||||
}
|
||||
|
||||
public void visit(ReturnStatement node) {
|
||||
visitProperty(node, ReturnStatement::getReturnValue, ReturnStatement::setReturnValue);
|
||||
}
|
||||
|
||||
public void visit(ThrowStatement node) {
|
||||
visitProperty(node, ThrowStatement::getExpression, ThrowStatement::setExpression,
|
||||
() -> new KeywordLiteral(0, 0, Token.NULL));
|
||||
}
|
||||
|
||||
public void visit(DoLoop node) {
|
||||
visitProperty(node, DoLoop::getBody, DoLoop::setBody, EMPTY_DEFAULT);
|
||||
visitProperty(node, DoLoop::getCondition, DoLoop::setCondition, NULL_DEFAULT);
|
||||
}
|
||||
|
||||
public void visit(ForInLoop 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);
|
||||
}
|
||||
|
||||
public void visit(ForLoop 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);
|
||||
}
|
||||
|
||||
public void visit(WhileLoop node) {
|
||||
visitProperty(node, WhileLoop::getCondition, WhileLoop::setCondition, NULL_DEFAULT);
|
||||
visitProperty(node, WhileLoop::getBody, WhileLoop::setBody, EMPTY_DEFAULT);
|
||||
}
|
||||
|
||||
public void visit(IfStatement node) {
|
||||
visitProperty(node, IfStatement::getCondition, IfStatement::setCondition, NULL_DEFAULT);
|
||||
visitProperty(node, IfStatement::getThenPart, IfStatement::setThenPart, EMPTY_DEFAULT);
|
||||
visitProperty(node, IfStatement::getElsePart, IfStatement::setElsePart);
|
||||
}
|
||||
|
||||
public void visit(SwitchStatement node) {
|
||||
visitProperty(node, SwitchStatement::getExpression, SwitchStatement::setExpression, NULL_DEFAULT);
|
||||
for (var sc : node.getCases()) {
|
||||
visitProperty(sc, SwitchCase::getExpression, SwitchCase::setExpression);
|
||||
if (sc.getStatements() != null) {
|
||||
visitMany(sc.getStatements());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(TryStatement node) {
|
||||
visitProperty(node, TryStatement::getTryBlock, TryStatement::setTryBlock, NULL_DEFAULT);
|
||||
for (var cc : node.getCatchClauses()) {
|
||||
visitProperty(cc, CatchClause::getVarName, CatchClause::setVarName);
|
||||
visitProperty(cc, CatchClause::getCatchCondition, CatchClause::setCatchCondition);
|
||||
if (cc.getBody() != null) {
|
||||
visitChildren(cc.getBody());
|
||||
}
|
||||
}
|
||||
visitProperty(node, TryStatement::getFinallyBlock, TryStatement::setFinallyBlock);
|
||||
}
|
||||
|
||||
public void visit(VariableDeclaration node) {
|
||||
for (var variable : node.getVariables()) {
|
||||
visit(variable);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(VariableInitializer node) {
|
||||
visitProperty(node, VariableInitializer::getTarget, VariableInitializer::setTarget);
|
||||
visitProperty(node, VariableInitializer::getInitializer, VariableInitializer::setInitializer);
|
||||
}
|
||||
|
||||
public void visit(ExpressionStatement node) {
|
||||
visitProperty(node, ExpressionStatement::getExpression, ExpressionStatement::setExpression, NULL_DEFAULT);
|
||||
}
|
||||
|
||||
public void visit(ElementGet node) {
|
||||
visitProperty(node, ElementGet::getTarget, ElementGet::setTarget, NULL_DEFAULT);
|
||||
visitProperty(node, ElementGet::getElement, ElementGet::setElement, NULL_DEFAULT);
|
||||
}
|
||||
|
||||
public void visit(PropertyGet node) {
|
||||
visitProperty(node, PropertyGet::getTarget, PropertyGet::setTarget, NULL_DEFAULT);
|
||||
visitProperty(node, PropertyGet::getProperty, PropertyGet::setProperty);
|
||||
}
|
||||
|
||||
public void visit(FunctionCall node) {
|
||||
visitProperty(node, FunctionCall::getTarget, FunctionCall::setTarget);
|
||||
visitMany(node.getArguments());
|
||||
if (node instanceof NewExpression) {
|
||||
var newExpr = (NewExpression) node;
|
||||
visitProperty(newExpr, NewExpression::getInitializer, NewExpression::setInitializer);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(ConditionalExpression node) {
|
||||
visitProperty(node, ConditionalExpression::getTestExpression, ConditionalExpression::setTestExpression);
|
||||
visitProperty(node, ConditionalExpression::getTrueExpression, ConditionalExpression::setTrueExpression);
|
||||
visitProperty(node, ConditionalExpression::getFalseExpression, ConditionalExpression::setFalseExpression);
|
||||
}
|
||||
|
||||
public void visit(ArrayComprehension 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);
|
||||
}
|
||||
|
||||
public void visit(GeneratorExpression node) {
|
||||
for (var loop : node.getLoops()) {
|
||||
visitProperty(loop, GeneratorExpressionLoop::getIterator, GeneratorExpressionLoop::setIterator);
|
||||
visitProperty(loop, GeneratorExpressionLoop::getIteratedObject,
|
||||
GeneratorExpressionLoop::setIteratedObject);
|
||||
}
|
||||
visitProperty(node, GeneratorExpression::getFilter, GeneratorExpression::setFilter);
|
||||
visitProperty(node, GeneratorExpression::getResult, GeneratorExpression::setResult);
|
||||
}
|
||||
|
||||
public void visit(NumberLiteral node) {
|
||||
}
|
||||
|
||||
public void visit(StringLiteral node) {
|
||||
}
|
||||
|
||||
public void visit(KeywordLiteral node) {
|
||||
}
|
||||
|
||||
public void visit(Name node) {
|
||||
}
|
||||
|
||||
public void visit(RegExpLiteral node) {
|
||||
}
|
||||
|
||||
public void visit(ArrayLiteral node) {
|
||||
visitMany(node.getElements());
|
||||
}
|
||||
|
||||
public void visit(ObjectLiteral node) {
|
||||
if (node.getElements() != null) {
|
||||
for (var element : node.getElements()) {
|
||||
visit(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(ObjectProperty node) {
|
||||
visitProperty(node, ObjectProperty::getLeft, ObjectProperty::setLeft);
|
||||
visitProperty(node, ObjectProperty::getRight, ObjectProperty::setRight);
|
||||
}
|
||||
|
||||
public void visit(FunctionNode node) {
|
||||
visitProperty(node, FunctionNode::getFunctionName, FunctionNode::setFunctionName);
|
||||
visitMany(node.getParams());
|
||||
visitChildren(node.getBody());
|
||||
}
|
||||
|
||||
public void visit(LetNode node) {
|
||||
visitProperty(node, LetNode::getVariables, LetNode::setVariables);
|
||||
visitProperty(node, LetNode::getBody, LetNode::setBody);
|
||||
}
|
||||
|
||||
public void visit(ParenthesizedExpression node) {
|
||||
visitProperty(node, ParenthesizedExpression::getExpression, ParenthesizedExpression::setExpression);
|
||||
}
|
||||
|
||||
public void visit(EmptyExpression node) {
|
||||
}
|
||||
|
||||
public void visit(EmptyStatement node) {
|
||||
}
|
||||
|
||||
public void visit(InfixExpression node) {
|
||||
visitProperty(node, InfixExpression::getLeft, InfixExpression::setLeft);
|
||||
visitProperty(node, InfixExpression::getRight, InfixExpression::setRight);
|
||||
}
|
||||
|
||||
public void visit(UnaryExpression node) {
|
||||
visitProperty(node, UnaryExpression::getOperand, UnaryExpression::setOperand);
|
||||
}
|
||||
|
||||
public void visit(UpdateExpression node) {
|
||||
visitProperty(node, UpdateExpression::getOperand, UpdateExpression::setOperand);
|
||||
}
|
||||
|
||||
protected final void replaceWith(AstNode node) {
|
||||
hasReplacement = true;
|
||||
replacement = node;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
|
@ -1,441 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 konsoletyper.
|
||||
*
|
||||
* 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 org.mozilla.javascript.Node;
|
||||
import org.mozilla.javascript.Token;
|
||||
import org.mozilla.javascript.ast.ArrayComprehension;
|
||||
import org.mozilla.javascript.ast.ArrayComprehensionLoop;
|
||||
import org.mozilla.javascript.ast.ArrayLiteral;
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
import org.mozilla.javascript.ast.Block;
|
||||
import org.mozilla.javascript.ast.BreakStatement;
|
||||
import org.mozilla.javascript.ast.CatchClause;
|
||||
import org.mozilla.javascript.ast.ConditionalExpression;
|
||||
import org.mozilla.javascript.ast.ContinueStatement;
|
||||
import org.mozilla.javascript.ast.DoLoop;
|
||||
import org.mozilla.javascript.ast.ElementGet;
|
||||
import org.mozilla.javascript.ast.EmptyExpression;
|
||||
import org.mozilla.javascript.ast.EmptyStatement;
|
||||
import org.mozilla.javascript.ast.ExpressionStatement;
|
||||
import org.mozilla.javascript.ast.ForInLoop;
|
||||
import org.mozilla.javascript.ast.ForLoop;
|
||||
import org.mozilla.javascript.ast.FunctionCall;
|
||||
import org.mozilla.javascript.ast.FunctionNode;
|
||||
import org.mozilla.javascript.ast.GeneratorExpression;
|
||||
import org.mozilla.javascript.ast.GeneratorExpressionLoop;
|
||||
import org.mozilla.javascript.ast.IfStatement;
|
||||
import org.mozilla.javascript.ast.InfixExpression;
|
||||
import org.mozilla.javascript.ast.LabeledStatement;
|
||||
import org.mozilla.javascript.ast.LetNode;
|
||||
import org.mozilla.javascript.ast.Name;
|
||||
import org.mozilla.javascript.ast.NewExpression;
|
||||
import org.mozilla.javascript.ast.NumberLiteral;
|
||||
import org.mozilla.javascript.ast.ObjectLiteral;
|
||||
import org.mozilla.javascript.ast.ObjectProperty;
|
||||
import org.mozilla.javascript.ast.ParenthesizedExpression;
|
||||
import org.mozilla.javascript.ast.PropertyGet;
|
||||
import org.mozilla.javascript.ast.RegExpLiteral;
|
||||
import org.mozilla.javascript.ast.ReturnStatement;
|
||||
import org.mozilla.javascript.ast.Scope;
|
||||
import org.mozilla.javascript.ast.StringLiteral;
|
||||
import org.mozilla.javascript.ast.SwitchCase;
|
||||
import org.mozilla.javascript.ast.SwitchStatement;
|
||||
import org.mozilla.javascript.ast.ThrowStatement;
|
||||
import org.mozilla.javascript.ast.TryStatement;
|
||||
import org.mozilla.javascript.ast.UnaryExpression;
|
||||
import org.mozilla.javascript.ast.VariableDeclaration;
|
||||
import org.mozilla.javascript.ast.VariableInitializer;
|
||||
import org.mozilla.javascript.ast.WhileLoop;
|
||||
|
||||
public class AstVisitor {
|
||||
public void accept(AstNode node) {
|
||||
switch (node.getType()) {
|
||||
case Token.SCRIPT:
|
||||
visitRoot((AstRoot) node);
|
||||
break;
|
||||
case Token.CALL:
|
||||
case Token.NEW:
|
||||
visitFunctionCall((FunctionCall) node);
|
||||
break;
|
||||
case Token.FUNCTION:
|
||||
visitFunction((FunctionNode) node);
|
||||
break;
|
||||
case Token.ARRAYCOMP:
|
||||
visitArrayComprehension((ArrayComprehension) node);
|
||||
break;
|
||||
case Token.GETPROP:
|
||||
visitPropertyGet((PropertyGet) node);
|
||||
break;
|
||||
case Token.GENEXPR:
|
||||
visitGenerator((GeneratorExpression) node);
|
||||
break;
|
||||
case Token.NUMBER:
|
||||
visitNumber((NumberLiteral) node);
|
||||
break;
|
||||
case Token.STRING:
|
||||
visitString((StringLiteral) node);
|
||||
break;
|
||||
case Token.TRUE:
|
||||
visitTrue(node);
|
||||
break;
|
||||
case Token.FALSE:
|
||||
visitFalse(node);
|
||||
break;
|
||||
case Token.THIS:
|
||||
visitThis(node);
|
||||
break;
|
||||
case Token.NULL:
|
||||
visitNull(node);
|
||||
break;
|
||||
case Token.NAME:
|
||||
visitName((Name) node);
|
||||
break;
|
||||
case Token.REGEXP:
|
||||
visitRegexp((RegExpLiteral) node);
|
||||
break;
|
||||
case Token.OBJECTLIT:
|
||||
visitObjectLiteral((ObjectLiteral) node);
|
||||
break;
|
||||
case Token.ARRAYLIT:
|
||||
visitArrayLiteral((ArrayLiteral) node);
|
||||
break;
|
||||
case Token.BLOCK:
|
||||
if (node instanceof Block) {
|
||||
visitBlock((Block) node);
|
||||
} else if (node instanceof Scope) {
|
||||
visitScope((Scope) node);
|
||||
}
|
||||
break;
|
||||
case Token.HOOK:
|
||||
visitConditionalExpr((ConditionalExpression) node);
|
||||
break;
|
||||
case Token.GETELEM:
|
||||
visitElementGet((ElementGet) node);
|
||||
break;
|
||||
case Token.LETEXPR:
|
||||
visitLet((LetNode) node);
|
||||
break;
|
||||
case Token.LP:
|
||||
visitParenthesized((ParenthesizedExpression) node);
|
||||
break;
|
||||
case Token.EMPTY:
|
||||
if (node instanceof EmptyStatement) {
|
||||
visitEmpty((EmptyStatement) node);
|
||||
} else {
|
||||
visitEmpty((EmptyExpression) node);
|
||||
}
|
||||
break;
|
||||
case Token.EXPR_VOID:
|
||||
case Token.EXPR_RESULT:
|
||||
if (node instanceof ExpressionStatement) {
|
||||
visitExpressionStatement((ExpressionStatement) node);
|
||||
} else if (node instanceof LabeledStatement) {
|
||||
visitLabeledStatement((LabeledStatement) node);
|
||||
}
|
||||
break;
|
||||
case Token.BREAK:
|
||||
visitBreak((BreakStatement) node);
|
||||
break;
|
||||
case Token.CONTINUE:
|
||||
visitContinue((ContinueStatement) node);
|
||||
break;
|
||||
case Token.RETURN:
|
||||
visitReturn((ReturnStatement) node);
|
||||
break;
|
||||
case Token.DO:
|
||||
visitDo((DoLoop) node);
|
||||
break;
|
||||
case Token.FOR:
|
||||
if (node instanceof ForInLoop) {
|
||||
visitForIn((ForInLoop) node);
|
||||
} else if (node instanceof ForLoop) {
|
||||
visitFor((ForLoop) node);
|
||||
}
|
||||
break;
|
||||
case Token.IF:
|
||||
visitIf((IfStatement) node);
|
||||
break;
|
||||
case Token.SWITCH:
|
||||
visitSwitch((SwitchStatement) node);
|
||||
break;
|
||||
case Token.THROW:
|
||||
visitThrow((ThrowStatement) node);
|
||||
break;
|
||||
case Token.TRY:
|
||||
visitTry((TryStatement) node);
|
||||
break;
|
||||
case Token.CONST:
|
||||
case Token.VAR:
|
||||
case Token.LET:
|
||||
visitVariableDeclaration((VariableDeclaration) node);
|
||||
break;
|
||||
case Token.WHILE:
|
||||
visitWhile((WhileLoop) node);
|
||||
break;
|
||||
default:
|
||||
if (node instanceof InfixExpression) {
|
||||
visitInfix((InfixExpression) node);
|
||||
} else if (node instanceof UnaryExpression) {
|
||||
visitUnary((UnaryExpression) node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitRoot(AstRoot node) {
|
||||
for (Node child : node) {
|
||||
accept((AstNode) child);
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitBlock(Block node) {
|
||||
for (Node child = node.getFirstChild(); child != null; child = child.getNext()) {
|
||||
accept((AstNode) child);
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitScope(Scope node) {
|
||||
for (Node child = node.getFirstChild(); child != null; child = child.getNext()) {
|
||||
accept((AstNode) child);
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitLabeledStatement(LabeledStatement node) {
|
||||
accept(node.getStatement());
|
||||
}
|
||||
|
||||
protected void visitBreak(BreakStatement node) {
|
||||
}
|
||||
|
||||
protected void visitContinue(ContinueStatement node) {
|
||||
}
|
||||
|
||||
protected void visitReturn(ReturnStatement node) {
|
||||
if (node.getReturnValue() != null) {
|
||||
accept(node.getReturnValue());
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitThrow(ThrowStatement node) {
|
||||
accept(node.getExpression());
|
||||
}
|
||||
|
||||
protected void visitDo(DoLoop node) {
|
||||
accept(node.getBody());
|
||||
accept(node.getCondition());
|
||||
}
|
||||
|
||||
protected void visitForIn(ForInLoop node) {
|
||||
accept(node.getIterator());
|
||||
accept(node.getIteratedObject());
|
||||
accept(node.getBody());
|
||||
}
|
||||
|
||||
protected void visitFor(ForLoop node) {
|
||||
accept(node.getInitializer());
|
||||
accept(node.getCondition());
|
||||
accept(node.getIncrement());
|
||||
accept(node.getBody());
|
||||
}
|
||||
|
||||
protected void visitWhile(WhileLoop node) {
|
||||
accept(node.getCondition());
|
||||
accept(node.getBody());
|
||||
}
|
||||
|
||||
protected void visitIf(IfStatement node) {
|
||||
accept(node.getCondition());
|
||||
accept(node.getThenPart());
|
||||
if (node.getElsePart() != null) {
|
||||
accept(node.getElsePart());
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitSwitch(SwitchStatement node) {
|
||||
accept(node.getExpression());
|
||||
for (SwitchCase sc : node.getCases()) {
|
||||
if (sc.getExpression() != null) {
|
||||
accept(sc.getExpression());
|
||||
}
|
||||
if (sc.getStatements() != null) {
|
||||
for (AstNode stmt : sc.getStatements()) {
|
||||
accept(stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitTry(TryStatement node) {
|
||||
accept(node.getTryBlock());
|
||||
for (CatchClause cc : node.getCatchClauses()) {
|
||||
if (cc.getCatchCondition() != null) {
|
||||
accept(cc.getCatchCondition());
|
||||
}
|
||||
accept(cc.getBody());
|
||||
}
|
||||
if (node.getFinallyBlock() != null) {
|
||||
accept(node.getFinallyBlock());
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitVariableDeclaration(VariableDeclaration node) {
|
||||
for (int i = 1; i < node.getVariables().size(); ++i) {
|
||||
visitVariableInitializer(node.getVariables().get(i));
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitVariableInitializer(VariableInitializer node) {
|
||||
accept(node.getTarget());
|
||||
if (node.getInitializer() != null) {
|
||||
accept(node.getInitializer());
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitExpressionStatement(ExpressionStatement node) {
|
||||
accept(node.getExpression());
|
||||
}
|
||||
|
||||
protected void visitElementGet(ElementGet node) {
|
||||
accept(node.getTarget());
|
||||
accept(node.getElement());
|
||||
}
|
||||
|
||||
protected void visitPropertyGet(PropertyGet node) {
|
||||
accept(node.getLeft());
|
||||
accept(node.getRight());
|
||||
}
|
||||
|
||||
protected void visitFunctionCall(FunctionCall node) {
|
||||
accept(node.getTarget());
|
||||
for (AstNode arg : node.getArguments()) {
|
||||
accept(arg);
|
||||
}
|
||||
if (node instanceof NewExpression) {
|
||||
NewExpression newExpr = (NewExpression) node;
|
||||
if (newExpr.getInitializer() != null) {
|
||||
accept(newExpr.getInitializer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitConditionalExpr(ConditionalExpression node) {
|
||||
accept(node.getTestExpression());
|
||||
accept(node.getTrueExpression());
|
||||
accept(node.getFalseExpression());
|
||||
}
|
||||
|
||||
protected void visitArrayComprehension(ArrayComprehension node) {
|
||||
for (ArrayComprehensionLoop loop : node.getLoops()) {
|
||||
accept(loop.getIterator());
|
||||
accept(loop.getIteratedObject());
|
||||
}
|
||||
if (node.getFilter() != null) {
|
||||
accept(node.getFilter());
|
||||
}
|
||||
accept(node.getResult());
|
||||
}
|
||||
|
||||
protected void visitGenerator(GeneratorExpression node) {
|
||||
for (GeneratorExpressionLoop loop : node.getLoops()) {
|
||||
accept(loop.getIterator());
|
||||
accept(loop.getIteratedObject());
|
||||
}
|
||||
if (node.getFilter() != null) {
|
||||
accept(node.getFilter());
|
||||
}
|
||||
accept(node.getResult());
|
||||
}
|
||||
|
||||
protected void visitNumber(NumberLiteral node) {
|
||||
}
|
||||
|
||||
protected void visitString(StringLiteral node) {
|
||||
}
|
||||
|
||||
protected void visitThis(AstNode node) {
|
||||
}
|
||||
|
||||
protected void visitTrue(AstNode node) {
|
||||
}
|
||||
|
||||
protected void visitFalse(AstNode node) {
|
||||
}
|
||||
|
||||
protected void visitNull(AstNode node) {
|
||||
}
|
||||
|
||||
protected void visitEmpty(EmptyStatement node) {
|
||||
}
|
||||
|
||||
protected void visitEmpty(EmptyExpression node) {
|
||||
}
|
||||
|
||||
protected void visitName(Name node) {
|
||||
}
|
||||
|
||||
protected void visitRegexp(RegExpLiteral node) {
|
||||
}
|
||||
|
||||
protected void visitArrayLiteral(ArrayLiteral node) {
|
||||
for (AstNode element : node.getElements()) {
|
||||
accept(element);
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitObjectLiteral(ObjectLiteral node) {
|
||||
if (node.getElements() != null) {
|
||||
for (ObjectProperty property : node.getElements()) {
|
||||
visitObjectProperty(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void visitObjectProperty(ObjectProperty node) {
|
||||
accept(node.getLeft());
|
||||
accept(node.getRight());
|
||||
}
|
||||
|
||||
protected void visitFunction(FunctionNode node) {
|
||||
if (node.getFunctionName() != null) {
|
||||
accept(node.getFunctionName());
|
||||
}
|
||||
for (AstNode param : node.getParams()) {
|
||||
accept(param);
|
||||
}
|
||||
|
||||
accept(node.getBody());
|
||||
}
|
||||
|
||||
protected void visitLet(LetNode node) {
|
||||
accept(node.getVariables());
|
||||
accept(node.getBody());
|
||||
}
|
||||
|
||||
protected void visitParenthesized(ParenthesizedExpression node) {
|
||||
accept(node.getExpression());
|
||||
}
|
||||
|
||||
protected void visitUnary(UnaryExpression node) {
|
||||
accept(node.getOperand());
|
||||
}
|
||||
|
||||
protected void visitInfix(InfixExpression node) {
|
||||
accept(node.getLeft());
|
||||
accept(node.getRight());
|
||||
}
|
||||
}
|
|
@ -24,32 +24,12 @@ import org.mozilla.javascript.CompilerEnvirons;
|
|||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.templating.TemplatingAstTransformer;
|
||||
import org.teavm.backend.javascript.templating.TemplatingAstWriter;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.vm.RenderingException;
|
||||
|
||||
public class RuntimeRenderer {
|
||||
private static final String STRING_CLASS = String.class.getName();
|
||||
private static final String THREAD_CLASS = Thread.class.getName();
|
||||
private static final String STE_CLASS = StackTraceElement.class.getName();
|
||||
|
||||
private static final MethodDescriptor STRING_INTERN_METHOD = new MethodDescriptor("intern", String.class);
|
||||
private static final MethodDescriptor CURRENT_THREAD_METHOD = new MethodDescriptor("currentThread",
|
||||
Thread.class);
|
||||
private static final MethodReference STACK_TRACE_ELEM_INIT = new MethodReference(StackTraceElement.class,
|
||||
"<init>", String.class, String.class, String.class, int.class, void.class);
|
||||
private static final MethodReference SET_STACK_TRACE_METHOD = new MethodReference(Throwable.class,
|
||||
"setStackTrace", StackTraceElement[].class, void.class);
|
||||
private static final MethodReference AIOOBE_INIT_METHOD = new MethodReference(ArrayIndexOutOfBoundsException.class,
|
||||
"<init>", void.class);
|
||||
private static final MethodReference CCE_INIT_METHOD = new MethodReference(ClassCastException.class,
|
||||
"<init>", void.class);
|
||||
|
||||
private final ClassReaderSource classSource;
|
||||
private final SourceWriter writer;
|
||||
|
||||
|
@ -61,13 +41,7 @@ public class RuntimeRenderer {
|
|||
public void renderRuntime() throws RenderingException {
|
||||
try {
|
||||
renderHandWrittenRuntime("runtime.js");
|
||||
renderRuntimeThrowablecls();
|
||||
renderRuntimeIntern();
|
||||
renderRuntimeThreads();
|
||||
renderCreateStackTraceElement();
|
||||
renderSetStackTrace();
|
||||
renderThrowAIOOBE();
|
||||
renderThrowCCE();
|
||||
renderHandWrittenRuntime("intern.js");
|
||||
} catch (IOException e) {
|
||||
throw new RenderingException("IO error", e);
|
||||
}
|
||||
|
@ -76,6 +50,7 @@ public class RuntimeRenderer {
|
|||
public void renderHandWrittenRuntime(String name) throws IOException {
|
||||
AstRoot ast = parseRuntime(name);
|
||||
ast.visit(new StringConstantElimination());
|
||||
new TemplatingAstTransformer(classSource).visit(ast);
|
||||
var astWriter = new TemplatingAstWriter(writer);
|
||||
astWriter.hoist(ast);
|
||||
astWriter.print(ast);
|
||||
|
@ -93,121 +68,4 @@ public class RuntimeRenderer {
|
|||
return factory.parse(reader, null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderRuntimeIntern() throws IOException {
|
||||
if (!needInternMethod()) {
|
||||
writer.append("function $rt_intern(str) {").indent().softNewLine();
|
||||
writer.append("return str;").softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
} else {
|
||||
renderHandWrittenRuntime("intern.js");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needInternMethod() {
|
||||
ClassReader cls = classSource.get(STRING_CLASS);
|
||||
if (cls == null) {
|
||||
return false;
|
||||
}
|
||||
MethodReader method = cls.getMethod(STRING_INTERN_METHOD);
|
||||
return method != null && method.hasModifier(ElementModifier.NATIVE);
|
||||
}
|
||||
|
||||
private void renderRuntimeThrowablecls() throws IOException {
|
||||
writer.append("function $rt_stecls()").ws().append("{").indent().softNewLine();
|
||||
writer.append("return ");
|
||||
if (classSource.get(STE_CLASS) != null) {
|
||||
writer.appendClass(STE_CLASS);
|
||||
} else {
|
||||
writer.appendClass("java.lang.Object");
|
||||
}
|
||||
writer.append(";").softNewLine().outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeThreads() throws IOException {
|
||||
ClassReader threadCls = classSource.get(THREAD_CLASS);
|
||||
MethodReader currentThreadMethod = threadCls != null ? threadCls.getMethod(CURRENT_THREAD_METHOD) : null;
|
||||
boolean threadUsed = currentThreadMethod != null && currentThreadMethod.getProgram() != null;
|
||||
|
||||
writer.append("function $rt_getThread()").ws().append("{").indent().softNewLine();
|
||||
if (threadUsed) {
|
||||
writer.append("return ").appendMethodBody(Thread.class, "currentThread", Thread.class).append("();")
|
||||
.softNewLine();
|
||||
} else {
|
||||
writer.append("return null;").softNewLine();
|
||||
}
|
||||
writer.outdent().append("}").newLine();
|
||||
|
||||
writer.append("function $rt_setThread(t)").ws().append("{").indent().softNewLine();
|
||||
if (threadUsed) {
|
||||
writer.append("return ").appendMethodBody(Thread.class, "setCurrentThread", Thread.class, void.class)
|
||||
.append("(t);").softNewLine();
|
||||
}
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderCreateStackTraceElement() throws IOException {
|
||||
ClassReader cls = classSource.get(STACK_TRACE_ELEM_INIT.getClassName());
|
||||
MethodReader stackTraceElemInit = cls != null ? cls.getMethod(STACK_TRACE_ELEM_INIT.getDescriptor()) : null;
|
||||
boolean supported = stackTraceElemInit != null && stackTraceElemInit.getProgram() != null;
|
||||
|
||||
writer.append("function $rt_createStackElement(")
|
||||
.append("className,").ws()
|
||||
.append("methodName,").ws()
|
||||
.append("fileName,").ws()
|
||||
.append("lineNumber)").ws().append("{").indent().softNewLine();
|
||||
writer.append("return ");
|
||||
if (supported) {
|
||||
writer.appendInit(STACK_TRACE_ELEM_INIT);
|
||||
writer.append("(className,").ws()
|
||||
.append("methodName,").ws()
|
||||
.append("fileName,").ws()
|
||||
.append("lineNumber)");
|
||||
} else {
|
||||
writer.append("null");
|
||||
}
|
||||
writer.append(";").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderSetStackTrace() throws IOException {
|
||||
ClassReader cls = classSource.get(SET_STACK_TRACE_METHOD.getClassName());
|
||||
MethodReader setStackTrace = cls != null ? cls.getMethod(SET_STACK_TRACE_METHOD.getDescriptor()) : null;
|
||||
boolean supported = setStackTrace != null && setStackTrace.getProgram() != null;
|
||||
|
||||
writer.append("function $rt_setStack(e,").ws().append("stack)").ws().append("{").indent().softNewLine();
|
||||
if (supported) {
|
||||
writer.appendMethodBody(SET_STACK_TRACE_METHOD);
|
||||
writer.append("(e,").ws().append("stack);").softNewLine();
|
||||
}
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderThrowAIOOBE() throws IOException {
|
||||
writer.append("function $rt_throwAIOOBE()").ws().append("{").indent().softNewLine();
|
||||
|
||||
ClassReader cls = classSource.get(AIOOBE_INIT_METHOD.getClassName());
|
||||
if (cls != null) {
|
||||
MethodReader method = cls.getMethod(AIOOBE_INIT_METHOD.getDescriptor());
|
||||
if (method != null && !method.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
writer.append("$rt_throw(").appendInit(AIOOBE_INIT_METHOD).append("());").softNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderThrowCCE() throws IOException {
|
||||
writer.append("function $rt_throwCCE()").ws().append("{").indent().softNewLine();
|
||||
|
||||
ClassReader cls = classSource.get(CCE_INIT_METHOD.getClassName());
|
||||
if (cls != null) {
|
||||
MethodReader method = cls.getMethod(CCE_INIT_METHOD.getDescriptor());
|
||||
if (method != null && !method.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
writer.append("$rt_throw(").appendInit(CCE_INIT_METHOD).append("());").softNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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.backend.javascript.templating;
|
||||
|
||||
import org.mozilla.javascript.Token;
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
import org.mozilla.javascript.ast.Block;
|
||||
import org.mozilla.javascript.ast.ConditionalExpression;
|
||||
import org.mozilla.javascript.ast.FunctionCall;
|
||||
import org.mozilla.javascript.ast.IfStatement;
|
||||
import org.mozilla.javascript.ast.InfixExpression;
|
||||
import org.mozilla.javascript.ast.KeywordLiteral;
|
||||
import org.mozilla.javascript.ast.Name;
|
||||
import org.mozilla.javascript.ast.StringLiteral;
|
||||
import org.mozilla.javascript.ast.UnaryExpression;
|
||||
import org.teavm.backend.javascript.ast.AstVisitor;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class TemplatingAstTransformer extends AstVisitor {
|
||||
private ClassReaderSource classes;
|
||||
|
||||
public TemplatingAstTransformer(ClassReaderSource classes) {
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Block node) {
|
||||
super.visit(node);
|
||||
if (node.getFirstChild() == null) {
|
||||
replaceWith(null);
|
||||
} else if (node.getFirstChild() == node.getLastChild()) {
|
||||
replaceWith((AstNode) node.getFirstChild());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IfStatement node) {
|
||||
super.visit(node);
|
||||
if (node.getCondition().getType() == Token.TRUE) {
|
||||
replaceWith(node.getThenPart());
|
||||
} else if (node.getCondition().getType() == Token.FALSE) {
|
||||
replaceWith(node.getElsePart());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ConditionalExpression node) {
|
||||
super.visit(node);
|
||||
if (node.getTestExpression().getType() == Token.TRUE) {
|
||||
replaceWith(node.getTrueExpression());
|
||||
} else if (node.getTestExpression().getType() == Token.FALSE) {
|
||||
replaceWith(node.getFalseExpression());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InfixExpression node) {
|
||||
super.visit(node);
|
||||
if (node.getType() == Token.AND) {
|
||||
if (node.getLeft().getType() == Token.FALSE) {
|
||||
replaceWith(node.getLeft());
|
||||
} else if (node.getRight().getType() == Token.FALSE) {
|
||||
replaceWith(node.getRight());
|
||||
} else if (node.getLeft().getType() == Token.TRUE) {
|
||||
replaceWith(node.getRight());
|
||||
} else if (node.getRight().getType() == Token.TRUE) {
|
||||
replaceWith(node.getLeft());
|
||||
}
|
||||
} else if (node.getType() == Token.OR) {
|
||||
if (node.getLeft().getType() == Token.TRUE) {
|
||||
replaceWith(node.getLeft());
|
||||
} else if (node.getRight().getType() == Token.TRUE) {
|
||||
replaceWith(node.getRight());
|
||||
} else if (node.getLeft().getType() == Token.FALSE) {
|
||||
replaceWith(node.getRight());
|
||||
} else if (node.getRight().getType() == Token.FALSE) {
|
||||
replaceWith(node.getLeft());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UnaryExpression node) {
|
||||
super.visit(node);
|
||||
if (node.getType() == Token.NOT) {
|
||||
if (node.getOperand().getType() == Token.TRUE) {
|
||||
node.getOperand().setType(Token.FALSE);
|
||||
} else if (node.getOperand().getType() == Token.FALSE) {
|
||||
node.getOperand().setType(Token.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(FunctionCall node) {
|
||||
super.visit(node);
|
||||
if (node.getTarget() instanceof Name) {
|
||||
var name = (Name) node.getTarget();
|
||||
if (name.getDefiningScope() == null) {
|
||||
tryIntrinsicName(node, name.getIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryIntrinsicName(FunctionCall node, String identifier) {
|
||||
switch (identifier) {
|
||||
case "teavm_javaClassExists":
|
||||
javaClassExists(node);
|
||||
break;
|
||||
case "teavm_javaMethodExists":
|
||||
javaMethodExists(node);
|
||||
break;
|
||||
case "teavm_javaConstructorExists":
|
||||
javaConstructorExists(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void javaClassExists(FunctionCall node) {
|
||||
if (node.getArguments().size() != 1) {
|
||||
return;
|
||||
}
|
||||
var classArg = node.getArguments().get(0);
|
||||
if (!(classArg instanceof StringLiteral)) {
|
||||
return;
|
||||
}
|
||||
var className = ((StringLiteral) classArg).getValue();
|
||||
var exists = classes.get(className) != null;
|
||||
replaceWith(new KeywordLiteral(0, 0, exists ? Token.TRUE : Token.FALSE));
|
||||
}
|
||||
|
||||
private void javaMethodExists(FunctionCall node) {
|
||||
if (node.getArguments().size() != 2) {
|
||||
return;
|
||||
}
|
||||
var classArg = node.getArguments().get(0);
|
||||
var methodArg = node.getArguments().get(1);
|
||||
if (!(classArg instanceof StringLiteral) || !(methodArg instanceof StringLiteral)) {
|
||||
return;
|
||||
}
|
||||
var className = ((StringLiteral) classArg).getValue();
|
||||
var methodName = ((StringLiteral) methodArg).getValue();
|
||||
var method = classes.resolveImplementation(new MethodReference(className, MethodDescriptor.parse(methodName)));
|
||||
var exists = method != null && (method.getProgram() != null || method.hasModifier(ElementModifier.NATIVE));
|
||||
replaceWith(new KeywordLiteral(0, 0, exists ? Token.TRUE : Token.FALSE));
|
||||
}
|
||||
|
||||
private void javaConstructorExists(FunctionCall node) {
|
||||
if (node.getArguments().size() != 2) {
|
||||
return;
|
||||
}
|
||||
var classArg = node.getArguments().get(0);
|
||||
var methodArg = node.getArguments().get(1);
|
||||
if (!(classArg instanceof StringLiteral) || !(methodArg instanceof StringLiteral)) {
|
||||
return;
|
||||
}
|
||||
var className = ((StringLiteral) classArg).getValue();
|
||||
var methodName = ((StringLiteral) methodArg).getValue();
|
||||
var method = classes.resolveImplementation(new MethodReference(className, "<init>",
|
||||
MethodDescriptor.parseSignature(methodName)));
|
||||
var exists = method != null && (method.getProgram() != null || method.hasModifier(ElementModifier.NATIVE));
|
||||
replaceWith(new KeywordLiteral(0, 0, exists ? Token.TRUE : Token.FALSE));
|
||||
}
|
||||
}
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
"use strict";
|
||||
|
||||
var $rt_intern = function() {
|
||||
var $rt_intern
|
||||
if (teavm_javaMethodExists("java.lang.String", "intern()Ljava/lang/String;")) {
|
||||
$rt_intern = function() {
|
||||
var map = Object.create(null);
|
||||
|
||||
var get;
|
||||
|
@ -49,3 +51,8 @@ var $rt_intern = function() {
|
|||
|
||||
return get;
|
||||
}();
|
||||
} else {
|
||||
$rt_intern = function(str) {
|
||||
return str;
|
||||
}
|
||||
}
|
|
@ -968,3 +968,49 @@ function $rt_throwableMessage(t) {
|
|||
function $rt_throwableCause(t) {
|
||||
return teavm_javaMethod("java.lang.Throwable", "getCause()Ljava/lang/Throwable;")(t);
|
||||
}
|
||||
function $rt_stecls() {
|
||||
if (teavm_javaClassExists("java.lang.StackTraceElement")) {
|
||||
return teavm_javaClass("java.lang.StackTraceElement");
|
||||
} else {
|
||||
return $rt_objcls();
|
||||
}
|
||||
}
|
||||
function $rt_throwAIOOBE() {
|
||||
if (teavm_javaConstructorExists("java.lang.ArrayIndexOutOfBoundsException", "()V")) {
|
||||
$rt_throw(teavm_javaConstructor("java.lang.ArrayIndexOutOfBoundsException", "()V")());
|
||||
} else {
|
||||
$rt_throw($rt_createException($rt_str("")));
|
||||
}
|
||||
}
|
||||
function $rt_throwCCE() {
|
||||
if (teavm_javaConstructorExists("java.lang.ClassCastException", "()V")) {
|
||||
$rt_throw(teavm_javaConstructor("java.lang.ClassCastException", "()V")());
|
||||
} else {
|
||||
$rt_throw($rt_createException($rt_str("")));
|
||||
}
|
||||
}
|
||||
|
||||
function $rt_getThread() {
|
||||
if (teavm_javaMethodExists("java.lang.Thread", "currentThread()Ljava/lang/Thread;")) {
|
||||
return teavm_javaMethod("java.lang.Thread", "currentThread()Ljava/lang/Thread;")();
|
||||
}
|
||||
}
|
||||
function $rt_setThread(t) {
|
||||
if (teavm_javaMethodExists("java.lang.Thread", "setCurrentThread(Ljava/lang/Thread;)V")) {
|
||||
return teavm_javaMethod("java.lang.Thread", "setCurrentThread(Ljava/lang/Thread;)V")(t);
|
||||
}
|
||||
}
|
||||
function $rt_createStackElement(className, methodName, fileName, lineNumber) {
|
||||
if (teavm_javaConstructorExists("java.lang.StackTraceElement",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V")) {
|
||||
return teavm_javaConstructor("java.lang.StackTraceElement",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V")(className, methodName, fileName, lineNumber);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function $rt_setStack(e, stack) {
|
||||
if (teavm_javaMethodExists("java.lang.Throwable", "setStackTrace([Ljava/lang/StackTraceElement;)V")) {
|
||||
teavm_javaMethod("java.lang.Throwable", "setStackTrace([Ljava/lang/StackTraceElement;)V")(e, stack);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user