use of com.google.javascript.rhino.Token in project closure-compiler by google.
the class PeepholeFoldConstants method tryFoldLeftChildOp.
/**
* Expressions such as [foo() * 10 * 20] generate parse trees where no node has two const children
* ((foo() * 10) * 20), so performArithmeticOp() won't fold it: tryFoldLeftChildOp() will.
*
* <p>Specifically, this folds associative expressions where:
*
* <ul>
* <li>The left child is also an associative expression of the same type.
* <li>The right child is a BIGINT or NUMBER constant.
* <li>The left child's right child is a BIGINT or NUMBER constant.
*/
private Node tryFoldLeftChildOp(Node n, Node left, Node right) {
Token opType = n.getToken();
checkState((NodeUtil.isAssociative(opType) && NodeUtil.isCommutative(opType)) || n.isAdd());
checkState(!n.isAdd() || !NodeUtil.mayBeString(n, shouldUseTypes));
// Use getNumberValue to handle constants like "NaN" and "Infinity"
// other values are converted to numbers elsewhere.
Double rightValObj = getSideEffectFreeNumberValue(right);
BigInteger rightBigInt = getSideEffectFreeBigIntValue(right);
if ((rightValObj != null || rightBigInt != null) && left.getToken() == opType) {
checkState(left.hasTwoChildren());
Node ll = left.getFirstChild();
Node lr = ll.getNext();
Node valueToCombine = ll;
Node replacement = performArithmeticOp(n, valueToCombine, right);
if (replacement == null) {
valueToCombine = lr;
replacement = performArithmeticOp(n, valueToCombine, right);
}
if (replacement != null) {
// Remove the child that has been combined
valueToCombine.detach();
// Replace the left op with the remaining child.
left.replaceWith(left.removeFirstChild());
// New "-Infinity" node need location info explicitly
// added.
replacement.srcrefTreeIfMissing(right);
right.replaceWith(replacement);
reportChangeToEnclosingScope(n);
}
}
return n;
}
use of com.google.javascript.rhino.Token in project closure-compiler by google.
the class CodeGenerator method add.
protected void add(Node node, Context context) {
if (!cc.continueProcessing()) {
return;
}
if (preserveTypeAnnotations && node.getJSDocInfo() != null) {
String jsdocAsString = jsDocInfoPrinter.print(node.getJSDocInfo());
// Don't print an empty jsdoc
if (!jsdocAsString.equals("/** */ ")) {
add(jsdocAsString);
if (!node.isCast()) {
cc.endLine();
}
}
}
// print any non-trailing non-JSDoc comment attached to this node
if (printNonJSDocComments) {
NonJSDocComment nonJSDocComment = node.getNonJSDocComment();
if (nonJSDocComment != null && !nonJSDocComment.isTrailing()) {
String nonJSDocCommentString = node.getNonJSDocCommentString();
if (!nonJSDocCommentString.isEmpty()) {
addNonJsDoc_nonTrailing(node, nonJSDocComment);
}
}
}
Token type = node.getToken();
String opstr = NodeUtil.opToStr(type);
int childCount = node.getChildCount();
Node first = node.getFirstChild();
Node last = node.getLastChild();
// Handle all binary operators
if (opstr != null && first != last) {
Preconditions.checkState(childCount == 2, "Bad binary operator \"%s\": expected 2 arguments but got %s", opstr, childCount);
int p = precedence(node);
// For right-hand-side of operations, only pass context if it's
// the IN_FOR_INIT_CLAUSE one.
Context rhsContext = getContextForNoInOperator(context);
boolean needsParens = (context == Context.START_OF_EXPR || context.atArrowFunctionBody()) && first.isObjectPattern();
if (node.isAssign() && needsParens) {
add("(");
}
if (NodeUtil.isAssignmentOp(node) || type == Token.EXPONENT) {
// Assignment operators and '**' are the only right-associative binary operators
addExpr(first, p + 1, context);
cc.addOp(opstr, true);
addExpr(last, p, rhsContext);
} else {
unrollBinaryOperator(node, type, opstr, context, rhsContext, p, p + 1);
}
if (node.isAssign() && needsParens) {
add(")");
}
return;
}
cc.startSourceMapping(node);
switch(type) {
case TRY:
{
checkState(first.getNext().isBlock() && !first.getNext().hasMoreThanOneChild());
checkState(childCount >= 2 && childCount <= 3);
add("try");
add(first);
// second child contains the catch block, or nothing if there
// isn't a catch block
Node catchblock = first.getNext().getFirstChild();
if (catchblock != null) {
add(catchblock);
}
if (childCount == 3) {
cc.maybeInsertSpace();
add("finally");
add(last);
}
break;
}
case CATCH:
Preconditions.checkState(childCount == 2, node);
cc.maybeInsertSpace();
add("catch");
cc.maybeInsertSpace();
if (!first.isEmpty()) {
// optional catch binding
add("(");
add(first);
add(")");
}
add(last);
break;
case THROW:
Preconditions.checkState(childCount == 1, node);
add("throw");
cc.maybeInsertSpace();
add(first);
// Must have a ';' after a throw statement, otherwise safari can't
// parse this.
cc.endStatement(true);
break;
case RETURN:
add("return");
if (childCount == 1) {
cc.maybeInsertSpace();
if (preserveTypeAnnotations && first.getJSDocInfo() != null) {
add("(");
add(first);
add(")");
} else {
add(first);
}
} else {
checkState(childCount == 0, node);
}
cc.endStatement();
break;
case VAR:
add("var ");
addList(first, false, getContextForNoInOperator(context), ",");
if (node.getParent() == null || NodeUtil.isStatement(node)) {
cc.endStatement();
}
break;
case CONST:
add("const ");
addList(first, false, getContextForNoInOperator(context), ",");
if (node.getParent() == null || NodeUtil.isStatement(node)) {
cc.endStatement();
}
break;
case LET:
add("let ");
addList(first, false, getContextForNoInOperator(context), ",");
if (node.getParent() == null || NodeUtil.isStatement(node)) {
cc.endStatement();
}
break;
case LABEL_NAME:
Preconditions.checkState(!node.getString().isEmpty(), node);
addIdentifier(node.getString());
break;
case DESTRUCTURING_LHS:
add(first);
if (first != last) {
checkState(childCount == 2, node);
cc.addOp("=", true);
addExpr(last, NodeUtil.precedence(Token.ASSIGN), getContextForNoInOperator(context));
}
break;
case NAME:
if (useOriginalName && node.getOriginalName() != null) {
addIdentifier(node.getOriginalName());
} else {
addIdentifier(node.getString());
}
maybeAddOptional(node);
maybeAddTypeDecl(node);
if (first != null && !first.isEmpty()) {
checkState(childCount == 1, node);
cc.addOp("=", true);
addExpr(first, NodeUtil.precedence(Token.ASSIGN), getContextForNoInOperator(context));
}
break;
case ARRAYLIT:
add("[");
addArrayList(first);
add("]");
break;
case ARRAY_PATTERN:
add("[");
addArrayList(first);
add("]");
maybeAddTypeDecl(node);
break;
case PARAM_LIST:
// If this is the list for a non-TypeScript arrow function with one simple name param.
if (node.getParent().isArrowFunction() && node.hasOneChild() && first.isName() && !outputFeatureSet.has(Feature.TYPE_ANNOTATION)) {
add(first);
} else {
add("(");
addList(first);
add(")");
}
break;
case DEFAULT_VALUE:
add(first);
maybeAddTypeDecl(node);
cc.addOp("=", true);
addExpr(first.getNext(), 1, Context.OTHER);
break;
case COMMA:
Preconditions.checkState(childCount == 2, node);
unrollBinaryOperator(node, Token.COMMA, ",", context, getContextForNoInOperator(context), 0, 0);
break;
case NUMBER:
Preconditions.checkState(childCount == 0, node);
cc.addNumber(node.getDouble(), node);
break;
case BIGINT:
Preconditions.checkState(childCount == 0, node);
cc.add(node.getBigInt() + "n");
break;
case TYPEOF:
case VOID:
case NOT:
case BITNOT:
case POS:
case NEG:
{
// All of these unary operators are right-associative
checkState(childCount == 1, node);
cc.addOp(NodeUtil.opToStrNoFail(type), false);
addExpr(first, NodeUtil.precedence(type), Context.OTHER);
break;
}
case HOOK:
{
checkState(childCount == 3, "%s wrong number of children: %s", node, childCount);
int p = NodeUtil.precedence(type);
Context rhsContext = getContextForNoInOperator(context);
addExpr(first, p + 1, context);
cc.addOp("?", true);
addExpr(first.getNext(), 1, rhsContext);
cc.addOp(":", true);
addExpr(last, 1, rhsContext);
break;
}
case REGEXP:
if (!first.isStringLit() || !last.isStringLit()) {
throw new Error("Expected children to be strings");
}
String regexp = regexpEscape(first.getString());
// I only use one .add because whitespace matters
if (childCount == 2) {
add(regexp + last.getString());
} else {
checkState(childCount == 1, node);
add(regexp);
}
break;
case FUNCTION:
{
if (node.getClass() != Node.class) {
throw new Error("Unexpected Node subclass.");
}
checkState(childCount == 3, node);
if (node.isArrowFunction()) {
addArrowFunction(node, first, last, context);
} else {
addFunction(node, first, last, context);
}
break;
}
case ITER_REST:
case OBJECT_REST:
add("...");
add(first);
maybeAddTypeDecl(node);
break;
case ITER_SPREAD:
case OBJECT_SPREAD:
add("...");
addExpr(first, NodeUtil.precedence(type), Context.OTHER);
break;
case EXPORT:
add("export");
if (node.getBooleanProp(Node.EXPORT_DEFAULT)) {
add("default");
}
if (node.getBooleanProp(Node.EXPORT_ALL_FROM)) {
add("*");
checkState(first != null && first.isEmpty(), node);
} else {
add(first);
}
if (childCount == 2) {
add("from");
add(last);
}
processEnd(first, context);
break;
case IMPORT:
add("import");
Node second = first.getNext();
if (!first.isEmpty()) {
add(first);
if (!second.isEmpty()) {
cc.listSeparator();
}
}
if (!second.isEmpty()) {
add(second);
}
if (!first.isEmpty() || !second.isEmpty()) {
add("from");
}
add(last);
cc.endStatement();
break;
case EXPORT_SPECS:
case IMPORT_SPECS:
add("{");
for (Node c = first; c != null; c = c.getNext()) {
if (c != first) {
cc.listSeparator();
}
add(c);
}
add("}");
break;
case EXPORT_SPEC:
case IMPORT_SPEC:
add(first);
if (node.isShorthandProperty() && first.getString().equals(last.getString())) {
break;
}
add("as");
add(last);
break;
case IMPORT_STAR:
add("*");
add("as");
add(node.getString());
break;
case DYNAMIC_IMPORT:
add("import(");
addExpr(first, NodeUtil.precedence(type), context);
add(")");
break;
case IMPORT_META:
add("import.meta");
break;
// CLASS -> NAME,EXPR|EMPTY,BLOCK
case CLASS:
{
checkState(childCount == 3, node);
boolean classNeedsParens = (context == Context.START_OF_EXPR);
if (classNeedsParens) {
add("(");
}
Node name = first;
Node superClass = first.getNext();
Node members = last;
add("class");
if (!name.isEmpty()) {
add(name);
}
maybeAddGenericTypes(first);
if (!superClass.isEmpty()) {
add("extends");
add(superClass);
}
Node interfaces = (Node) node.getProp(Node.IMPLEMENTS);
if (interfaces != null) {
add("implements");
Node child = interfaces.getFirstChild();
add(child);
while ((child = child.getNext()) != null) {
add(",");
cc.maybeInsertSpace();
add(child);
}
}
add(members);
cc.endClass(context == Context.STATEMENT);
if (classNeedsParens) {
add(")");
}
}
break;
case CLASS_MEMBERS:
case INTERFACE_MEMBERS:
case NAMESPACE_ELEMENTS:
cc.beginBlock();
for (Node c = first; c != null; c = c.getNext()) {
add(c);
processEnd(c, context);
cc.endLine();
}
cc.endBlock(false);
break;
case ENUM_MEMBERS:
cc.beginBlock();
for (Node c = first; c != null; c = c.getNext()) {
add(c);
if (c.getNext() != null) {
add(",");
}
cc.endLine();
}
cc.endBlock(false);
break;
case GETTER_DEF:
case SETTER_DEF:
case MEMBER_FUNCTION_DEF:
case MEMBER_VARIABLE_DEF:
{
checkState(node.getParent().isObjectLit() || node.getParent().isClassMembers() || node.getParent().isInterfaceMembers() || node.getParent().isRecordType() || node.getParent().isIndexSignature());
maybeAddAccessibilityModifier(node);
if (node.isStaticMember()) {
add("static ");
}
if (node.isMemberFunctionDef() && node.getFirstChild().isAsyncFunction()) {
add("async ");
}
if (!node.isMemberVariableDef() && node.getFirstChild().isGeneratorFunction()) {
checkState(type == Token.MEMBER_FUNCTION_DEF, node);
add("*");
}
switch(type) {
case GETTER_DEF:
// Get methods have no parameters.
Preconditions.checkState(!first.getSecondChild().hasChildren(), node);
add("get ");
break;
case SETTER_DEF:
// Set methods have one parameter.
Preconditions.checkState(first.getSecondChild().hasOneChild(), node);
add("set ");
break;
case MEMBER_FUNCTION_DEF:
case MEMBER_VARIABLE_DEF:
// nothing to do.
break;
default:
break;
}
// The name is on the GET or SET node.
String name = node.getString();
if (node.isMemberVariableDef()) {
add(node.getString());
maybeAddOptional(node);
maybeAddTypeDecl(node);
} else {
checkState(childCount == 1, node);
checkState(first.isFunction(), first);
// The function referenced by the definition should always be unnamed.
checkState(first.getFirstChild().getString().isEmpty(), first);
Node fn = first;
Node parameters = fn.getSecondChild();
Node body = fn.getLastChild();
// Add the property name.
if (!node.isQuotedString() && TokenStream.isJSIdentifier(name) && // Unicode escaped.
NodeUtil.isLatin(name)) {
add(name);
maybeAddGenericTypes(fn.getFirstChild());
} else {
// Determine if the string is a simple number.
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d, node);
} else {
addJsString(node);
}
}
maybeAddOptional(fn);
add(parameters);
maybeAddTypeDecl(fn);
add(body);
}
break;
}
case MEMBER_FIELD_DEF:
case COMPUTED_FIELD_DEF:
{
checkState(node.getParent().isClassMembers());
if (node.getBooleanProp(Node.STATIC_MEMBER)) {
add("static ");
}
Node init = null;
switch(type) {
case MEMBER_FIELD_DEF:
String propertyName = node.getString();
add(propertyName);
init = first;
break;
case COMPUTED_FIELD_DEF:
add("[");
// Must use addExpr() with a priority of 1, because comma expressions aren't allowed.
// https://www.ecma-international.org/ecma-262/9.0/index.html#prod-ComputedPropertyName
addExpr(first, 1, Context.OTHER);
add("]");
init = node.getSecondChild();
break;
default:
break;
}
if (init != null) {
add("=");
addExpr(init, 1, Context.OTHER);
}
add(";");
break;
}
case SCRIPT:
case MODULE_BODY:
case BLOCK:
case ROOT:
{
if (node.getClass() != Node.class) {
throw new Error("Unexpected Node subclass.");
}
boolean preserveBlock = node.isBlock() && !node.isSyntheticBlock();
if (preserveBlock) {
cc.beginBlock();
}
boolean preferLineBreaks = type == Token.SCRIPT || (type == Token.BLOCK && !preserveBlock && node.getParent().isScript());
for (Node c = first; c != null; c = c.getNext()) {
add(c, Context.STATEMENT);
if (c.isFunction() || c.isClass()) {
cc.maybeLineBreak();
}
// because top-level statements are more homogeneous.
if (preferLineBreaks) {
cc.notePreferredLineBreak();
}
}
if (preserveBlock) {
cc.endBlock(cc.breakAfterBlockFor(node, context == Context.STATEMENT));
}
break;
}
case FOR:
Preconditions.checkState(childCount == 4, node);
add("for");
cc.maybeInsertSpace();
add("(");
if (NodeUtil.isNameDeclaration(first)) {
add(first, Context.IN_FOR_INIT_CLAUSE);
} else {
addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE);
}
add(";");
if (!first.getNext().isEmpty()) {
cc.maybeInsertSpace();
}
add(first.getNext());
add(";");
if (!first.getNext().getNext().isEmpty()) {
cc.maybeInsertSpace();
}
add(first.getNext().getNext());
add(")");
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
break;
case FOR_IN:
Preconditions.checkState(childCount == 3, node);
add("for");
cc.maybeInsertSpace();
add("(");
add(first);
add("in");
add(first.getNext());
add(")");
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
break;
case FOR_OF:
Preconditions.checkState(childCount == 3, node);
add("for");
cc.maybeInsertSpace();
add("(");
add(first);
cc.maybeInsertSpace();
add("of");
cc.maybeInsertSpace();
// the iterable must be an AssignmentExpression
addExpr(first.getNext(), NodeUtil.precedence(Token.ASSIGN), Context.OTHER);
add(")");
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
break;
case FOR_AWAIT_OF:
Preconditions.checkState(childCount == 3, node);
add("for await");
cc.maybeInsertSpace();
add("(");
add(first);
cc.maybeInsertSpace();
add("of");
cc.maybeInsertSpace();
// the iterable must be an AssignmentExpression
addExpr(first.getNext(), NodeUtil.precedence(Token.ASSIGN), Context.OTHER);
add(")");
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
break;
case DO:
Preconditions.checkState(childCount == 2, node);
add("do");
addNonEmptyStatement(first, Context.OTHER, false);
cc.maybeInsertSpace();
add("while");
cc.maybeInsertSpace();
add("(");
add(last);
add(")");
cc.endStatement();
break;
case WHILE:
Preconditions.checkState(childCount == 2, node);
add("while");
cc.maybeInsertSpace();
add("(");
add(first);
add(")");
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
break;
case EMPTY:
Preconditions.checkState(childCount == 0, node);
break;
case OPTCHAIN_GETPROP:
{
addExpr(first, NodeUtil.precedence(type), context);
add(node.isOptionalChainStart() ? "?." : ".");
addGetpropIdentifier(node);
break;
}
case GETPROP:
{
// qualified names.
if (useOriginalName && node.getOriginalName() != null) {
// rewrite it back to the original code.
if (node.getFirstChild().matchesQualifiedName("$jscomp.scope") && node.getParent().isAssign()) {
add("var ");
}
addGetpropIdentifier(node);
break;
}
// We need parentheses to distinguish
// `a?.b.c` from `(a?.b).c`
boolean breakOutOfOptionalChain = NodeUtil.isOptChainNode(first);
// `2.toString()` is invalid - it must be `(2).toString()`
boolean needsParens = first.isNumber() || breakOutOfOptionalChain;
if (needsParens) {
add("(");
}
addExpr(first, NodeUtil.precedence(type), context);
if (needsParens) {
add(")");
}
if (quoteKeywordProperties && TokenStream.isKeyword(node.getString())) {
// NOTE: We don't have to worry about quoting keyword properties in the
// OPTCHAIN_GETPROP case above, because we only need to quote keywords for
// ES3-compatible output.
//
// Must be a single call to `add` otherwise the generator will add a trailing space.
add("[\"" + node.getString() + "\"]");
} else {
add(".");
addGetpropIdentifier(node);
}
break;
}
case OPTCHAIN_GETELEM:
{
checkState(childCount == 2, "Bad GETELEM node: Expected 2 children but got %s. For node: %s", childCount, node);
addExpr(first, NodeUtil.precedence(type), context);
if (node.isOptionalChainStart()) {
add("?.");
}
add("[");
add(first.getNext());
add("]");
break;
}
case GETELEM:
{
checkState(childCount == 2, "Bad GETELEM node: Expected 2 children but got %s. For node: %s", childCount, node);
boolean needsParens = NodeUtil.isOptChainNode(first);
if (needsParens) {
add("(");
}
addExpr(first, NodeUtil.precedence(type), context);
if (needsParens) {
add(")");
}
add("[");
add(first.getNext());
add("]");
break;
}
case WITH:
Preconditions.checkState(childCount == 2, node);
add("with(");
add(first);
add(")");
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
break;
case INC:
case DEC:
{
checkState(childCount == 1, node);
String o = type == Token.INC ? "++" : "--";
boolean postProp = node.getBooleanProp(Node.INCRDECR_PROP);
if (postProp) {
addExpr(first, NodeUtil.precedence(type), context);
cc.addOp(o, false);
} else {
cc.addOp(o, false);
add(first);
}
break;
}
case OPTCHAIN_CALL:
{
// that must be preserved.
if (isIndirectEval(first) || (node.getBooleanProp(Node.FREE_CALL) && NodeUtil.isNormalOrOptChainGet(first))) {
add("(0,");
addExpr(first, NodeUtil.precedence(Token.COMMA), Context.OTHER);
add(")");
} else {
addExpr(first, NodeUtil.precedence(type), context);
}
Node args = first.getNext();
if (node.isOptionalChainStart()) {
add("?.");
}
add("(");
addList(args);
add(")");
break;
}
case CALL:
this.addInvocationTarget(node, context);
add("(");
addList(first.getNext());
add(")");
break;
case IF:
Preconditions.checkState(childCount == 2 || childCount == 3, node);
boolean hasElse = childCount == 3;
boolean ambiguousElseClause = context == Context.BEFORE_DANGLING_ELSE && !hasElse;
if (ambiguousElseClause) {
cc.beginBlock();
}
add("if");
cc.maybeInsertSpace();
add("(");
add(first);
add(")");
if (hasElse) {
addNonEmptyStatement(first.getNext(), Context.BEFORE_DANGLING_ELSE, false);
cc.maybeInsertSpace();
add("else");
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), false);
} else {
addNonEmptyStatement(first.getNext(), Context.OTHER, false);
}
if (ambiguousElseClause) {
cc.endBlock();
}
break;
case NULL:
Preconditions.checkState(childCount == 0, node);
cc.addConstant("null");
break;
case THIS:
Preconditions.checkState(childCount == 0, node);
add("this");
break;
case SUPER:
Preconditions.checkState(childCount == 0, node);
add("super");
break;
case NEW_TARGET:
Preconditions.checkState(childCount == 0, node);
add("new.target");
break;
case YIELD:
add("yield");
if (node.isYieldAll()) {
checkNotNull(first);
add("*");
}
if (first != null) {
cc.maybeInsertSpace();
addExpr(first, NodeUtil.precedence(type), Context.OTHER);
}
break;
case AWAIT:
add("await ");
addExpr(first, NodeUtil.precedence(type), Context.OTHER);
break;
case FALSE:
Preconditions.checkState(childCount == 0, node);
cc.addConstant("false");
break;
case TRUE:
Preconditions.checkState(childCount == 0, node);
cc.addConstant("true");
break;
case CONTINUE:
Preconditions.checkState(childCount <= 1, node);
add("continue");
if (childCount == 1) {
if (!first.isLabelName()) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(" ");
add(first);
}
cc.endStatement();
break;
case DEBUGGER:
Preconditions.checkState(childCount == 0, node);
add("debugger");
cc.endStatement();
break;
case BREAK:
Preconditions.checkState(childCount <= 1, node);
add("break");
if (childCount == 1) {
if (!first.isLabelName()) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(" ");
add(first);
}
cc.endStatement();
break;
case EXPR_RESULT:
Preconditions.checkState(childCount == 1, node);
add(first, Context.START_OF_EXPR);
cc.endStatement();
break;
case NEW:
add("new ");
int precedence = NodeUtil.precedence(type);
// `new void 0` is a syntax error add parenthese in this case. This is only particularly
// interesting for code in dead branches.
int precedenceOfFirst = NodeUtil.precedence(first.getToken());
if (precedenceOfFirst == precedence) {
precedence = precedence + 1;
}
// If the first child is an arrow function, then parentheses is needed
if (NodeUtil.has(first, Node::isCall, NodeUtil.MATCH_NOT_FUNCTION) || NodeUtil.isOptChainNode(first)) {
precedence = NodeUtil.precedence(first.getToken()) + 1;
}
addExpr(first, precedence, Context.OTHER);
// '()' is optional when no arguments are present
Node next = first.getNext();
if (next != null) {
add("(");
addList(next);
add(")");
} else {
if (cc.shouldPreserveExtras(node)) {
add("(");
add(")");
}
}
break;
case STRING_KEY:
addStringKey(node);
break;
case STRINGLIT:
Preconditions.checkState(childCount == 0, "String node %s may not have children", node);
addJsString(node);
break;
case DELPROP:
Preconditions.checkState(childCount == 1, node);
add("delete ");
add(first);
break;
case OBJECTLIT:
{
boolean needsParens = context == Context.START_OF_EXPR || context.atArrowFunctionBody();
if (needsParens) {
add("(");
}
add("{");
for (Node c = first; c != null; c = c.getNext()) {
if (c != first) {
cc.listSeparator();
}
checkState(NodeUtil.isObjLitProperty(c) || c.isSpread(), c);
add(c);
}
if (first != null && node.hasTrailingComma()) {
cc.optionalListSeparator();
}
add("}");
if (needsParens) {
add(")");
}
break;
}
case COMPUTED_PROP:
maybeAddAccessibilityModifier(node);
if (node.getBooleanProp(Node.STATIC_MEMBER)) {
add("static ");
}
if (node.getBooleanProp(Node.COMPUTED_PROP_GETTER)) {
add("get ");
} else if (node.getBooleanProp(Node.COMPUTED_PROP_SETTER)) {
add("set ");
} else if (node.getBooleanProp(Node.COMPUTED_PROP_METHOD)) {
if (last.isAsyncFunction()) {
add("async");
}
if (last.getBooleanProp(Node.GENERATOR_FN)) {
add("*");
}
}
add("[");
// Must use addExpr() with a priority of 1, because comma expressions aren't allowed.
// https://www.ecma-international.org/ecma-262/9.0/index.html#prod-ComputedPropertyName
addExpr(first, 1, Context.OTHER);
add("]");
// TODO(martinprobst): There's currently no syntax for properties in object literals that
// have type declarations on them (a la `{foo: number: 12}`). This comes up for, e.g.,
// function parameters with default values. Support when figured out.
maybeAddTypeDecl(node);
if (node.getBooleanProp(Node.COMPUTED_PROP_METHOD) || node.getBooleanProp(Node.COMPUTED_PROP_GETTER) || node.getBooleanProp(Node.COMPUTED_PROP_SETTER)) {
Node function = first.getNext();
Node params = function.getSecondChild();
Node body = function.getLastChild();
add(params);
add(body);
} else {
// This is a field or object literal property.
boolean isInClass = node.getParent().isClassMembers();
Node initializer = first.getNext();
if (initializer != null) {
// Object literal value.
checkState(!isInClass, "initializers should only exist in object literals, not classes");
cc.add(":");
// Must use addExpr() with a priority of 1, because a comma expression here would cause
// a syntax error within the object literal.
addExpr(initializer, 1, Context.OTHER);
} else {
// Computed properties must either have an initializer or be computed member-variable
// properties that exist for their type declaration.
checkState(node.getBooleanProp(Node.COMPUTED_PROP_VARIABLE), node);
}
}
break;
case OBJECT_PATTERN:
addObjectPattern(node);
maybeAddTypeDecl(node);
break;
case SWITCH:
add("switch(");
add(first);
add(")");
cc.beginBlock();
addAllSiblings(first.getNext());
cc.endBlock(context == Context.STATEMENT);
break;
case CASE:
Preconditions.checkState(childCount == 2, node);
add("case ");
add(first);
addCaseBody(last);
break;
case DEFAULT_CASE:
Preconditions.checkState(childCount == 1, node);
add("default");
addCaseBody(first);
break;
case LABEL:
Preconditions.checkState(childCount == 2, node);
if (!first.isLabelName()) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(first);
add(":");
if (!last.isBlock()) {
cc.maybeInsertSpace();
}
addNonEmptyStatement(last, getContextForNonEmptyExpression(context), true);
break;
case CAST:
if (preserveTypeAnnotations) {
add("(");
// drop context because of added parentheses
add(first);
add(")");
} else {
// preserve context
add(first, context);
}
break;
case TAGGED_TEMPLATELIT:
this.addInvocationTarget(node, context);
add(first.getNext());
break;
case TEMPLATELIT:
cc.beginTemplateLit();
for (Node c = first; c != null; c = c.getNext()) {
if (c.isTemplateLitString()) {
add(escapeUnrecognizedCharacters(c.getRawString()));
} else {
cc.beginTemplateLitSub();
add(c.getFirstChild(), Context.START_OF_EXPR);
cc.endTemplateLitSub();
}
}
cc.endTemplateLit();
break;
// Type Declaration ASTs.
case STRING_TYPE:
add("string");
break;
case BOOLEAN_TYPE:
add("boolean");
break;
case NUMBER_TYPE:
add("number");
break;
case ANY_TYPE:
add("any");
break;
case VOID_TYPE:
add("void");
break;
case NAMED_TYPE:
// Children are a chain of getprop nodes.
add(first);
break;
case ARRAY_TYPE:
addExpr(first, NodeUtil.precedence(Token.ARRAY_TYPE), context);
add("[]");
break;
case FUNCTION_TYPE:
Node returnType = first;
add("(");
addList(first.getNext());
add(")");
cc.addOp("=>", true);
add(returnType);
break;
case UNION_TYPE:
addList(first, "|");
break;
case RECORD_TYPE:
add("{");
addList(first, false, Context.OTHER, ",");
add("}");
break;
case PARAMETERIZED_TYPE:
// First child is the type that's parameterized, later children are the arguments.
add(first);
add("<");
addList(first.getNext());
add(">");
break;
// CLASS -> NAME,EXPR|EMPTY,BLOCK
case GENERIC_TYPE_LIST:
add("<");
addList(first, false, Context.STATEMENT, ",");
add(">");
break;
case GENERIC_TYPE:
addIdentifier(node.getString());
if (node.hasChildren()) {
add("extends");
cc.maybeInsertSpace();
add(node.getFirstChild());
}
break;
case INTERFACE:
{
checkState(childCount == 3, node);
Node name = first;
Node superTypes = first.getNext();
Node members = last;
add("interface");
add(name);
maybeAddGenericTypes(name);
if (!superTypes.isEmpty()) {
add("extends");
Node superType = superTypes.getFirstChild();
add(superType);
while ((superType = superType.getNext()) != null) {
add(",");
cc.maybeInsertSpace();
add(superType);
}
}
add(members);
}
break;
case ENUM:
{
checkState(childCount == 2, node);
Node name = first;
Node members = last;
add("enum");
add(name);
add(members);
break;
}
case NAMESPACE:
{
checkState(childCount == 2, node);
Node name = first;
Node elements = last;
add("namespace");
add(name);
add(elements);
break;
}
case TYPE_ALIAS:
add("type");
add(node.getString());
cc.addOp("=", true);
add(last);
cc.endStatement(true);
break;
case DECLARE:
add("declare");
add(first);
processEnd(node, context);
break;
case INDEX_SIGNATURE:
add("[");
add(first);
add("]");
maybeAddTypeDecl(node);
cc.endStatement(true);
break;
case CALL_SIGNATURE:
if (node.getBooleanProp(Node.CONSTRUCT_SIGNATURE)) {
add("new ");
}
maybeAddGenericTypes(node);
add(first);
maybeAddTypeDecl(node);
cc.endStatement(true);
break;
default:
throw new IllegalStateException("Unknown token " + type + "\n" + node.toStringTree());
}
// print any trailing nonJSDoc comment attached to this node
if (printNonJSDocComments) {
NonJSDocComment nonJSDocComment = node.getNonJSDocComment();
if (nonJSDocComment != null && nonJSDocComment.isTrailing()) {
String nonJSDocCommentString = node.getNonJSDocCommentString();
if (!nonJSDocCommentString.isEmpty()) {
addNonJsDoctrailing(nonJSDocComment);
}
}
}
cc.endSourceMapping(node);
}
use of com.google.javascript.rhino.Token in project closure-compiler by google.
the class CheckRegExp method visit.
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (NodeUtil.isReferenceName(n)) {
String name = n.getString();
if (name.equals("RegExp") && t.getScope().getVar(name) == null) {
Token parentType = parent.getToken();
boolean first = (n == parent.getFirstChild());
if (!((parentType == Token.NEW && first) || (parentType == Token.CALL && first) || (parentType == Token.INSTANCEOF && !first) || parentType == Token.EQ || parentType == Token.NE || parentType == Token.SHEQ || parentType == Token.SHNE || parentType == Token.CASE || (parentType == Token.GETPROP && first && !REGEXP_PROPERTY_SKIPLIST.contains(parent.getString())))) {
if (reportErrors) {
t.report(n, REGEXP_REFERENCE);
}
globalRegExpPropertiesUsed = true;
}
}
// Check the syntax of regular expression patterns.
} else if (reportErrors && n.isRegExp()) {
String pattern = n.getFirstChild().getString();
String flags = n.hasTwoChildren() ? n.getLastChild().getString() : "";
try {
RegExpTree.parseRegExp(pattern, flags);
} catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
t.report(n, MALFORMED_REGEXP, ex.getMessage());
}
}
}
use of com.google.javascript.rhino.Token in project closure-compiler by google.
the class Es6RewriteDestructuring method visitDestructuringPatternInEnhancedForInnerVars.
/**
* for (const [a, b, c] of arr)
*/
private void visitDestructuringPatternInEnhancedForInnerVars(Node pattern) {
checkArgument(pattern.isDestructuringPattern());
String tempVarName = getTempVariableName();
Node destructuringLhs = pattern.getParent();
checkState(destructuringLhs.isDestructuringLhs());
Node declarationNode = destructuringLhs.getParent();
Node forNode = declarationNode.getParent();
checkState(NodeUtil.isEnhancedFor(forNode));
Node block = forNode.getLastChild();
destructuringLhs.replaceWith(astFactory.createName(tempVarName, type(pattern)).srcref(pattern));
Token declarationType = declarationNode.getToken();
Node decl = IR.declaration(pattern.detach(), astFactory.createName(tempVarName, type(pattern)), declarationType);
decl.srcrefTreeIfMissing(pattern);
// Move the body into an inner block to handle cases where declared variables in the for
// loop initializer are shadowed by variables in the for loop body. e.g.
// for (const [value] of []) { const value = 1; }
Node newBlock = IR.block(decl);
block.replaceWith(newBlock);
newBlock.addChildToBack(block);
}
use of com.google.javascript.rhino.Token in project closure-compiler by google.
the class Es6ForOfConverter method visitForOf.
// TODO(lharker): break up this method
private void visitForOf(Node node) {
Node variable = node.removeFirstChild();
Node iterable = node.removeFirstChild();
Node body = node.removeFirstChild();
JSDocInfo varJSDocInfo = variable.getJSDocInfo();
Node iterName = astFactory.createName(ITER_BASE + compiler.getUniqueNameIdSupplier().get(), type(StandardColors.ITERATOR_ID));
iterName.makeNonIndexable();
Node getNext = astFactory.createCallWithUnknownType(astFactory.createGetPropWithUnknownType(iterName.cloneTree(), "next"));
String iteratorResultName = ITER_RESULT;
if (NodeUtil.isNameDeclaration(variable)) {
iteratorResultName += variable.getFirstChild().getString();
} else if (variable.isName()) {
iteratorResultName += variable.getString();
} else {
// give arbitrary lhs expressions an arbitrary name
iteratorResultName += namer.generateNextName();
}
Node iterResult = astFactory.createNameWithUnknownType(iteratorResultName);
iterResult.makeNonIndexable();
Node call = astFactory.createJSCompMakeIteratorCall(iterable, this.namespace);
Node init = IR.var(iterName.cloneTree().setColor(iterName.getColor()), call);
Node initIterResult = iterResult.cloneTree();
initIterResult.addChildToFront(getNext.cloneTree());
init.addChildToBack(initIterResult);
Node cond = astFactory.createNot(astFactory.createGetProp(iterResult.cloneTree(), "done", type(StandardColors.BOOLEAN)));
Node incr = astFactory.createAssign(iterResult.cloneTree(), getNext.cloneTree());
Node declarationOrAssign;
if (!NodeUtil.isNameDeclaration(variable)) {
declarationOrAssign = astFactory.createAssign(variable.cloneTree().setJSDocInfo(null), astFactory.createGetProp(iterResult.cloneTree(), "value", type(variable)));
declarationOrAssign.setJSDocInfo(varJSDocInfo);
declarationOrAssign = IR.exprResult(declarationOrAssign);
} else {
AstFactory.Type type = type(variable.getFirstChild());
// i.e. VAR, CONST, or LET.
Token declarationType = variable.getToken();
declarationOrAssign = new Node(declarationType, astFactory.createName(variable.getFirstChild().getString(), type).srcref(variable.getFirstChild()));
declarationOrAssign.getFirstChild().addChildToBack(astFactory.createGetProp(iterResult.cloneTree(), "value", type));
declarationOrAssign.setJSDocInfo(varJSDocInfo);
}
Node newBody = IR.block(declarationOrAssign, body).srcref(body);
Node newFor = IR.forNode(init, cond, incr, newBody);
newFor.srcrefTreeIfMissing(node);
node.replaceWith(newFor);
compiler.reportChangeToEnclosingScope(newFor);
}
Aggregations