Search in sources :

Example 26 with Token

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;
}
Also used : Node(com.google.javascript.rhino.Node) BigInteger(java.math.BigInteger) Token(com.google.javascript.rhino.Token)

Example 27 with Token

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);
}
Also used : Node(com.google.javascript.rhino.Node) Token(com.google.javascript.rhino.Token) NonJSDocComment(com.google.javascript.rhino.NonJSDocComment)

Example 28 with Token

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());
        }
    }
}
Also used : Token(com.google.javascript.rhino.Token)

Example 29 with Token

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);
}
Also used : Node(com.google.javascript.rhino.Node) Token(com.google.javascript.rhino.Token)

Example 30 with Token

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);
}
Also used : Node(com.google.javascript.rhino.Node) Token(com.google.javascript.rhino.Token) JSDocInfo(com.google.javascript.rhino.JSDocInfo)

Aggregations

Token (com.google.javascript.rhino.Token)35 Node (com.google.javascript.rhino.Node)29 JSType (com.google.javascript.rhino.jstype.JSType)4 Tri (com.google.javascript.jscomp.base.Tri)3 DiGraphNode (com.google.javascript.jscomp.graph.DiGraph.DiGraphNode)3 FlowScope (com.google.javascript.jscomp.type.FlowScope)3 JSDocInfo (com.google.javascript.rhino.JSDocInfo)3 Test (org.junit.Test)3 MeasuredNode (com.google.javascript.jscomp.MinimizedCondition.MeasuredNode)2 JSType (com.google.javascript.jscomp.newtypes.JSType)2 Preconditions.checkState (com.google.common.base.Preconditions.checkState)1 Predicate (com.google.common.base.Predicate)1 HashMultiset (com.google.common.collect.HashMultiset)1 Multiset (com.google.common.collect.Multiset)1 CheckReturnValue (com.google.errorprone.annotations.CheckReturnValue)1 AssertionFunctionLookup (com.google.javascript.jscomp.CodingConvention.AssertionFunctionLookup)1 LinearFlowState (com.google.javascript.jscomp.DataFlowAnalysis.LinearFlowState)1 FunctionlessLocalScope (com.google.javascript.jscomp.NodeIterators.FunctionlessLocalScope)1 AbstractScopedCallback (com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback)1 LogFile (com.google.javascript.jscomp.diagnostic.LogFile)1