JS backend: added visitors with context
This commit is contained in:
committed by
Zalim Bashorov
parent
15e963de25
commit
915f8734c5
@@ -51,4 +51,9 @@ public class ChameleonJsExpression implements JsExpression {
|
||||
public JsExpression source(Object info) {
|
||||
return expression.source(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext visitor, JsContext ctx) {
|
||||
expression.traverse(visitor, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,4 +46,13 @@ public final class JsArrayAccess extends JsExpressionImpl {
|
||||
visitor.accept(arrayExpression);
|
||||
visitor.accept(indexExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
arrayExpression = v.accept(arrayExpression);
|
||||
indexExpression = v.accept(indexExpression);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,12 @@ public final class JsArrayLiteral extends JsLiteral {
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
visitor.acceptWithInsertRemove(expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptList(expressions);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,4 +48,17 @@ public final class JsBinaryOperation extends JsExpressionImpl {
|
||||
}
|
||||
visitor.accept(arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (op.isAssignment()) {
|
||||
arg1 = v.acceptLvalue(arg1);
|
||||
} else {
|
||||
arg1 = v.accept(arg1);
|
||||
}
|
||||
arg2 = v.accept(arg2);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ package com.google.dart.compiler.backend.js.ast;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -56,4 +54,12 @@ public class JsBlock extends SourceInfoAwareJsNode implements JsStatement {
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
visitor.acceptWithInsertRemove(statements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptStatementList(statements);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,10 @@ public final class JsBreak extends JsContinue {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitBreak(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,13 @@ public final class JsCase extends JsSwitchMember {
|
||||
visitor.accept(caseExpression);
|
||||
super.acceptChildren(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
caseExpression = v.accept(caseExpression);
|
||||
v.acceptStatementList(statements);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,4 +60,16 @@ public class JsCatch extends SourceInfoAwareJsNode implements HasCondition {
|
||||
}
|
||||
visitor.accept(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
param = v.accept(param);
|
||||
if (condition != null) {
|
||||
condition = v.accept(condition);
|
||||
}
|
||||
body = v.accept(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +53,14 @@ public final class JsConditional extends JsExpressionImpl {
|
||||
visitor.accept(thenExpression);
|
||||
visitor.accept(elseExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
testExpression = v.accept(testExpression);
|
||||
thenExpression = v.accept(thenExpression);
|
||||
elseExpression = v.accept(elseExpression);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package com.google.dart.compiler.backend.js.ast;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The context in which a JsNode visitation occurs. This represents the set of
|
||||
* possible operations a JsVisitor subclass can perform on the currently visited
|
||||
@@ -23,4 +25,7 @@ public interface JsContext {
|
||||
void removeMe();
|
||||
|
||||
void replaceMe(JsNode node);
|
||||
|
||||
@Nullable
|
||||
JsNode getCurrentNode();
|
||||
}
|
||||
|
||||
@@ -26,4 +26,10 @@ public class JsContinue extends SourceInfoAwareJsNode implements JsStatement {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitContinue(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,10 @@ public class JsDebugger extends SourceInfoAwareJsNode implements JsStatement {
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,4 +12,12 @@ public final class JsDefault extends JsSwitchMember {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitDefault(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptStatementList(statements);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,13 @@ public class JsDoWhile extends JsWhile {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitDoWhile(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
body = v.acceptStatement(body);
|
||||
condition = v.accept(condition);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,4 +26,8 @@ public class JsDocComment extends JsExpressionImpl {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitDocComment(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,4 +12,10 @@ public class JsEmpty extends SourceInfoAwareJsNode implements JsStatement {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitEmpty(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,4 +33,9 @@ public class JsEmptyExpression extends JsExpressionImpl {
|
||||
public void accept(JsVisitor visitor) {
|
||||
throw new IllegalArgumentException("empty expression should not be here during generating Javascript code");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext visitor, JsContext ctx) {
|
||||
throw new IllegalArgumentException("empty expression should not be here during generating Javascript code");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,4 +39,12 @@ public final class JsExpressionStatement extends AbstractNode implements JsState
|
||||
public JsNode source(Object info) {
|
||||
throw new IllegalStateException("You must not set source info for JsExpressionStatement, set for expression");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
expression = v.accept(expression);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,4 +95,27 @@ public class JsFor extends SourceInfoAwareJsNode implements JsStatement {
|
||||
}
|
||||
visitor.accept(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
assert (!(initExpression != null && initVars != null));
|
||||
|
||||
if (initExpression != null) {
|
||||
initExpression = v.accept(initExpression);
|
||||
} else if (initVars != null) {
|
||||
initVars = v.accept(initVars);
|
||||
}
|
||||
|
||||
if (condition != null) {
|
||||
condition = v.accept(condition);
|
||||
}
|
||||
|
||||
if (incrementExpression != null) {
|
||||
incrementExpression = v.accept(incrementExpression);
|
||||
}
|
||||
body = v.acceptStatement(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,4 +61,16 @@ public class JsForIn extends SourceInfoAwareJsNode implements JsStatement {
|
||||
visitor.accept(objectExpression);
|
||||
visitor.accept(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (iterExpression != null) {
|
||||
iterExpression = v.acceptLvalue(iterExpression);
|
||||
}
|
||||
objectExpression = v.accept(objectExpression);
|
||||
body = v.acceptStatement(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,16 @@ public final class JsFunction extends JsLiteral implements HasName {
|
||||
|
||||
@Override
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
visitor.acceptWithInsertRemove(params);
|
||||
visitor.acceptWithInsertRemove(getParameters());
|
||||
visitor.accept(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptList(getParameters());
|
||||
body = v.accept(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,4 +63,16 @@ public final class JsIf extends SourceInfoAwareJsNode implements JsStatement {
|
||||
visitor.accept(elseStatement);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
ifExpression = v.accept(ifExpression);
|
||||
thenStatement = v.acceptStatement(thenStatement);
|
||||
if (elseStatement != null) {
|
||||
elseStatement = v.acceptStatement(elseStatement);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class JsInvocation extends JsExpressionImpl.JsExpressionHasArguments {
|
||||
@@ -60,4 +59,13 @@ public final class JsInvocation extends JsExpressionImpl.JsExpressionHasArgument
|
||||
visitor.accept(qualifier);
|
||||
visitor.acceptList(arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
qualifier = v.accept(qualifier);
|
||||
v.acceptList(arguments);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,4 +50,12 @@ public class JsLabel extends SourceInfoAwareJsNode implements JsStatement, HasNa
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
visitor.accept(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
statement = v.acceptStatement(statement);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,24 +26,36 @@ public abstract class JsLiteral extends JsExpressionImpl {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitThis(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class JsBooleanLiteral extends JsValueLiteral {
|
||||
private final boolean value;
|
||||
private final boolean value;
|
||||
|
||||
// Should be interned by JsProgram
|
||||
private JsBooleanLiteral(boolean value) {
|
||||
// Should be interned by JsProgram
|
||||
private JsBooleanLiteral(boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean getValue() {
|
||||
public boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(JsVisitor v) {
|
||||
@Override
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitBoolean(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,4 +79,14 @@ public final class JsNameRef extends JsExpressionImpl implements HasName {
|
||||
visitor.accept(qualifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (qualifier != null) {
|
||||
qualifier = v.accept(qualifier);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,4 +34,13 @@ public final class JsNew extends JsExpressionImpl.JsExpressionHasArguments {
|
||||
visitor.accept(constructorExpression);
|
||||
visitor.acceptList(arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
constructorExpression = v.accept(constructorExpression);
|
||||
v.acceptList(arguments);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,12 @@ public interface JsNode {
|
||||
void setSource(Object info);
|
||||
|
||||
JsNode source(Object info);
|
||||
|
||||
/**
|
||||
* Causes this object to have the visitor visit itself and its children.
|
||||
*
|
||||
* @param visitor the visitor that should traverse this node
|
||||
* @param ctx the context of an existing traversal
|
||||
*/
|
||||
void traverse(JsVisitorWithContext visitor, JsContext ctx);
|
||||
}
|
||||
|
||||
@@ -12,4 +12,10 @@ public final class JsNullLiteral extends JsLiteral.JsValueLiteral {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitNull(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ public abstract class JsNumberLiteral extends JsLiteral.JsValueLiteral {
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class JsIntLiteral extends JsNumberLiteral {
|
||||
@@ -39,5 +45,11 @@ public abstract class JsNumberLiteral extends JsLiteral.JsValueLiteral {
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,4 +47,12 @@ public final class JsObjectLiteral extends JsLiteral {
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
visitor.acceptWithInsertRemove(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptList(properties);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,4 +34,10 @@ public final class JsParameter extends SourceInfoAwareJsNode implements HasName
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitParameter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,12 @@ public final class JsPostfixOperation extends JsUnaryOperation {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitPostfixOperation(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
super.traverse(v, ctx);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,12 @@ public final class JsPrefixOperation extends JsUnaryOperation {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitPrefixOperation(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
super.traverse(v, ctx);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,4 +129,14 @@ public final class JsProgram extends SourceInfoAwareJsNode {
|
||||
visitor.accept(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
for (JsProgramFragment fragment : fragments) {
|
||||
v.accept(fragment);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,12 @@ public class JsProgramFragment extends SourceInfoAwareJsNode {
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
visitor.accept(globalBlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.accept(globalBlock);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,4 +44,13 @@ public class JsPropertyInitializer extends SourceInfoAwareJsNode {
|
||||
visitor.accept(labelExpr);
|
||||
visitor.accept(valueExpr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
labelExpr = v.accept(labelExpr);
|
||||
valueExpr = v.accept(valueExpr);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,10 @@ public final class JsRegExp extends JsLiteral.JsValueLiteral {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitRegExp(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,14 @@ public final class JsReturn extends SourceInfoAwareJsNode implements JsStatement
|
||||
visitor.accept(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (expression != null) {
|
||||
expression = v.accept(expression);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ package com.google.dart.compiler.backend.js.ast;
|
||||
|
||||
public final class JsStringLiteral extends JsLiteral.JsValueLiteral {
|
||||
|
||||
private final String value;
|
||||
private final String value;
|
||||
|
||||
// These only get created by JsProgram so that they can be interned.
|
||||
JsStringLiteral(String value) {
|
||||
// These only get created by JsProgram so that they can be interned.
|
||||
JsStringLiteral(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -21,4 +21,10 @@ public final class JsStringLiteral extends JsLiteral.JsValueLiteral {
|
||||
public void accept(JsVisitor v) {
|
||||
v.visitString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,13 @@ public class JsSwitch extends SourceInfoAwareJsNode implements JsStatement {
|
||||
visitor.accept(expression);
|
||||
visitor.acceptWithInsertRemove(cases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
expression = v.accept(expression);
|
||||
v.acceptList(cases);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,12 @@ public class JsThrow extends SourceInfoAwareJsNode implements JsStatement {
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
visitor.accept(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
expression = v.accept(expression);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,4 +60,16 @@ public class JsTry extends SourceInfoAwareJsNode implements JsStatement {
|
||||
visitor.accept(finallyBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
tryBlock = v.accept(tryBlock);
|
||||
v.acceptList(catches);
|
||||
if (finallyBlock != null) {
|
||||
finallyBlock = v.accept(finallyBlock);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,4 +43,17 @@ public abstract class JsUnaryOperation extends JsExpressionImpl {
|
||||
visitor.accept(arg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (op.isModifying()) {
|
||||
/*
|
||||
* The delete operator is practically like an assignment of undefined, so for practical
|
||||
* purposes we're treating it as an lvalue.
|
||||
*/
|
||||
arg = v.acceptLvalue(arg);
|
||||
} else {
|
||||
arg = v.accept(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,16 @@ public class JsVars extends SourceInfoAwareJsNode implements JsStatement, Iterab
|
||||
visitor.accept(initExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (initExpression != null) {
|
||||
initExpression = v.accept(initExpression);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(JsVar var) {
|
||||
@@ -126,4 +136,12 @@ public class JsVars extends SourceInfoAwareJsNode implements JsStatement, Iterab
|
||||
public void acceptChildren(JsVisitor visitor) {
|
||||
visitor.acceptWithInsertRemove(vars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptList(vars);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* 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 com.google.dart.compiler.backend.js.ast;
|
||||
|
||||
/**
|
||||
* Taken from GWT project with modifications.
|
||||
* Original:
|
||||
* repository: https://gwt.googlesource.com/gwt
|
||||
* revision: e32bf0a95029165d9e6ab457c7ee7ca8b07b908c
|
||||
* file: dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implemented by nodes that will visit child nodes.
|
||||
*/
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public abstract class JsVisitorWithContext {
|
||||
|
||||
public final <T extends JsNode> T accept(T node) {
|
||||
return doAccept(node);
|
||||
}
|
||||
|
||||
public JsExpression acceptLvalue(JsExpression expr) {
|
||||
return doAcceptLvalue(expr);
|
||||
}
|
||||
|
||||
public final <T extends JsNode> void acceptList(List<T> collection) {
|
||||
doAcceptList(collection);
|
||||
}
|
||||
|
||||
public final <T extends JsStatement> T acceptStatement(T statement) {
|
||||
//noinspection unchecked
|
||||
return (T) doAcceptStatement(statement);
|
||||
}
|
||||
|
||||
public final <T extends JsStatement> void acceptStatementList(List<T> statements) {
|
||||
doAcceptStatementList(statements);
|
||||
}
|
||||
|
||||
public void endVisit(JsArrayAccess x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsArrayLiteral x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsBinaryOperation x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsBlock x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsLiteral.JsBooleanLiteral x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsBreak x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsCase x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsCatch x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsConditional x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsContinue x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsDebugger x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsDefault x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsDoWhile x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsEmpty x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsExpressionStatement x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsFor x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsForIn x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsFunction x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsIf x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsInvocation x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsLabel x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsName x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsNameRef x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsNew x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsNullLiteral x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsNumberLiteral x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsObjectLiteral x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsParameter x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsPostfixOperation x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsPrefixOperation x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsProgram x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsProgramFragment x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsPropertyInitializer x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsRegExp x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsReturn x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsStringLiteral x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsSwitch x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsLiteral.JsThisRef x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsThrow x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsTry x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsVars.JsVar x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsVars x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public void endVisit(JsWhile x, JsContext ctx) {
|
||||
}
|
||||
|
||||
public boolean visit(JsArrayAccess x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsArrayLiteral x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsBinaryOperation x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsBlock x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsLiteral.JsBooleanLiteral x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsBreak x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsCase x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsCatch x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsConditional x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsContinue x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsDebugger x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsDefault x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsDoWhile x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsEmpty x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsExpressionStatement x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsFor x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsForIn x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsFunction x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsIf x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsInvocation x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsLabel x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsName x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsNameRef x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsNew x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsNullLiteral x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsNumberLiteral x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsObjectLiteral x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsParameter x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsPostfixOperation x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsPrefixOperation x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsProgram x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsProgramFragment x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsPropertyInitializer x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsRegExp x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsReturn x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsStringLiteral x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsSwitch x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsLiteral.JsThisRef x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsThrow x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsTry x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsVars.JsVar x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsVars x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean visit(JsWhile x, JsContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract <T extends JsNode> T doAccept(T node);
|
||||
|
||||
protected abstract JsExpression doAcceptLvalue(JsExpression expr);
|
||||
|
||||
protected abstract <T extends JsNode> void doAcceptList(List<T> collection);
|
||||
|
||||
protected abstract <T extends JsStatement> JsStatement doAcceptStatement(T statement);
|
||||
|
||||
protected abstract <T extends JsStatement> void doAcceptStatementList(List<T> statements);
|
||||
|
||||
protected abstract <T extends JsNode> void doTraverse(T node, JsContext ctx) ;
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* 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 com.google.dart.compiler.backend.js.ast;
|
||||
|
||||
/**
|
||||
* Taken from GWT project with modifications.
|
||||
* Original:
|
||||
* repository: https://gwt.googlesource.com/gwt
|
||||
* revision: e32bf0a95029165d9e6ab457c7ee7ca8b07b908c
|
||||
* file: dev/core/src/com/google/gwt/dev/js/ast/JsModVisitor.java
|
||||
*/
|
||||
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* A visitor for iterating through and modifying an AST.
|
||||
*/
|
||||
public class JsVisitorWithContextImpl extends JsVisitorWithContext {
|
||||
|
||||
private final Stack<JsContext> statementContexts = new Stack<JsContext>();
|
||||
|
||||
public class ListContext<T extends JsNode> implements JsContext {
|
||||
private List<T> collection;
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public boolean canInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAfter(JsNode node) {
|
||||
//noinspection unchecked
|
||||
collection.add(index + 1, (T) node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertBefore(JsNode node) {
|
||||
//noinspection unchecked
|
||||
collection.add(index++, (T) node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLvalue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMe() {
|
||||
collection.remove(index--);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceMe(JsNode node) {
|
||||
checkReplacement(collection.get(index), node);
|
||||
//noinspection unchecked
|
||||
collection.set(index, (T) node);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JsNode getCurrentNode() {
|
||||
if (index < collection.size()) {
|
||||
return collection.get(index);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void traverse(List<T> collection) {
|
||||
this.collection = collection;
|
||||
for (index = 0; index < collection.size(); ++index) {
|
||||
T node = collection.get(index);
|
||||
doTraverse(node, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LvalueContext extends NodeContext<JsExpression> {
|
||||
@Override
|
||||
public boolean isLvalue() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private class NodeContext<T extends JsNode> implements JsContext {
|
||||
protected T node;
|
||||
|
||||
@Override
|
||||
public boolean canInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRemove() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAfter(JsNode node) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertBefore(JsNode node) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLvalue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMe() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceMe(JsNode node) {
|
||||
checkReplacement(this.node, node);
|
||||
this.node = (T) node;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JsNode getCurrentNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
protected T traverse(T node) {
|
||||
this.node = node;
|
||||
doTraverse(node, this);
|
||||
return this.node;
|
||||
}
|
||||
}
|
||||
|
||||
protected static void checkReplacement(@SuppressWarnings("UnusedParameters") JsNode origNode, JsNode newNode) {
|
||||
if (newNode == null) throw new RuntimeException("Cannot replace with null");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends JsNode> T doAccept(T node) {
|
||||
return new NodeContext<T>().traverse(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsExpression doAcceptLvalue(JsExpression expr) {
|
||||
return new LvalueContext().traverse(expr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends JsStatement> JsStatement doAcceptStatement(T statement) {
|
||||
List<JsStatement> statements = new SmartList<JsStatement>(statement);
|
||||
doAcceptStatementList(statements);
|
||||
|
||||
if (statements.size() == 1) {
|
||||
return statements.get(0);
|
||||
}
|
||||
|
||||
return new JsBlock(statements);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends JsStatement> void doAcceptStatementList(List<T> statements) {
|
||||
ListContext<T> context = new ListContext<T>();
|
||||
statementContexts.push(context);
|
||||
context.traverse(statements);
|
||||
statementContexts.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends JsNode> void doAcceptList(List<T> collection) {
|
||||
new ListContext<T>().traverse(collection);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected JsContext getLastStatementLevelContext() {
|
||||
return statementContexts.peek();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends JsNode> void doTraverse(T node, JsContext ctx) {
|
||||
node.traverse(this, ctx);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,4 +45,13 @@ public class JsWhile extends SourceInfoAwareJsNode implements JsStatement {
|
||||
visitor.accept(condition);
|
||||
visitor.accept(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitorWithContext v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
condition = v.accept(condition);
|
||||
body = v.acceptStatement(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user