All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.js.parser.Parser Maven / Gradle / Ivy

/*
 * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.oracle.js.parser;

import static com.oracle.js.parser.ScriptEnvironment.ES_2015;
import static com.oracle.js.parser.ScriptEnvironment.ES_2017;
import static com.oracle.js.parser.ScriptEnvironment.ES_2020;
import static com.oracle.js.parser.ScriptEnvironment.ES_2021;
import static com.oracle.js.parser.ScriptEnvironment.ES_2022;
import static com.oracle.js.parser.ScriptEnvironment.ES_STAGING;
import static com.oracle.js.parser.TokenType.ACCESSOR;
import static com.oracle.js.parser.TokenType.ARROW;
import static com.oracle.js.parser.TokenType.AS;
import static com.oracle.js.parser.TokenType.ASSERT;
import static com.oracle.js.parser.TokenType.ASSIGN;
import static com.oracle.js.parser.TokenType.ASSIGN_INIT;
import static com.oracle.js.parser.TokenType.ASYNC;
import static com.oracle.js.parser.TokenType.AT;
import static com.oracle.js.parser.TokenType.AWAIT;
import static com.oracle.js.parser.TokenType.CASE;
import static com.oracle.js.parser.TokenType.CATCH;
import static com.oracle.js.parser.TokenType.CLASS;
import static com.oracle.js.parser.TokenType.COLON;
import static com.oracle.js.parser.TokenType.COMMARIGHT;
import static com.oracle.js.parser.TokenType.COMMENT;
import static com.oracle.js.parser.TokenType.CONST;
import static com.oracle.js.parser.TokenType.DECPOSTFIX;
import static com.oracle.js.parser.TokenType.DECPREFIX;
import static com.oracle.js.parser.TokenType.ELLIPSIS;
import static com.oracle.js.parser.TokenType.ELSE;
import static com.oracle.js.parser.TokenType.EOF;
import static com.oracle.js.parser.TokenType.EOL;
import static com.oracle.js.parser.TokenType.EQ_STRICT;
import static com.oracle.js.parser.TokenType.ESCSTRING;
import static com.oracle.js.parser.TokenType.EXPORT;
import static com.oracle.js.parser.TokenType.EXTENDS;
import static com.oracle.js.parser.TokenType.FINALLY;
import static com.oracle.js.parser.TokenType.FROM;
import static com.oracle.js.parser.TokenType.FUNCTION;
import static com.oracle.js.parser.TokenType.GET;
import static com.oracle.js.parser.TokenType.IDENT;
import static com.oracle.js.parser.TokenType.IF;
import static com.oracle.js.parser.TokenType.IMPORT;
import static com.oracle.js.parser.TokenType.IN;
import static com.oracle.js.parser.TokenType.INCPOSTFIX;
import static com.oracle.js.parser.TokenType.INCPREFIX;
import static com.oracle.js.parser.TokenType.LBRACE;
import static com.oracle.js.parser.TokenType.LBRACKET;
import static com.oracle.js.parser.TokenType.LET;
import static com.oracle.js.parser.TokenType.LPAREN;
import static com.oracle.js.parser.TokenType.MUL;
import static com.oracle.js.parser.TokenType.OF;
import static com.oracle.js.parser.TokenType.PERIOD;
import static com.oracle.js.parser.TokenType.PRIVATE_IDENT;
import static com.oracle.js.parser.TokenType.RBRACE;
import static com.oracle.js.parser.TokenType.RBRACKET;
import static com.oracle.js.parser.TokenType.RPAREN;
import static com.oracle.js.parser.TokenType.SEMICOLON;
import static com.oracle.js.parser.TokenType.SET;
import static com.oracle.js.parser.TokenType.SPREAD_ARGUMENT;
import static com.oracle.js.parser.TokenType.SPREAD_ARRAY;
import static com.oracle.js.parser.TokenType.SPREAD_OBJECT;
import static com.oracle.js.parser.TokenType.STATIC;
import static com.oracle.js.parser.TokenType.STRING;
import static com.oracle.js.parser.TokenType.SUPER;
import static com.oracle.js.parser.TokenType.TEMPLATE;
import static com.oracle.js.parser.TokenType.TEMPLATE_HEAD;
import static com.oracle.js.parser.TokenType.TEMPLATE_MIDDLE;
import static com.oracle.js.parser.TokenType.TEMPLATE_TAIL;
import static com.oracle.js.parser.TokenType.TERNARY;
import static com.oracle.js.parser.TokenType.THIS;
import static com.oracle.js.parser.TokenType.VAR;
import static com.oracle.js.parser.TokenType.VOID;
import static com.oracle.js.parser.TokenType.WHILE;
import static com.oracle.js.parser.TokenType.WITH;
import static com.oracle.js.parser.TokenType.YIELD;
import static com.oracle.js.parser.TokenType.YIELD_STAR;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import org.graalvm.collections.Pair;

import com.oracle.js.parser.ir.AccessNode;
import com.oracle.js.parser.ir.BaseNode;
import com.oracle.js.parser.ir.BinaryNode;
import com.oracle.js.parser.ir.Block;
import com.oracle.js.parser.ir.BlockStatement;
import com.oracle.js.parser.ir.BreakNode;
import com.oracle.js.parser.ir.CallNode;
import com.oracle.js.parser.ir.CaseNode;
import com.oracle.js.parser.ir.CatchNode;
import com.oracle.js.parser.ir.ClassElement;
import com.oracle.js.parser.ir.ClassNode;
import com.oracle.js.parser.ir.ContinueNode;
import com.oracle.js.parser.ir.DebuggerNode;
import com.oracle.js.parser.ir.EmptyNode;
import com.oracle.js.parser.ir.ErrorNode;
import com.oracle.js.parser.ir.ExportNode;
import com.oracle.js.parser.ir.ExportSpecifierNode;
import com.oracle.js.parser.ir.Expression;
import com.oracle.js.parser.ir.ExpressionList;
import com.oracle.js.parser.ir.ExpressionStatement;
import com.oracle.js.parser.ir.ForNode;
import com.oracle.js.parser.ir.FromNode;
import com.oracle.js.parser.ir.FunctionNode;
import com.oracle.js.parser.ir.IdentNode;
import com.oracle.js.parser.ir.IfNode;
import com.oracle.js.parser.ir.ImportClauseNode;
import com.oracle.js.parser.ir.ImportNode;
import com.oracle.js.parser.ir.ImportSpecifierNode;
import com.oracle.js.parser.ir.IndexNode;
import com.oracle.js.parser.ir.JoinPredecessorExpression;
import com.oracle.js.parser.ir.LabelNode;
import com.oracle.js.parser.ir.LexicalContext;
import com.oracle.js.parser.ir.LiteralNode;
import com.oracle.js.parser.ir.LiteralNode.ArrayLiteralNode;
import com.oracle.js.parser.ir.Module;
import com.oracle.js.parser.ir.Module.ImportEntry;
import com.oracle.js.parser.ir.Module.ModuleRequest;
import com.oracle.js.parser.ir.NameSpaceImportNode;
import com.oracle.js.parser.ir.NamedExportsNode;
import com.oracle.js.parser.ir.NamedImportsNode;
import com.oracle.js.parser.ir.Node;
import com.oracle.js.parser.ir.ObjectNode;
import com.oracle.js.parser.ir.ParameterNode;
import com.oracle.js.parser.ir.PropertyKey;
import com.oracle.js.parser.ir.PropertyNode;
import com.oracle.js.parser.ir.ReturnNode;
import com.oracle.js.parser.ir.Scope;
import com.oracle.js.parser.ir.Statement;
import com.oracle.js.parser.ir.SwitchNode;
import com.oracle.js.parser.ir.Symbol;
import com.oracle.js.parser.ir.TemplateLiteralNode;
import com.oracle.js.parser.ir.TernaryNode;
import com.oracle.js.parser.ir.ThrowNode;
import com.oracle.js.parser.ir.TryNode;
import com.oracle.js.parser.ir.UnaryNode;
import com.oracle.js.parser.ir.VarNode;
import com.oracle.js.parser.ir.WhileNode;
import com.oracle.js.parser.ir.WithNode;
import com.oracle.js.parser.ir.visitor.NodeVisitor;
import com.oracle.truffle.api.strings.TruffleString;

/**
 * Builds the IR.
 */
@SuppressWarnings("fallthrough")
public class Parser extends AbstractParser {
    /** The arguments variable name. */
    static final String ARGUMENTS_NAME = "arguments";
    static final TruffleString ARGUMENTS_NAME_TS = ParserStrings.constant(ARGUMENTS_NAME);
    /** The eval function variable name. */
    private static final String EVAL_NAME = "eval";
    private static final String CONSTRUCTOR_NAME = "constructor";
    private static final TruffleString CONSTRUCTOR_NAME_TS = ParserStrings.constant(CONSTRUCTOR_NAME);
    private static final String PRIVATE_CONSTRUCTOR_NAME = "#constructor";
    private static final String PROTO_NAME = "__proto__";
    static final String NEW_TARGET_NAME = "new.target";
    static final TruffleString NEW_TARGET_NAME_TS = ParserStrings.constant(NEW_TARGET_NAME);
    private static final TruffleString IMPORT_META_NAME = ParserStrings.constant("import.meta");
    private static final String PROTOTYPE_NAME = "prototype";
    /** Function.prototype.apply method name. */
    private static final String APPLY_NAME = "apply";

    /** EXEC name - special property used by $EXEC API. */
    private static final String EXEC_NAME = "$EXEC";
    /** Function name for anonymous functions. */
    private static final TruffleString ANONYMOUS_FUNCTION_NAME = ParserStrings.constant(":anonymous");
    /** Function name for the program entry point. */
    private static final TruffleString PROGRAM_NAME = ParserStrings.constant(":program");
    /** Internal lexical binding name for {@code catch} error (destructuring). */
    private static final TruffleString ERROR_BINDING_NAME = ParserStrings.constant(":error");
    /** Internal lexical binding name for {@code switch} expression. */
    private static final TruffleString SWITCH_BINDING_NAME = ParserStrings.constant(":switch");
    /** Function name for arrow functions. */
    private static final TruffleString ARROW_FUNCTION_NAME = ParserStrings.constant(":=>");
    /** Internal function name for class field initializer. */
    private static final TruffleString INITIALIZER_FUNCTION_NAME = ParserStrings.constant(":initializer");

    private static final boolean ES6_FOR_OF = Options.getBooleanProperty("parser.for.of", true);
    private static final boolean ES6_CLASS = Options.getBooleanProperty("parser.class", true);
    private static final boolean ES6_ARROW_FUNCTION = Options.getBooleanProperty("parser.arrow.function", true);
    private static final boolean ES6_REST_PARAMETER = Options.getBooleanProperty("parser.rest.parameter", true);
    private static final boolean ES6_SPREAD_ARGUMENT = Options.getBooleanProperty("parser.spread.argument", true);
    private static final boolean ES6_GENERATOR_FUNCTION = Options.getBooleanProperty("parser.generator.function", true);
    private static final boolean ES6_DESTRUCTURING = Options.getBooleanProperty("parser.destructuring", true);
    private static final boolean ES6_SPREAD_ARRAY = Options.getBooleanProperty("parser.spread.array", true);
    private static final boolean ES6_COMPUTED_PROPERTY_NAME = Options.getBooleanProperty("parser.computed.property.name", true);
    private static final boolean ES6_DEFAULT_PARAMETER = Options.getBooleanProperty("parser.default.parameter", true);
    private static final boolean ES6_NEW_TARGET = Options.getBooleanProperty("parser.new.target", true);

    private static final boolean ES8_TRAILING_COMMA = Options.getBooleanProperty("parser.trailing.comma", true);
    private static final boolean ES8_ASYNC_FUNCTION = Options.getBooleanProperty("parser.async.function", true);
    private static final boolean ES8_REST_SPREAD_PROPERTY = Options.getBooleanProperty("parser.rest.spread.property", true);
    private static final boolean ES8_FOR_AWAIT_OF = Options.getBooleanProperty("parser.for.await.of", true);
    private static final boolean ES2019_OPTIONAL_CATCH_BINDING = Options.getBooleanProperty("parser.optional.catch.binding", true);
    private static final boolean ES2020_CLASS_FIELDS = Options.getBooleanProperty("parser.class.fields", true);
    private static final boolean ES2022_TOP_LEVEL_AWAIT = Options.getBooleanProperty("parser.top.level.await", true);

    private static final int REPARSE_IS_PROPERTY_ACCESSOR = 1 << 0;
    private static final int REPARSE_IS_METHOD = 1 << 1;
    /** Parsing eval. */
    private static final int PARSE_EVAL = 1 << 2;
    /** Parsing eval in a function (i.e. not script or module) context. */
    private static final int PARSE_FUNCTION_CONTEXT_EVAL = 1 << 3;

    private static final String USE_STRICT = "use strict";
    private static final TruffleString ARGS = ParserStrings.constant("args");
    private static final String GET_SPC = "get ";
    private static final String SET_SPC = "set ";
    private static final String META = "meta";
    private static final TruffleString TARGET = ParserStrings.constant("target");

    private static final String CONTEXT_ASSIGNMENT_TARGET = "assignment target";
    private static final String CONTEXT_ASYNC_FUNCTION_DECLARATION = "async function declaration";
    private static final String CONTEXT_CATCH_PARAMETER = "catch parameter";
    private static final String CONTEXT_CLASS_DECLARATION = "class declaration";
    private static final String CONTEXT_CLASS_NAME = "class name";
    private static final String CONTEXT_CONST_DECLARATION = "const declaration";
    private static final String CONTEXT_FOR_IN_ITERATOR = "for-in iterator";
    private static final String CONTEXT_FOR_OF_ITERATOR = "for-of iterator";
    private static final String CONTEXT_FUNCTION_DECLARATION = "function declaration";
    private static final String CONTEXT_FUNCTION_NAME = "function name";
    private static final String CONTEXT_FUNCTION_PARAMETER = "function parameter";
    private static final String CONTEXT_GENERATOR_FUNCTION_DECLARATION = "generator function declaration";
    private static final String CONTEXT_IDENTIFIER_REFERENCE = "IdentifierReference";
    private static final String CONTEXT_IMPORTED_BINDING = "imported binding";
    private static final String CONTEXT_IN = "in";
    private static final String CONTEXT_LABEL_IDENTIFIER = "LabelIdentifier";
    private static final String CONTEXT_LET_DECLARATION = "let declaration";
    private static final String CONTEXT_OF = "of";
    private static final String CONTEXT_OPERAND_FOR_DEC_OPERATOR = "operand for -- operator";
    private static final String CONTEXT_OPERAND_FOR_INC_OPERATOR = "operand for ++ operator";
    private static final String CONTEXT_VARIABLE_NAME = "variable name";

    private static final String MSG_ACCESSOR_CONSTRUCTOR = "accessor.constructor";
    private static final String MSG_ARGUMENTS_IN_FIELD_INITIALIZER = "arguments.in.field.initializer";
    private static final String MSG_ASYNC_CONSTRUCTOR = "async.constructor";
    private static final String MSG_CONSTRUCTOR_FIELD = "constructor.field";
    private static final String MSG_DUPLICATE_DEFAULT_IN_SWITCH = "duplicate.default.in.switch";
    private static final String MSG_DUPLICATE_IMPORT_ATTRIBUTE = "duplicate.import.attribute";
    private static final String MSG_DUPLICATE_LABEL = "duplicate.label";
    private static final String MSG_ESCAPED_KEYWORD = "escaped.keyword";
    private static final String MSG_EXPECTED_ARROW_PARAMETER = "expected.arrow.parameter";
    private static final String MSG_EXPECTED_BINDING = "expected.binding";
    private static final String MSG_EXPECTED_BINDING_IDENTIFIER = "expected.binding.identifier";
    private static final String MSG_EXPECTED_COMMA = "expected.comma";
    private static final String MSG_EXPECTED_IMPORT = "expected.import";
    private static final String MSG_EXPECTED_NAMED_IMPORT = "expected.named.import";
    private static final String MSG_EXPECTED_OPERAND = "expected.operand";
    private static final String MSG_EXPECTED_PROPERTY_ID = "expected.property.id";
    private static final String MSG_EXPECTED_STMT = "expected.stmt";
    private static final String MSG_EXPECTED_TARGET = "expected.target";
    private static final String MSG_FOR_EACH_WITHOUT_IN = "for.each.without.in";
    private static final String MSG_FOR_IN_LOOP_INITIALIZER = "for.in.loop.initializer";
    private static final String MSG_GENERATOR_CONSTRUCTOR = "generator.constructor";
    private static final String MSG_ILLEGAL_BREAK_STMT = "illegal.break.stmt";
    private static final String MSG_ILLEGAL_CONTINUE_STMT = "illegal.continue.stmt";
    private static final String MSG_INVALID_ARROW_PARAMETER = "invalid.arrow.parameter";
    private static final String MSG_INVALID_AWAIT = "invalid.await";
    private static final String MSG_INVALID_EXPORT = "invalid.export";
    private static final String MSG_INVALID_FOR_AWAIT_OF = "invalid.for.await.of";
    private static final String MSG_INVALID_LVALUE = "invalid.lvalue";
    private static final String MSG_INVALID_PRIVATE_IDENT = "invalid.private.ident";
    private static final String MSG_INVALID_PROPERTY_INITIALIZER = "invalid.property.initializer";
    private static final String MSG_INVALID_RETURN = "invalid.return";
    private static final String MSG_INVALID_SUPER = "invalid.super";
    private static final String MSG_LET_LEXICAL_BINDING = "let.lexical.binding";
    private static final String MSG_MANY_VARS_IN_FOR_IN_LOOP = "many.vars.in.for.in.loop";
    private static final String MSG_MISSING_CATCH_OR_FINALLY = "missing.catch.or.finally";
    private static final String MSG_MISSING_CONST_ASSIGNMENT = "missing.const.assignment";
    private static final String MSG_MISSING_DESTRUCTURING_ASSIGNMENT = "missing.destructuring.assignment";
    private static final String MSG_MULTIPLE_CONSTRUCTORS = "multiple.constructors";
    private static final String MSG_MULTIPLE_PROTO_KEY = "multiple.proto.key";
    private static final String MSG_NEW_TARGET_IN_FUNCTION = "new.target.in.function";
    private static final String MSG_NO_FUNC_DECL_HERE = "no.func.decl.here";
    private static final String MSG_NO_FUNC_DECL_HERE_WARN = "no.func.decl.here.warn";
    private static final String MSG_NOT_LVALUE_FOR_IN_LOOP = "not.lvalue.for.in.loop";
    private static final String MSG_OPTIONAL_CHAIN_TEMPLATE = "optional.chain.template";
    private static final String MSG_PRIVATE_CONSTRUCTOR_METHOD = "private.constructor.method";
    private static final String MSG_PROPERTY_REDEFINITON = "property.redefinition";
    private static final String MSG_STATIC_PROTOTYPE_FIELD = "static.prototype.field";
    private static final String MSG_STATIC_PROTOTYPE_METHOD = "static.prototype.method";
    private static final String MSG_STRICT_CANT_DELETE_IDENT = "strict.cant.delete.ident";
    private static final String MSG_STRICT_CANT_DELETE_PRIVATE = "strict.cant.delete.private";
    private static final String MSG_STRICT_NAME = "strict.name";
    private static final String MSG_STRICT_NO_FUNC_DECL_HERE = "strict.no.func.decl.here";
    private static final String MSG_STRICT_NO_NONOCTALDECIMAL = "strict.no.nonoctaldecimal";
    private static final String MSG_STRICT_NO_OCTAL = "strict.no.octal";
    private static final String MSG_STRICT_NO_WITH = "strict.no.with";
    private static final String MSG_STRICT_PARAM_REDEFINITION = "strict.param.redefinition";
    private static final String MSG_SYNTAX_ERROR_REDECLARE_VARIABLE = "syntax.error.redeclare.variable";
    private static final String MSG_UNDEFINED_LABEL = "undefined.label";
    private static final String MSG_UNEXPECTED_IDENT = "unexpected.ident";
    private static final String MSG_UNEXPECTED_IMPORT_META = "unexpected.import.meta";
    private static final String MSG_UNEXPECTED_TOKEN = "unexpected.token";
    private static final String MSG_UNTERMINATED_TEMPLATE_EXPRESSION = "unterminated.template.expression";
    private static final String MSG_USE_STRICT_NON_SIMPLE_PARAM = "use.strict.non.simple.param";
    private static final String MSG_DECORATED_CONSTRUCTOR = "decorated.constructor";
    private static final String MSG_DECORATED_STATIC_BLOCK = "decorated.static.block";
    private static final String MSG_AUTO_ACCESSOR_NOT_FIELD = "auto.accessor.not.field";

    /** Current env. */
    private final ScriptEnvironment env;

    /** Is scripting mode. */
    private final boolean scripting;

    /** Is shebang supported */
    private final boolean shebang;

    /** Is BigInt supported */
    private final boolean allowBigInt;

    private List functionDeclarations;

    private final ParserContext lc;
    private final List defaultNames;

    /** to receive line information from Lexer when scanning multiline literals. */
    protected final Lexer.LineInfoReceiver lineInfoReceiver;

    private RecompilableScriptFunctionData reparsedFunction;

    private boolean isModule;

    /**
     * Used to pass (async) arrow function flags from head to body.
     *
     * Must be immediately consumed, i.e. before parsing the next expression, in order to avoid any
     * nesting issues.
     */
    private ParserContextFunctionNode coverArrowFunction;

    private static final boolean PROFILE_PARSING = Options.getBooleanProperty("parser.profiling", false);

    /**
     * Constructor
     *
     * @param env script environment
     * @param source source to parse
     * @param errors error manager
     */
    public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors) {
        this(env, source, errors, env.strict);
    }

    /**
     * Constructor
     *
     * @param env script environment
     * @param source source to parse
     * @param errors error manager
     * @param strict strict
     */
    public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict) {
        this(env, source, errors, strict, 0);
    }

    /**
     * Construct a parser.
     *
     * @param env script environment
     * @param source source to parse
     * @param errors error manager
     * @param strict parser created with strict mode enabled.
     * @param lineOffset line offset to start counting lines from
     */
    public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset) {
        super(source, errors, strict, lineOffset);
        this.lc = new ParserContext();
        this.defaultNames = new ArrayList<>();
        this.env = env;
        this.scripting = env.scripting && env.syntaxExtensions;
        this.shebang = env.shebang || scripting;
        this.allowBigInt = env.allowBigInt;
        if (this.scripting) {
            this.lineInfoReceiver = new Lexer.LineInfoReceiver() {
                @Override
                public void lineInfo(final int receiverLine, final int receiverLinePosition) {
                    // update the parser maintained line information
                    Parser.this.line = receiverLine;
                    Parser.this.linePosition = receiverLinePosition;
                }
            };
        } else {
            // non-scripting mode script can't have multi-line literals
            this.lineInfoReceiver = null;
        }
    }

    /**
     * Execute parse and return the resulting function node. Errors will be thrown and the error
     * manager will contain information if parsing should fail
     *
     * This is the default parse call, which will name the function node {@link #PROGRAM_NAME}.
     *
     * @return function node resulting from successful parse
     */
    public FunctionNode parse() {
        return parse(PROGRAM_NAME, 0, source.getLength(), 0, null, null);
    }

    /**
     * Sets the @link RecompilableScriptFunctionData representing the function being reparsed (when
     * this parser instance is used to reparse a previously parsed function, as part of its
     * on-demand compilation). This will trigger various special behaviors, such as skipping nested
     * function bodies.
     *
     * @param reparsedFunction the function being reparsed.
     */
    public void setReparsedFunction(final RecompilableScriptFunctionData reparsedFunction) {
        this.reparsedFunction = reparsedFunction;
    }

    /**
     * Set up first token. Skips opening EOL.
     */
    private void scanFirstToken() {
        k = -1;
        next();
    }

    /**
     * Prepare {@link TokenStream} and {@link Lexer} for parsing.
     *
     * @param startPos source start position
     * @param len source length
     */
    private void prepareLexer(final int startPos, final int len) {
        stream = new TokenStream();
        lexer = new Lexer(source, startPos, len, stream, scripting, env.ecmaScriptVersion, shebang, isModule, reparsedFunction != null, allowBigInt, env.annexB);
        lexer.line = lexer.pendingLine = lineOffset + 1;
        line = lineOffset;
    }

    /**
     * Peek ahead at the next token, skipping COMMENT and EOL tokens.
     */
    private TokenType lookahead() {
        for (int i = 1;; i++) {
            TokenType t = T(k + i);
            if (t == EOL || t == COMMENT) {
                continue;
            } else {
                return t;
            }
        }
    }

    /**
     * Peek ahead at the next 'accessor' token, skipping COMMENT but not EOL tokens.
     */
    private TokenType lookaheadNoLineTerminator() {
        for (int i = 1;; i++) {
            TokenType t = T(k + i);
            if (t == COMMENT) {
                continue;
            } else {
                return t;
            }
        }
    }

    /**
     * Execute parse and return the resulting function node. Errors will be thrown and the error
     * manager will contain information if parsing should fail
     *
     * This should be used to create one and only one function node
     *
     * @param scriptName name for the script, given to the parsed FunctionNode
     * @param startPos start position in source
     * @param len length of parse
     * @param reparseFlags flags provided by {@link RecompilableScriptFunctionData} as context for
     *            the code being reparsed. This allows us to recognize special forms of functions
     *            such as property getters and setters or instances of ES6 method shorthand in
     *            object literals.
     * @param argumentNames optional names of arguments assumed by the parsed function node.
     *
     * @return function node resulting from successful parse
     */
    public FunctionNode parse(final TruffleString scriptName, final int startPos, final int len, final int reparseFlags, Scope parentScope, List argumentNames) {
        long startTime = PROFILE_PARSING ? System.nanoTime() : 0L;
        FunctionNode program;
        try {
            prepareLexer(startPos, len);

            scanFirstToken();

            program = program(scriptName, reparseFlags, parentScope, argumentNames);
        } catch (ParserException e) {
            handleParseException(e);

            program = null;
        }
        if (PROFILE_PARSING) {
            long duration = (System.nanoTime() - startTime);
            System.out.println("Parsing: " + duration / 1_000_000);
        }
        return program;
    }

    /**
     * Parse and return the resulting module. Errors will be thrown and the error manager will
     * contain information if parsing should fail
     *
     * @param moduleName name for the module, given to the parsed FunctionNode
     * @param startPos start position in source
     * @param len length of parse
     *
     * @return function node resulting from successful parse
     */
    public FunctionNode parseModule(final String moduleName, final int startPos, final int len) {
        boolean oldModule = isModule;
        boolean oldStrictMode = isStrictMode;
        try {
            isModule = true;
            isStrictMode = true; // Module code is always strict mode code. (ES6 10.2.1)
            prepareLexer(startPos, len);

            scanFirstToken();

            return module(moduleName);
        } catch (ParserException e) {
            handleParseException(e);

            return null;
        } finally {
            isStrictMode = oldStrictMode;
            isModule = oldModule;
        }
    }

    public FunctionNode parseModule(final String moduleName) {
        return parseModule(moduleName, 0, source.getLength());
    }

    /**
     * Parse eval code.
     *
     * @param parentScope optional caller context scope (direct eval)
     */
    public FunctionNode parseEval(boolean functionContext, Scope parentScope) {
        return parse(PROGRAM_NAME, 0, source.getLength(), PARSE_EVAL | (functionContext ? PARSE_FUNCTION_CONTEXT_EVAL : 0), parentScope, null);
    }

    /**
     * Parse code assuming a set of given arguments for the returned {@code FunctionNode}.
     *
     * @param argumentNames names of arguments assumed by the parsed function node.
     */
    public FunctionNode parseWithArguments(List argumentNames) {
        return parse(PROGRAM_NAME, 0, source.getLength(), 0, null, argumentNames);
    }

    /**
     * Parse and return the list of function parameter list. A comma separated list of function
     * parameter identifiers is expected to be parsed. Errors will be thrown and the error manager
     * will contain information if parsing should fail. This method is used to check if parameter
     * Strings passed to "Function" constructor is a valid or not.
     *
     * @see #parseFunctionBody
     */
    public void parseFormalParameterList() {
        try {
            stream = new TokenStream();
            lexer = new Lexer(source, stream, scripting, env.ecmaScriptVersion, shebang, isModule, allowBigInt, env.annexB);

            scanFirstToken();

            assert lc.getCurrentScope() == null;

            // Set up a fake function.
            final int functionLine = line;
            final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
            final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), lexer.stringIntern(PROGRAM_NAME));
            final ParserContextFunctionNode function = createParserContextFunctionNode(ident, functionToken, 0, functionLine);
            function.clearFlag(FunctionNode.IS_PROGRAM);

            lc.push(function);
            try {
                final ParserContextBlockNode parameterBlock = function.createParameterBlock();
                lc.push(parameterBlock);
                try {
                    formalParameterList(TokenType.EOF, false, false);
                } finally {
                    restoreBlock(parameterBlock);
                }
            } finally {
                lc.pop(function);
            }
        } catch (ParserException e) {
            handleParseException(e);
        }
    }

    /**
     * Execute parse and return the resulting function node. Errors will be thrown and the error
     * manager will contain information if parsing should fail. This method is used to check if code
     * String passed to "Function" constructor is a valid function body or not.
     *
     * @return function node resulting from successful parse
     * @see #parseFormalParameterList
     */
    public FunctionNode parseFunctionBody(boolean generator, boolean async) {
        try {
            stream = new TokenStream();
            lexer = new Lexer(source, stream, scripting, env.ecmaScriptVersion, shebang, isModule, allowBigInt, env.annexB);
            final int functionLine = line;

            scanFirstToken();

            // Make a fake token for the function.
            final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
            // Set up the function to append elements.

            final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), lexer.stringIntern(PROGRAM_NAME));
            final int functionFlags = (generator ? FunctionNode.IS_GENERATOR : 0) | (async ? FunctionNode.IS_ASYNC : 0);
            final ParserContextFunctionNode function = createParserContextFunctionNode(ident, functionToken, functionFlags, functionLine, List.of(), 0);
            function.clearFlag(FunctionNode.IS_PROGRAM);

            assert lc.getCurrentScope() == null;
            lc.push(function);
            final ParserContextBlockNode body = newBlock(function.createBodyScope());
            functionDeclarations = new ArrayList<>();
            try {
                sourceElements(generator, async, 0);
                addFunctionDeclarations(function);

                function.finishBodyScope(lexer);
            } finally {
                functionDeclarations = null;
                restoreBlock(body);
                lc.pop(function);
            }
            body.setFlag(Block.NEEDS_SCOPE);
            final Block functionBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getScope(), body.getStatements());

            expect(EOF);

            final FunctionNode functionNode = createFunctionNode(
                            function,
                            functionToken,
                            ident,
                            functionLine,
                            functionBody);
            return functionNode;
        } catch (ParserException e) {
            handleParseException(e);
            return null;
        }
    }

    private void handleParseException(final ParserException e) {
        // Issue message.
        errors.error(e);

        if (env.dumpOnError) {
            e.printStackTrace(env.getErr());
        }
    }

    /**
     * Skip to a good parsing recovery point.
     */
    private void recover(final ParserException e) {
        if (e != null) {
            // Issue message.
            errors.error(e);

            if (env.dumpOnError) {
                e.printStackTrace(env.getErr());
            }
        }

        // Skip to a recovery point.
        loop: while (true) {
            switch (type) {
                case EOF:
                    // Can not go any further.
                    break loop;
                case EOL:
                case SEMICOLON:
                case RBRACE:
                    // Good recovery points.
                    next();
                    break loop;
                default:
                    // So we can recover after EOL.
                    nextOrEOL();
                    break;
            }
        }
    }

    private Scope newBlockScope() {
        return Scope.createBlock(lc.getCurrentScope());
    }

    /**
     * Set up a new block.
     *
     * @return New block.
     */
    private ParserContextBlockNode newBlock(Scope scope) {
        return lc.push(new ParserContextBlockNode(token, scope));
    }

    private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final int functionFlags, final int functionLine) {
        return createParserContextFunctionNode(ident, functionToken, functionFlags, functionLine, null, 0);
    }

    private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final int functionFlags, final int functionLine,
                    final List parameters, int functionLength) {
        return createParserContextFunctionNode(ident, functionToken, functionFlags, functionLine, parameters, functionLength, null);
    }

    private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final int functionFlags, final int functionLine,
                    final List parameters, int functionLength, Scope functionTopScope) {
        final ParserContextFunctionNode parentFunction = lc.getCurrentFunction();

        final TruffleString name = ident == null ? TruffleString.Encoding.UTF_16.getEmpty() : ident.getNameTS();

        int flags = functionFlags;
        if (isStrictMode) {
            flags |= FunctionNode.IS_STRICT;
        }
        if (parentFunction == null) {
            flags |= FunctionNode.IS_PROGRAM;
            flags |= FunctionNode.IS_ANONYMOUS;
        }

        final Scope parentScope = lc.getCurrentScope();
        return new ParserContextFunctionNode(functionToken, ident, name, functionLine, flags, parameters, functionLength, parentScope, functionTopScope);
    }

    private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident,
                    final int functionLine, final Block body) {
        assert body.isFunctionBody() || (body.isParameterBlock() && ((BlockStatement) body.getLastStatement()).getBlock().isFunctionBody());

        VarNode varNode = function.verifyHoistedVarDeclarations();
        if (varNode != null) {
            throw error(ECMAErrors.getMessage(MSG_SYNTAX_ERROR_REDECLARE_VARIABLE, varNode.getName().getName()), varNode.getToken());
        }

        long lastTokenWithDelimiter = Token.withDelimiter(function.getLastToken());
        // EOL uses length field to store the line number
        int lastTokenFinish = Token.descPosition(lastTokenWithDelimiter) + (Token.descType(lastTokenWithDelimiter) == TokenType.EOL ? 0 : Token.descLength(lastTokenWithDelimiter));

        final FunctionNode functionNode = new FunctionNode(
                        source,
                        functionLine,
                        body.getToken(),
                        lastTokenFinish,
                        startToken,
                        function.getLastToken(),
                        ident,
                        function.getNameTS(),
                        function.getLength(),
                        function.getParameterCount(),
                        function.getParameters(),
                        function.getFlags(),
                        body,
                        function.getEndParserState(),
                        function.getModule(),
                        function.getInternalNameTS());

        return functionNode;
    }

    /**
     * Restore the current block.
     */
    private void restoreBlock(final ParserContextBlockNode block) {
        lc.pop(block);
    }

    /**
     * Get the statements in a block.
     *
     * @return Block statements.
     */
    private Block getBlock(boolean yield, boolean await, boolean needsBraces) {
        final long blockToken = token;
        final ParserContextBlockNode newBlock = newBlock(newBlockScope());
        try {
            // Block opening brace.
            if (needsBraces) {
                expect(LBRACE);
            }
            // Accumulate block statements.
            statementList(yield, await);

        } finally {
            restoreBlock(newBlock);
        }

        // Block closing brace.
        int realFinish;
        if (needsBraces) {
            expectDontAdvance(RBRACE);
            // otherwise in case block containing single braced block the inner
            // block could end somewhere later after comments and spaces
            realFinish = Token.descPosition(token) + Token.descLength(token);
            expect(RBRACE);
        } else {
            realFinish = finish;
        }

        newBlock.getScope().close();
        final int flags = newBlock.getFlags() | (needsBraces ? 0 : Block.IS_SYNTHETIC);
        return new Block(blockToken, Math.max(realFinish, Token.descPosition(blockToken)), flags, newBlock.getScope(), newBlock.getStatements());
    }

    /**
     * Get the statements in a case clause.
     */
    private List caseStatementList(boolean yield, boolean await) {
        // case clauses share the same scope.
        final ParserContextBlockNode newBlock = newBlock(lc.getCurrentScope());
        try {
            statementList(yield, await);
        } finally {
            lc.pop(newBlock);
        }
        return newBlock.getStatements();
    }

    /**
     * Get all the statements generated by a single statement.
     *
     * @return Statements.
     */
    private Block getStatement(boolean yield, boolean await) {
        return getStatement(yield, await, false, false);
    }

    private Block getStatement(boolean yield, boolean await, boolean labelledStatement, boolean mayBeFunctionDeclaration) {
        return getStatement(yield, await, labelledStatement, mayBeFunctionDeclaration, mayBeFunctionDeclaration);
    }

    private Block getStatement(boolean yield, boolean await, boolean labelledStatement, boolean mayBeFunctionDeclaration, boolean mayBeLabeledFunctionDeclaration) {
        if (type == LBRACE) {
            return getBlock(yield, await, true);
        }
        // Set up new block. Captures first token.
        final ParserContextBlockNode newBlock = newBlock(newBlockScope());
        newBlock.setFlag(Block.IS_SYNTHETIC);
        try {
            statement(yield, await, false, 0, true, labelledStatement, mayBeFunctionDeclaration, mayBeLabeledFunctionDeclaration);
        } finally {
            restoreBlock(newBlock);
        }
        newBlock.getScope().close();
        return new Block(newBlock.getToken(), finish, newBlock.getFlags(), newBlock.getScope(), newBlock.getStatements());
    }

    /**
     * Detect use of special properties.
     *
     * @param ident Referenced property.
     */
    private IdentNode detectSpecialProperty(final IdentNode ident) {
        if (isArguments(ident)) {
            return markArguments(ident);
        }
        return ident;
    }

    private IdentNode markArguments(final IdentNode ident) {
        Scope currentScope = lc.getCurrentScope();
        if (currentScope.inClassFieldInitializer()) {
            throw error(AbstractParser.message(MSG_ARGUMENTS_IN_FIELD_INITIALIZER), ident.getToken());
        }
        if (currentScope.isGlobalScope()) {
            // arguments has no special meaning in the global scope
            return ident;
        }
        // skip over arrow functions, e.g. function f() { return (() => arguments.length)(); }
        lc.getCurrentNonArrowFunction().setFlag(FunctionNode.USES_ARGUMENTS);
        return ident.setIsArguments();
    }

    private boolean useBlockScope() {
        return isES6();
    }

    /**
     * ES6 (a.k.a. ES2015) or newer.
     */
    private boolean isES6() {
        return env.ecmaScriptVersion >= ES_2015;
    }

    /**
     * ES2017 or newer.
     */
    private boolean isES2017() {
        return env.ecmaScriptVersion >= ES_2017;
    }

    /**
     * ES2020 or newer.
     */
    private boolean isES2020() {
        return env.ecmaScriptVersion >= ES_2020;
    }

    /**
     * ES2021 or newer.
     */
    private boolean isES2021() {
        return env.ecmaScriptVersion >= ES_2021;
    }

    /**
     * ES2022 or newer.
     */
    private boolean isES2022() {
        return env.ecmaScriptVersion >= ES_2022;
    }

    private boolean isClassDecorators() {
        return env.ecmaScriptVersion >= ES_STAGING;
    }

    private boolean isClassFields() {
        return ES2020_CLASS_FIELDS && env.classFields;
    }

    static boolean isArguments(final TruffleString name) {
        return ARGUMENTS_NAME_TS.equals(name);
    }

    static boolean isArguments(final String name) {
        return ARGUMENTS_NAME.equals(name);
    }

    static boolean isArguments(final IdentNode ident) {
        return isArguments(ident.getName());
    }

    /**
     * Tells whether a IdentNode can be used as L-value of an assignment
     *
     * @param ident IdentNode to be checked
     * @return whether the ident can be used as L-value
     */
    private static boolean checkIdentLValue(final IdentNode ident) {
        return ident.tokenType().getKind() != TokenKind.KEYWORD;
    }

    /**
     * Verify an assignment expression.
     *
     * @param op Operation token.
     * @param lhs Left hand side expression.
     * @param rhs Right hand side expression.
     * @return Verified expression.
     */
    private Expression verifyAssignment(final long op, final Expression lhs, final Expression rhs, boolean inPatternPosition) {
        final TokenType opType = Token.descType(op);
        Expression rhsExpr = rhs;

        switch (opType) {
            case ASSIGN:
            case ASSIGN_INIT:
            case ASSIGN_ADD:
            case ASSIGN_BIT_AND:
            case ASSIGN_BIT_OR:
            case ASSIGN_BIT_XOR:
            case ASSIGN_DIV:
            case ASSIGN_MOD:
            case ASSIGN_MUL:
            case ASSIGN_EXP:
            case ASSIGN_SAR:
            case ASSIGN_SHL:
            case ASSIGN_SHR:
            case ASSIGN_SUB:
            case ASSIGN_AND:
            case ASSIGN_OR:
            case ASSIGN_NULLCOAL:
                if (lhs instanceof IdentNode) {
                    IdentNode ident = (IdentNode) lhs;
                    if (!checkIdentLValue(ident) || ident.isMetaProperty()) {
                        throw invalidLHSError(lhs);
                    }
                    verifyStrictIdent(ident, CONTEXT_ASSIGNMENT_TARGET);

                    // IsIdentifierRef must be true, so lhs must not be parenthesized.
                    if (!lhs.isParenthesized() && isAnonymousFunctionDefinition(rhsExpr)) {
                        rhsExpr = setAnonymousFunctionName(rhsExpr, ident.getNameTS());
                    }

                    break;
                } else if (lhs instanceof AccessNode || lhs instanceof IndexNode) {
                    if (((BaseNode) lhs).isOptional()) {
                        throw invalidLHSError(lhs);
                    }
                    break;
                } else if ((opType == ASSIGN || opType == ASSIGN_INIT) && isDestructuringLhs(lhs) && (inPatternPosition || !lhs.isParenthesized())) {
                    verifyDestructuringAssignmentPattern(lhs, CONTEXT_ASSIGNMENT_TARGET);
                    break;
                } else {
                    throw invalidLHSError(lhs);
                }
            default:
                break;
        }

        assert !BinaryNode.isLogical(opType);
        return new BinaryNode(op, lhs, rhsExpr);
    }

    private boolean isDestructuringLhs(Expression lhs) {
        if (lhs instanceof ObjectNode || lhs instanceof ArrayLiteralNode) {
            return ES6_DESTRUCTURING && isES6();
        }
        return false;
    }

    private void verifyDestructuringAssignmentPattern(Expression pattern, String contextString) {
        assert pattern instanceof ObjectNode || pattern instanceof ArrayLiteralNode;
        pattern.accept(new VerifyDestructuringPatternNodeVisitor(new LexicalContext()) {
            @Override
            protected void verifySpreadElement(Expression lvalue) {
                if (!checkValidLValue(lvalue, contextString)) {
                    throw error(AbstractParser.message(MSG_INVALID_LVALUE), lvalue.getToken());
                }
                lvalue.accept(this);
            }

            @Override
            public boolean enterIdentNode(IdentNode identNode) {
                if (!checkIdentLValue(identNode) || identNode.isMetaProperty()) {
                    throw error(AbstractParser.message(MSG_INVALID_LVALUE), identNode.getToken());
                }
                verifyStrictIdent(identNode, contextString);
                return false;
            }

            @Override
            public boolean enterAccessNode(AccessNode accessNode) {
                if (accessNode.isOptional()) {
                    throw error(AbstractParser.message(MSG_INVALID_LVALUE), accessNode.getToken());
                }
                return false;
            }

            @Override
            public boolean enterIndexNode(IndexNode indexNode) {
                if (indexNode.isOptional()) {
                    throw error(AbstractParser.message(MSG_INVALID_LVALUE), indexNode.getToken());
                }
                return false;
            }

            @Override
            protected boolean enterDefault(Node node) {
                throw error(String.format("unexpected node in AssignmentPattern: %s", node));
            }
        });
    }

    private Expression newBinaryExpression(final long op, final Expression lhs, final Expression rhs) {
        final TokenType opType = Token.descType(op);

        // Build up node.
        if (BinaryNode.isLogical(opType)) {
            if (forbiddenNullishCoalescingUsage(opType, lhs, rhs)) {
                throw error(String.format("nullish coalescing operator cannot immediately contain, or be contained within, an && or || operation"));
            }
            return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs));
        }
        return new BinaryNode(op, lhs, rhs);
    }

    private static boolean forbiddenNullishCoalescingUsage(TokenType opType, Expression lhs, Expression rhs) {
        if (opType == TokenType.NULLISHCOALESC) {
            return forbiddenNullishCoalescingChaining(lhs) || forbiddenNullishCoalescingChaining(rhs);
        } else {
            assert opType == TokenType.AND || opType == TokenType.OR;
            return (!lhs.isParenthesized() && lhs.isTokenType(TokenType.NULLISHCOALESC)) || (!rhs.isParenthesized() && rhs.isTokenType(TokenType.NULLISHCOALESC));
        }
    }

    private static boolean forbiddenNullishCoalescingChaining(Expression expression) {
        return !expression.isParenthesized() && (expression.isTokenType(TokenType.AND) || expression.isTokenType(TokenType.OR));
    }

    /**
     * Reduce increment/decrement to simpler operations.
     *
     * @param firstToken First token.
     * @param tokenType Operation token (INCPREFIX/DEC.)
     * @param expression Left hand side expression.
     * @param isPostfix Prefix or postfix.
     * @return Reduced expression.
     */
    private static UnaryNode incDecExpression(final long firstToken, final TokenType tokenType, final Expression expression, final boolean isPostfix) {
        assert tokenType == INCPREFIX || tokenType == DECPREFIX;
        if (isPostfix) {
            long postfixToken = Token.recast(firstToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX);
            return new UnaryNode(postfixToken, expression.getStart(), Token.descPosition(firstToken) + Token.descLength(firstToken), expression);
        }

        return new UnaryNode(firstToken, expression);
    }

    /**
     * Parse the top level script.
     *
     * 
     * Program :
     *      SourceElements?
     * 
*/ private FunctionNode program(final TruffleString scriptName, final int parseFlags, final Scope parentScope, final List argumentNames) { // Make a pseudo-token for the script holding its start and length. int functionStart = Math.min(Token.descPosition(Token.withDelimiter(token)), finish); final long functionToken = Token.toDesc(FUNCTION, functionStart, source.getLength() - functionStart); final int functionLine = line; Scope initialTopScope = (parseFlags & PARSE_EVAL) != 0 ? createEvalScope(parseFlags, parentScope) : Scope.createGlobal(); initialTopScope = applyArgumentsToScope(initialTopScope, argumentNames); final IdentNode ident = null; final List parameters = createFunctionNodeParameters(argumentNames); final ParserContextFunctionNode script = createParserContextFunctionNode( ident, functionToken, FunctionNode.IS_SCRIPT, functionLine, parameters, parameters.size(), initialTopScope); script.setInternalName(lexer.stringIntern(scriptName)); lc.push(script); final ParserContextBlockNode body = newBlock(initialTopScope); functionDeclarations = new ArrayList<>(); try { sourceElements(false, false, parseFlags); addFunctionDeclarations(script); script.finishBodyScope(lexer); } finally { functionDeclarations = null; restoreBlock(body); lc.pop(script); } Scope finalTopScope = body.getScope(); // We may have switched non-strict eval into strict mode and introduced an extra scope. assert finalTopScope == initialTopScope || finalTopScope.isEvalScope() : finalTopScope; initialTopScope.close(); finalTopScope.close(); body.setFlag(Block.NEEDS_SCOPE); final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, finalTopScope, body.getStatements()); script.setLastToken(token); expect(EOF); return createFunctionNode(script, functionToken, ident, functionLine, programBody); } private Scope applyArgumentsToScope(Scope scope, List argumentNames) { if (argumentNames == null) { return scope; } // If parsing with arguments, create an artificial local scope to emulate // function-like semantics: Scope body = Scope.createFunctionBody(scope); // We have to also explicitly put parameters in the top scope, because // ParserContextFunctionNode will not do it automatically for script nodes. for (String argument : argumentNames) { body.putSymbol(new Symbol(lexer.stringIntern(argument), Symbol.IS_VAR | Symbol.IS_PARAM)); } return body; } private List createFunctionNodeParameters(List argumentNames) { if (argumentNames == null) { return List.of(); } List list = new ArrayList<>(); for (String argumentName : argumentNames) { // Create an artificial IdentNode that is not in the source. list.add(new IdentNode(0, 0, lexer.stringIntern(argumentName))); } return list; } private Scope createEvalScope(final int parseFlags, Scope parentScope) { // 1. strict eval code always has its own scope // 2. non-strict indirect eval is in global scope // 3. non-strict direct eval is in global scope if the caller is // NOTE: In non-strict mode, the scope is preliminary: we may still encounter a "use strict" // directive and switch into strict mode (in which case we replace the scope). assert (parseFlags & PARSE_EVAL) != 0; if ((isStrictMode || (parseFlags & PARSE_FUNCTION_CONTEXT_EVAL) != 0)) { return Scope.createEval(parentScope, isStrictMode); } else { return Scope.createGlobal(); } } /** * Returns {@code true} if {@code stmt} is a {@code "use strict"} directive. * * @param stmt Statement to be checked */ private static boolean isDirective(final Node stmt) { if (stmt instanceof ExpressionStatement) { final Node expr = ((ExpressionStatement) stmt).getExpression(); if (expr instanceof LiteralNode) { final LiteralNode lit = (LiteralNode) expr; final long litToken = lit.getToken(); final TokenType tt = Token.descType(litToken); // A directive is either a string or an escape string return tt == TokenType.STRING || tt == TokenType.ESCSTRING; } } return false; } private boolean isUseStrictDirective(final Node stmt) { assert isDirective(stmt); final Expression exp = ((ExpressionStatement) stmt).getExpression(); // A directive is either a string or an escape string // Make sure that we don't unescape anything. Return as seen in source! return source.getContent().regionMatches(exp.getStart() + 1, USE_STRICT, 0, Token.descLength(exp.getToken()) - 2); } /** * Parse the statements of the script, module, or function. */ private void sourceElements(boolean yield, boolean await, int parseFlags) { boolean checkDirective = true; int functionFlags = parseFlags; final boolean oldStrictMode = isStrictMode; try { // If is a script, then process until the end of the script. while (type != EOF) { final TokenType elementType = type; // Break if the end of a code block. if (elementType == RBRACE) { break; } try { // Get the next element. statement(yield, await, true, functionFlags, false, false, true); functionFlags = 0; // Check for string directive prologues like "use strict". // A directive is either an unescaped or an escaped string. if (checkDirective) { // skip any debug statement like line number to get actual first line final Statement lastStatement = (elementType == STRING || elementType == ESCSTRING) ? lc.getLastStatement() : null; // If we have seen first non-directive statement, // no more directive statements! checkDirective = isDirective(lastStatement); if (checkDirective) { // handle use strict directive if (elementType == STRING && isUseStrictDirective(lastStatement)) { final ParserContextFunctionNode function = lc.getCurrentFunction(); if (!function.isSimpleParameterList()) { throw error(AbstractParser.message(MSG_USE_STRICT_NON_SIMPLE_PARAM), lastStatement.getToken()); } // We don't need to check these, if lexical environment is already // strict if (!oldStrictMode) { function.setFlag(FunctionNode.IS_STRICT); isStrictMode = true; verifyUseStrict(function, parseFlags); } else { assert function.isStrict(); } } } } } catch (ParserException e) { final int errorLine = line; final long errorToken = token; // recover parsing recover(e); final ErrorNode errorExpr = new ErrorNode(errorToken, finish); final ExpressionStatement expressionStatement = new ExpressionStatement(errorLine, errorToken, finish, errorExpr); appendStatement(expressionStatement); } // No backtracking from here on. stream.commit(k); } } finally { isStrictMode = oldStrictMode; } } private void verifyUseStrict(final ParserContextFunctionNode function, final int parseFlags) { // check that directives preceding this one do not violate strictness for (final Node statement : lc.peek().getStatements()) { // the getValue will force unescape of preceding escaped string directives getValue(statement.getToken()); } // verify that function name as well as parameter names // satisfy strict mode restrictions. if (function.getIdent() != null) { verifyStrictIdent(function.getIdent(), CONTEXT_FUNCTION_NAME); } for (final IdentNode param : function.getParameters()) { verifyStrictIdent(param, CONTEXT_FUNCTION_PARAMETER); } // Strict mode eval always gets its own var scope. if ((parseFlags & PARSE_EVAL) != 0) { setupStrictEvalScope(); } } /** * Switch from non-strict to strict mode eval. */ private void setupStrictEvalScope() { ParserContextBlockNode body = lc.getCurrentBlock(); assert body.getScope().getSymbolCount() == 0; if (body.getScope().isGlobalScope()) { Scope evalScope = Scope.createEval(body.getScope(), true); body.setScope(evalScope); ParserContextFunctionNode function = lc.getCurrentFunction(); function.replaceBodyScope(evalScope); assert function.getBodyScope() == evalScope; } } /** * Parse any of the basic statement types. * *
     * Statement :
     *      BlockStatement
     *      VariableStatement
     *      EmptyStatement
     *      ExpressionStatement
     *      IfStatement
     *      BreakableStatement
     *      ContinueStatement
     *      BreakStatement
     *      ReturnStatement
     *      WithStatement
     *      LabelledStatement
     *      ThrowStatement
     *      TryStatement
     *      DebuggerStatement
     *      DebuggerStatement
     *
     * BreakableStatement :
     *      IterationStatement
     *      SwitchStatement
     *
     * BlockStatement :
     *      Block
     *
     * Block :
     *      { StatementList opt }
     *
     * StatementList :
     *      StatementListItem
     *      StatementList StatementListItem
     *
     * StatementItem :
     *      Statement
     *      Declaration
     *
     * Declaration :
     *     HoistableDeclaration
     *     ClassDeclaration
     *     LexicalDeclaration
     *
     * HoistableDeclaration :
     *     FunctionDeclaration
     *     GeneratorDeclaration
     * 
*/ private void statement(boolean yield, boolean await) { statement(yield, await, false, 0, false, false, false); } private void statement(boolean yield, boolean await, boolean topLevel, int reparseFlags, boolean singleStatement, boolean labelledStatement, boolean mayBeFunctionDeclaration) { statement(yield, await, topLevel, reparseFlags, singleStatement, labelledStatement, mayBeFunctionDeclaration, mayBeFunctionDeclaration); } /** * @param topLevel does this statement occur at the "top level" of a script or a function? * @param reparseFlags reparse flags to decide whether to allow property "get" and "set" * functions or ES6 methods. * @param singleStatement are we in a single statement context? */ private void statement(boolean yield, boolean await, boolean topLevel, int reparseFlags, boolean singleStatement, boolean labelledStatement, boolean mayBeFunctionDeclaration, boolean mayBeLabeledFunctionDeclaration) { switch (type) { case LBRACE: block(yield, await); return; case VAR: variableStatement(type, yield, await); return; case SEMICOLON: emptyStatement(); return; case IF: ifStatement(yield, await); return; case FOR: forStatement(yield, await); return; case WHILE: whileStatement(yield, await); return; case DO: doStatement(yield, await); return; case CONTINUE: continueStatement(yield, await); return; case BREAK: breakStatement(yield, await); return; case RETURN: returnStatement(yield, await); return; case WITH: withStatement(yield, await); return; case SWITCH: switchStatement(yield, await); return; case THROW: throwStatement(yield, await); return; case TRY: tryStatement(yield, await); return; case DEBUGGER: debuggerStatement(); return; case RPAREN: case RBRACKET: case EOF: expect(SEMICOLON); return; case FUNCTION: // As per spec (ECMA section 12), function declarations as arbitrary statement // is not "portable". Implementation can issue a warning or disallow the same. if (singleStatement) { // ES6 B.3.2 Labelled Function Declarations // It is a Syntax Error if any strict mode source code matches this rule: // LabelledItem : FunctionDeclaration. // ES6 B.3.4 FunctionDeclarations in IfStatement Statement Clauses if (isStrictMode || !mayBeFunctionDeclaration) { throw error(AbstractParser.message(MSG_EXPECTED_STMT, CONTEXT_FUNCTION_DECLARATION), token); } } functionDeclaration(true, topLevel || labelledStatement, singleStatement, yield, await, false); return; case LET: if (useBlockScope()) { TokenType lookahead = lookaheadOfLetDeclaration(); if (lookahead != null) { // lookahead is let declaration if (singleStatement) { // ExpressionStatement should not have "let [" in its lookahead. // The IDENT check is not needed here - the only purpose of this // shortcut is to produce the same error mesage as Nashorn. if (lookahead == LBRACKET || T(k + 1) == IDENT) { throw error(AbstractParser.message(MSG_EXPECTED_STMT, CONTEXT_LET_DECLARATION), token); } // else break and call expressionStatement() } else { variableStatement(type, yield, await); return; } } } break; case CONST: if (useBlockScope()) { if (singleStatement) { throw error(AbstractParser.message(MSG_EXPECTED_STMT, CONTEXT_CONST_DECLARATION), token); } variableStatement(type, yield, await); return; } else if (env.constAsVar) { variableStatement(TokenType.VAR, yield, await); return; } break; case CLASS: case AT: if (ES6_CLASS && isES6()) { if (singleStatement) { throw error(AbstractParser.message(MSG_EXPECTED_STMT, CONTEXT_CLASS_DECLARATION), token); } classDeclaration(yield, await, false); return; } break; case ASYNC: if (isAsync() && lookaheadIsAsyncFunction()) { if (singleStatement) { throw error(AbstractParser.message(MSG_EXPECTED_STMT, CONTEXT_ASYNC_FUNCTION_DECLARATION), token); } asyncFunctionDeclaration(true, topLevel || labelledStatement, yield, await, false); return; } break; default: break; } if (isBindingIdentifier()) { if (T(k + 1) == COLON && (type != YIELD || !yield) && (!isAwait() || !await)) { labelStatement(yield, await, mayBeLabeledFunctionDeclaration); return; } if (reparseFlags != 0 && reparseFunctionStatement(reparseFlags)) { return; } } expressionStatement(yield, await); } private boolean reparseFunctionStatement(final int reparseFlags) { final boolean allowPropertyFunction = (reparseFlags & REPARSE_IS_PROPERTY_ACCESSOR) != 0; final boolean isES6Method = (reparseFlags & REPARSE_IS_METHOD) != 0; if (allowPropertyFunction) { final long propertyToken = token; final int propertyLine = line; if (type == GET) { next(); addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine, false, false, false)); return true; } else if (type == SET) { next(); addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine, false, false, false)); return true; } } else if (isES6Method) { final TruffleString ident = (TruffleString) getValue(); IdentNode identNode = createIdentNode(token, finish, ident).setIsPropertyName(); final long propertyToken = token; final int propertyLine = line; next(); final int flags = CONSTRUCTOR_NAME_TS.equals(ident) ? FunctionNode.IS_CLASS_CONSTRUCTOR : FunctionNode.IS_METHOD; addPropertyFunctionStatement(propertyMethodFunction(identNode, propertyToken, propertyLine, false, flags, false, false)); return true; } return false; } private void addPropertyFunctionStatement(final PropertyFunction propertyFunction) { final FunctionNode fn = propertyFunction.functionNode; functionDeclarations.add(new ExpressionStatement(fn.getLineNumber(), fn.getToken(), finish, fn)); } /** * Parse ClassDeclaration. * *
     * ClassDeclaration[Yield, Await, Default] :
     *   DecoratorList[?Yield, ?Await]opt class BindingIdentifier[?Yield, ?Await] ClassTail[?Yield, ?Await]
     *   [+Default] DecoratorList[?Yield, ?Await]opt class ClassTail[?Yield, ?Await]
     * 
* * @return Class expression node. */ private ClassNode classDeclaration(boolean yield, boolean await, boolean defaultExport) { assert type == CLASS || type == AT; List classDecorators = null; if (type == AT) { assert isClassDecorators(); classDecorators = decoratorList(yield, await); } int classLineNumber = line; long classToken = token; next(); boolean oldStrictMode = isStrictMode; isStrictMode = true; try { IdentNode className = null; if (!defaultExport || isBindingIdentifier()) { className = bindingIdentifier(yield, await, CONTEXT_CLASS_NAME); } ClassNode classExpression = classTail(classLineNumber, classToken, className, yield, await, classDecorators); if (!defaultExport) { VarNode classVar = new VarNode(classLineNumber, Token.recast(classExpression.getToken(), LET), classExpression.getFinish(), className, classExpression, VarNode.IS_LET); appendStatement(classVar); declareVar(lc.getCurrentScope(), classVar); } return classExpression; } finally { isStrictMode = oldStrictMode; } } /** * Parse ClassExpression. * *
     * ClassExpression[Yield, Await] :
     *   DecoratorList[?Yield, ?Await]opt class BindingIdentifier[?Yield, ?Await]opt ClassTail[?Yield, ?Await]
     * 
* * @return Class expression node. */ private ClassNode classExpression(boolean yield, boolean await) { assert type == CLASS || type == AT; List classDecorators = null; if (type == AT) { assert isClassDecorators(); classDecorators = decoratorList(yield, await); } int classLineNumber = line; long classToken = token; next(); boolean oldStrictMode = isStrictMode; isStrictMode = true; try { IdentNode className = null; if (isBindingIdentifier()) { className = bindingIdentifier(yield, await, CONTEXT_CLASS_NAME); } return classTail(classLineNumber, classToken, className, yield, await, classDecorators); } finally { isStrictMode = oldStrictMode; } } /** * Parse ClassTail and ClassBody. * *
     * ClassTail[Yield] : ClassHeritage[?Yield]opt { ClassBody[?Yield]opt } ClassHeritage[Yield] :
     * extends LeftHandSideExpression[?Yield]
     *
     * ClassBody[Yield] :
     *      ClassElementList[?Yield]
     * ClassElementList[Yield] :
     *      ClassElement[?Yield]
     *      ClassElementList[?Yield] ClassElement[?Yield]
     * ClassElement[Yield] :
     *      DecoratorList[?Yield, ?Await]opt MethodDefinition[?Yield]
     *      DecoratorList[?Yield, ?Await]opt static MethodDefinition[?Yield]
     *      DecoratorList[?Yield, ?Await]opt FieldDefinition[?Yield]
     *      DecoratorList[?Yield, ?Await]opt static FieldDefinition[?Yield]
     *      ;
     * 
*/ private ClassNode classTail(int classLineNumber, long classToken, IdentNode className, boolean yield, boolean await, List classDecorators) { assert isStrictMode; Scope classHeadScope = Scope.createClassHead(lc.getCurrentScope()); if (className != null) { classHeadScope.putSymbol(new Symbol(className.getNameTS(), Symbol.IS_CONST)); } ParserContextClassNode classNode = new ParserContextClassNode(classHeadScope); lc.push(classNode); try { Expression classHeritage = null; if (type == EXTENDS) { next(); classHeritage = leftHandSideExpression(yield, await, CoverExpressionError.DENY); // Note: ClassHeritage should be parsed in the outer PrivateEnvironment. // We have only one scope for both classScope and classPrivateEnvironment, so we // emulate this by verifying the private identifiers before any private fields are // declared. Unresolved uses are reported as errors or pushed up to the outer class. IdentNode invalidPrivateIdent = classNode.verifyAllPrivateIdentifiersValid(lc); if (invalidPrivateIdent != null) { throw error(AbstractParser.message(MSG_INVALID_PRIVATE_IDENT), invalidPrivateIdent.getToken()); } } expect(LBRACE); Scope classScope = Scope.createClassBody(classHeadScope); classNode.setScope(classScope); ClassElement constructor = null; List classElements = new ArrayList<>(); Map privateNameToAccessorIndexMap = new HashMap<>(); int staticElementCount = 0; boolean hasPrivateMethods = false; boolean hasPrivateInstanceMethods = false; boolean hasClassElementDecorators = false; boolean hasInstanceFieldsOrAccessors = false; for (;;) { if (type == SEMICOLON) { next(); continue; } if (type == RBRACE) { break; } List classElementDecorators = null; if (type == AT) { classElementDecorators = decoratorList(yield, await); hasClassElementDecorators = true; } boolean isAutoAccessor = false; if (isClassDecorators() && type == ACCESSOR) { TokenType nextToken = lookaheadNoLineTerminator(); if (nextToken != LPAREN && nextToken != ASSIGN && nextToken != SEMICOLON && nextToken != RBRACE && nextToken != EOL) { isAutoAccessor = true; next(); } // else, this is a method/field named 'accessor' } boolean isStatic = false; if (type == STATIC) { TokenType nextToken = lookahead(); if (isClassDecorators() && nextToken == ACCESSOR) { next(); nextToken = lookaheadNoLineTerminator(); if (nextToken != LPAREN && nextToken != ASSIGN && nextToken != SEMICOLON && nextToken != RBRACE && nextToken != EOL) { // 'static accessor identifier|exp|...' isStatic = true; isAutoAccessor = true; next(); } else { // 'static accessor = 43', 'static accessor \n', ... isStatic = true; } } else if (nextToken != LPAREN && nextToken != ASSIGN && nextToken != SEMICOLON && nextToken != RBRACE) { isStatic = true; int staticLine = line; long staticToken = token; next(); if (type == LBRACE && isES2022()) { // static initialization block if (classElementDecorators != null && classElementDecorators.size() != 0) { // '@decorated static { ... }' throw error(AbstractParser.message(MSG_DECORATED_STATIC_BLOCK), staticToken); } ClassElement staticInit = staticInitializer(staticLine, staticToken); staticElementCount++; classElements.add(staticInit); continue; } } // else method/field named 'static' } long classElementToken = token; int classElementLine = line; boolean async = false; if (isAsync() && lookaheadIsAsyncMethod(true)) { async = true; next(); } boolean generator = false; if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) { generator = true; next(); } final TokenType nameTokenType = type; final boolean computed = nameTokenType == LBRACKET; final Expression classElementName = classElementName(yield, await, true); ClassElement classElement; if (!generator && !async && isClassFieldDefinition(nameTokenType)) { classElement = fieldDefinition(classElementName, isStatic, isAutoAccessor, classElementToken, computed, classElementDecorators); if (!isStatic) { hasInstanceFieldsOrAccessors = true; } } else { if (isAutoAccessor) { throw error(AbstractParser.message(MSG_AUTO_ACCESSOR_NOT_FIELD)); } classElement = methodDefinition(classElementName, isStatic, classHeritage != null, generator, async, classElementToken, classElementLine, yield, await, nameTokenType, computed, classElementDecorators); if (!classElement.isComputed() && classElement.isAccessor()) { if (classElement.isPrivate()) { // merge private accessor methods String privateName = classElement.getPrivateName(); Integer existing = privateNameToAccessorIndexMap.get(privateName); if (existing == null) { privateNameToAccessorIndexMap.put(privateName, classElements.size()); } else { ClassElement otherAccessor = classElements.get(existing); if (isStatic == otherAccessor.isStatic()) { if (otherAccessor.getGetter() == null && classElement.getGetter() != null) { classElements.set(existing, otherAccessor.setGetter(classElement.getGetter())); continue; } else if (otherAccessor.getSetter() == null && classElement.getSetter() != null) { classElements.set(existing, otherAccessor.setSetter(classElement.getSetter())); continue; } } // else: more than one getter or setter with the same private name // fall through: a syntax error will be thrown below } } else if (!classElements.isEmpty()) { // try to merge consecutive getter and setter pairs if they are not // decorated. ClassElement lastElement = classElements.get(classElements.size() - 1); if ((classElement.getDecorators() == null && lastElement.getDecorators() == null) && !lastElement.isComputed() && lastElement.isAccessor() && isStatic == lastElement.isStatic() && !lastElement.isPrivate() && classElement.getKeyName().equals(lastElement.getKeyName())) { ClassElement merged = classElement.getGetter() != null ? lastElement.setGetter(classElement.getGetter()) : lastElement.setSetter(classElement.getSetter()); classElements.set(classElements.size() - 1, merged); continue; } } } } if (classElement.isPrivate()) { hasPrivateMethods = hasPrivateMethods || classElement.isMethodOrAccessor(); hasPrivateInstanceMethods = hasPrivateInstanceMethods || (!classElement.isClassField() && !classElement.isStatic()); declarePrivateName(classScope, classElement); } if (!classElement.isStatic() && !classElement.isComputed() && classElement.getKeyNameTS().equals(CONSTRUCTOR_NAME_TS)) { assert !classElement.isClassField(); if (constructor == null) { if (classElement.getDecorators() != null && classElement.getDecorators().size() > 0) { throw error(AbstractParser.message(MSG_DECORATED_CONSTRUCTOR)); } constructor = classElement; } else { throw error(AbstractParser.message(MSG_MULTIPLE_CONSTRUCTORS), classElementToken); } } else { classElements.add(classElement); if (isStatic) { staticElementCount++; } } } long lastToken = token; expect(RBRACE); int classFinish = Token.descPosition(lastToken) + Token.descLength(lastToken); if (constructor == null) { constructor = createDefaultClassConstructor(classLineNumber, classToken, lastToken, className, classHeritage != null); } else { // constructor takes on source section of class declaration/expression FunctionNode ctor = (FunctionNode) constructor.getValue(); int flags = ctor.getFlags(); if (className == null) { flags |= FunctionNode.IS_ANONYMOUS; } constructor = constructor.setValue(new FunctionNode(ctor.getSource(), ctor.getLineNumber(), ctor.getToken(), classFinish, classToken, lastToken, className, className == null ? TruffleString.Encoding.UTF_16.getEmpty() : className.getNameTS(), ctor.getLength(), ctor.getNumOfParams(), ctor.getParameters(), flags, ctor.getBody(), ctor.getEndParserState(), ctor.getModule(), ctor.getInternalNameTS())); } IdentNode invalidPrivateIdent = classNode.verifyAllPrivateIdentifiersValid(lc); if (invalidPrivateIdent != null) { throw error(AbstractParser.message(MSG_INVALID_PRIVATE_IDENT), invalidPrivateIdent.getToken()); } if (hasPrivateMethods) { // synthetic binding providing access to the constructor for private brand checks. classScope.putSymbol(new Symbol(lexer.stringIntern(ClassNode.PRIVATE_CONSTRUCTOR_BINDING_NAME), Symbol.IS_CONST | Symbol.IS_PRIVATE_NAME | Symbol.HAS_BEEN_DECLARED)); } classScope.close(); classHeadScope.close(); return new ClassNode(classToken, classFinish, className, classHeritage, constructor, classElements, classDecorators, classScope, staticElementCount, hasPrivateMethods, hasPrivateInstanceMethods, hasInstanceFieldsOrAccessors, hasClassElementDecorators); } finally { lc.pop(classNode); } } private Expression classElementName(boolean yield, boolean await, boolean allowPrivate) { if (allowPrivate && type == TokenType.PRIVATE_IDENT) { return privateIdentifierDeclaration(); } return propertyName(yield, await); } private IdentNode parsePrivateIdentifier() { assert type == TokenType.PRIVATE_IDENT; if (!isClassFields() && !isES2021()) { throw error(AbstractParser.message(MSG_UNEXPECTED_TOKEN, type.getNameOrType())); } final long identToken = token; final TruffleString name = (TruffleString) getValue(identToken); next(); return createIdentNode(identToken, finish, name).setIsPrivate(); } private IdentNode privateIdentifierDeclaration() { IdentNode privateIdent = parsePrivateIdentifier(); ParserContextClassNode currentClass = lc.getCurrentClass(); if (currentClass == null) { throw error(AbstractParser.message(MSG_INVALID_PRIVATE_IDENT), privateIdent.getToken()); } return privateIdent; } private void declarePrivateName(Scope classScope, ClassElement classElement) { // Syntax Error if PrivateBoundIdentifiers of ClassBody contains any duplicate entries, // unless the name is used once for a getter and once for a setter and in no other entries. int privateFlags = (classElement.isStatic() ? Symbol.IS_PRIVATE_NAME_STATIC : 0) | (classElement.isMethod() ? Symbol.IS_PRIVATE_NAME_METHOD : 0) | ((classElement.isAccessor() || classElement.isAutoAccessor()) ? Symbol.IS_PRIVATE_NAME_ACCESSOR : 0); if (!classScope.addPrivateName(classElement.getPrivateNameTS(), privateFlags)) { throw error(ECMAErrors.getMessage(MSG_SYNTAX_ERROR_REDECLARE_VARIABLE, classElement.getPrivateName()), classElement.getKey().getToken()); } } private IdentNode privateIdentifierUse() { IdentNode privateIdent = parsePrivateIdentifier(); Scope currentScope = lc.getCurrentScope(); ParserContextClassNode currentClass = lc.getCurrentClass(); // In a class: try to eagerly resolve the private identifier; if it is not found, // defer resolving until the end of the class declaration. // In a direct eval: try to find a resolved private identifier in the caller scopes. if (currentClass != null) { currentClass.usePrivateName(privateIdent); } else { if (!currentScope.findPrivateName(privateIdent.getName())) { throw error(AbstractParser.message(MSG_INVALID_PRIVATE_IDENT), privateIdent.getToken()); } } currentScope.addIdentifierReference(privateIdent.getName()); return privateIdent; } private boolean isClassFieldDefinition(final TokenType nameTokenType) { if (!isClassFields()) { return false; } switch (type) { case ASSIGN: case SEMICOLON: case RBRACE: // must be a field return true; case LPAREN: // must be a method return false; default: if (nameTokenType == GET || nameTokenType == SET) { // `get` or `set` not followed by `;`, `=`, or `}`, must be an accessor method return false; } // not a method, either a field or syntax error if (last == EOL) { // field (automatic semicolon insertion) return true; } else { // syntax error return false; } } } private ClassElement createDefaultClassConstructor(int classLineNumber, long classToken, long lastToken, IdentNode className, boolean derived) { final int ctorFinish = finish; final List statements; final List parameters; final long identToken = Token.recast(classToken, TokenType.IDENT); if (derived) { IdentNode superIdent = new IdentNode(identToken, ctorFinish, lexer.stringIntern(SUPER.getNameTS())).setIsDirectSuper(); IdentNode argsIdent = new IdentNode(identToken, ctorFinish, lexer.stringIntern(ARGS)).setIsRestParameter(); Expression spreadArgs = new UnaryNode(Token.recast(classToken, TokenType.SPREAD_ARGUMENT), argsIdent); Expression superCall = CallNode.forCall(classLineNumber, classToken, Token.descPosition(classToken), ctorFinish, superIdent, List.of(spreadArgs), false, false, false, false, true); statements = List.of(new ExpressionStatement(classLineNumber, classToken, ctorFinish, superCall)); parameters = List.of(argsIdent); } else { statements = List.of(); parameters = List.of(); } int functionFlags = FunctionNode.IS_METHOD | FunctionNode.IS_CLASS_CONSTRUCTOR; ParserContextFunctionNode function = createParserContextFunctionNode(className, classToken, functionFlags, classLineNumber, parameters, 0); function.setLastToken(lastToken); Scope scope = function.createBodyScope(); scope.close(); Block body = new Block(classToken, ctorFinish, Block.IS_BODY, scope, statements); if (derived) { function.setFlag(FunctionNode.IS_DERIVED_CONSTRUCTOR); function.setFlag(FunctionNode.HAS_DIRECT_SUPER); } if (className == null) { function.setFlag(FunctionNode.IS_ANONYMOUS); function.setInternalName(lexer.stringIntern(CONSTRUCTOR_NAME_TS)); } // currently required for all functions, including synthetic ones. lc.setCurrentFunctionFlag(FunctionNode.HAS_CLOSURES); return ClassElement.createDefaultConstructor(classToken, ctorFinish, new IdentNode(identToken, ctorFinish, CONSTRUCTOR_NAME_TS), createFunctionNode(function, classToken, className, classLineNumber, body)); } private ClassElement methodDefinition(Expression propertyName, boolean isStatic, boolean derived, boolean generator, boolean async, long startToken, int methodLine, boolean yield, boolean await, TokenType nameTokenType, boolean computed, List classElementDecorators) { int flags = FunctionNode.IS_METHOD; boolean isPrivate = false; if (!computed) { final String name = ((PropertyKey) propertyName).getPropertyName(); if (!generator && nameTokenType == GET && type != LPAREN) { PropertyFunction methodDefinition = propertyGetterFunction(startToken, methodLine, yield, await, true); verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true, async); return ClassElement.createAccessor(startToken, finish, methodDefinition.key, methodDefinition.functionNode, null, classElementDecorators, isPrivate, isStatic, methodDefinition.computed); } else if (!generator && nameTokenType == SET && type != LPAREN) { PropertyFunction methodDefinition = propertySetterFunction(startToken, methodLine, yield, await, true); verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true, async); return ClassElement.createAccessor(startToken, finish, methodDefinition.key, null, methodDefinition.functionNode, classElementDecorators, isPrivate, isStatic, methodDefinition.computed); } else { if (!isStatic && !generator && name.equals(CONSTRUCTOR_NAME)) { flags |= FunctionNode.IS_CLASS_CONSTRUCTOR; if (derived) { flags |= FunctionNode.IS_DERIVED_CONSTRUCTOR; } } verifyAllowedMethodName(propertyName, isStatic, computed, generator, false, async); } } PropertyFunction methodDefinition = propertyMethodFunction(propertyName, startToken, methodLine, generator, flags, computed, async); return ClassElement.createMethod(startToken, finish, methodDefinition.key, methodDefinition.functionNode, classElementDecorators, isStatic, computed); } /** * ES6 14.5.1 Static Semantics: Early Errors. */ private void verifyAllowedMethodName(Expression key, boolean isStatic, boolean computed, boolean generator, boolean accessor, boolean async) { if (!computed) { final String name = ((PropertyKey) key).getPropertyName(); if (!isStatic && generator && name.equals(CONSTRUCTOR_NAME)) { throw error(AbstractParser.message(MSG_GENERATOR_CONSTRUCTOR), key.getToken()); } if (!isStatic && accessor && name.equals(CONSTRUCTOR_NAME)) { throw error(AbstractParser.message(MSG_ACCESSOR_CONSTRUCTOR), key.getToken()); } if (!isStatic && async && name.equals(CONSTRUCTOR_NAME)) { throw error(AbstractParser.message(MSG_ASYNC_CONSTRUCTOR), key.getToken()); } if (isStatic && name.equals(PROTOTYPE_NAME)) { throw error(AbstractParser.message(MSG_STATIC_PROTOTYPE_METHOD), key.getToken()); } if (name.equals(PRIVATE_CONSTRUCTOR_NAME)) { throw error(AbstractParser.message(MSG_PRIVATE_CONSTRUCTOR_METHOD), key.getToken()); } } } private ClassElement fieldDefinition(Expression propertyName, boolean isStatic, boolean isAutoAccessor, long startToken, boolean computed, List classElementDecorators) { // "constructor" or #constructor is not allowed as an instance field name if (!computed && propertyName instanceof PropertyKey) { String name = ((PropertyKey) propertyName).getPropertyName(); if (CONSTRUCTOR_NAME.equals(name) || PRIVATE_CONSTRUCTOR_NAME.equals(name)) { throw error(AbstractParser.message(MSG_CONSTRUCTOR_FIELD), startToken); } if (isStatic && PROTOTYPE_NAME.equals(name)) { throw error(AbstractParser.message(MSG_STATIC_PROTOTYPE_FIELD), startToken); } } FunctionNode initializer = null; boolean isAnonymousFunctionDefinition = false; if (type == ASSIGN) { next(); // Parse AssignmentExpression[In] in a function. Pair pair = fieldInitializer(line, startToken, propertyName, computed); initializer = pair.getLeft(); isAnonymousFunctionDefinition = pair.getRight(); endOfLine(); // semicolon or end of line } if (isAutoAccessor) { return ClassElement.createAutoAccessor(startToken, finish, propertyName, initializer, classElementDecorators, isStatic, computed, isAnonymousFunctionDefinition); } else { return ClassElement.createField(startToken, finish, propertyName, initializer, classElementDecorators, isStatic, computed, isAnonymousFunctionDefinition); } } private Pair fieldInitializer(int lineNumber, long fieldToken, Expression propertyName, boolean computed) { int functionFlags = FunctionNode.IS_METHOD | FunctionNode.IS_CLASS_FIELD_INITIALIZER | FunctionNode.IS_ANONYMOUS; ParserContextFunctionNode function = createParserContextFunctionNode(null, fieldToken, functionFlags, lineNumber, List.of(), 0); function.setInternalName(lexer.stringIntern(INITIALIZER_FUNCTION_NAME)); lc.push(function); ParserContextBlockNode body; Expression initializer; try { body = newBlock(function.createBodyScope()); try { initializer = assignmentExpression(true, false, false); function.finishBodyScope(lexer); } finally { restoreBlock(body); } lc.propagateFunctionFlags(); } finally { lc.pop(function); } // It is a Syntax Error if ContainsArguments of Initializer is true. assert function.getFlag(FunctionNode.USES_ARGUMENTS) == 0; function.setLastToken(token); boolean isAnonymousFunctionDefinition = false; if (isAnonymousFunctionDefinition(initializer)) { if (!computed && propertyName instanceof PropertyKey) { initializer = setAnonymousFunctionName(initializer, ((PropertyKey) propertyName).getPropertyNameTS()); } else { isAnonymousFunctionDefinition = true; initializer = new UnaryNode(Token.recast(initializer.getToken(), TokenType.NAMEDEVALUATION), initializer); } } // currently required for all functions, including synthetic ones. lc.setCurrentFunctionFlag(FunctionNode.HAS_CLOSURES); final List statements = List.of(new ReturnNode(lineNumber, fieldToken, finish, initializer)); Block bodyBlock = new Block(fieldToken, finish, Block.IS_BODY | Block.IS_SYNTHETIC, body.getScope(), statements); return Pair.create(createFunctionNode(function, fieldToken, null, lineNumber, bodyBlock), isAnonymousFunctionDefinition); } /** * Parse { ClassStaticBlockBody }. */ private ClassElement staticInitializer(int lineNumber, long staticToken) { assert type == LBRACE; int functionFlags = FunctionNode.IS_METHOD | FunctionNode.IS_CLASS_FIELD_INITIALIZER | FunctionNode.IS_ANONYMOUS; ParserContextFunctionNode function = createParserContextFunctionNode(null, staticToken, functionFlags, lineNumber, List.of(), 0); function.setInternalName(lexer.stringIntern(INITIALIZER_FUNCTION_NAME)); lc.push(function); Block bodyBlock; try { bodyBlock = functionBody(function); } finally { lc.pop(function); } // It is a Syntax Error if ContainsArguments of Initializer is true. assert function.getFlag(FunctionNode.USES_ARGUMENTS) == 0; // currently required for all nested functions. lc.setCurrentFunctionFlag(FunctionNode.HAS_CLOSURES); FunctionNode functionNode = createFunctionNode(function, staticToken, null, lineNumber, bodyBlock); return ClassElement.createStaticInitializer(staticToken, finish, functionNode); } private boolean isPropertyName(long currentToken) { TokenType currentType = Token.descType(currentToken); if (ES6_COMPUTED_PROPERTY_NAME && currentType == LBRACKET && isES6()) { // computed property return true; } switch (currentType) { case IDENT: return true; case NON_OCTAL_DECIMAL: case OCTAL_LEGACY: if (isStrictMode) { return false; } case STRING: case ESCSTRING: case DECIMAL: case HEXADECIMAL: case OCTAL: case BINARY_NUMBER: case BIGINT: case FLOATING: return true; default: return isIdentifierName(currentToken); } } /** * Parse a statement block. * *
     * Block :
     *      { StatementList? }
     * 
*/ private void block(boolean yield, boolean await) { appendStatement(new BlockStatement(line, getBlock(yield, await, true))); } /** * Parse a list of statements. * *
     * StatementList :
     *      Statement
     *      StatementList Statement
     * 
*/ private void statementList(boolean yield, boolean await) { // Accumulate statements until end of the statement list. loop: while (type != EOF) { switch (type) { case EOF: case CASE: case DEFAULT: case RBRACE: break loop; default: break; } // Get next statement. statement(yield, await); } } /** * Make sure that the identifier name used is allowed. * * @param ident the identifier that is verified */ private void verifyIdent(final IdentNode ident, final boolean yield, final boolean await) { // It is a Syntax Error if StringValue of IdentifierName is the same as the StringValue of // any ReservedWord except for yield or await. if (isES6()) { if (isEscapedIdent(ident) && isReservedWordSequence(ident.getName())) { throw error(AbstractParser.message(MSG_ESCAPED_KEYWORD, ident), ident.getToken()); } else { assert !isReservedWordSequence(ident.getName()) : ident.getName(); } } // It is a Syntax Error if this production has a [Yield] parameter and StringValue of // Identifier is "yield". if (yield) { if (ident.isTokenType(YIELD)) { throw error(expectMessage(IDENT, ident.getToken()), ident.getToken()); } else if (isEscapedIdent(ident) && YIELD.getName().equals(ident.getName())) { throw error(AbstractParser.message(MSG_ESCAPED_KEYWORD, ident), ident.getToken()); } else { assert !YIELD.getName().equals(ident.getName()); } } // It is a Syntax Error if this production has an [Await] parameter or if the goal symbol // of the syntactic grammar is Module and StringValue of Identifier is "await". // If we are inside CoverCallExpressionAndAsyncArrowHead, record the token for later // error handling: "await" is not a valid identifier iff it occurs inside AsyncArrowHead. boolean awaitOrModule = await || isModule; if (ident.isTokenType(AWAIT)) { if (awaitOrModule) { throw error(expectMessage(IDENT, ident.getToken()), ident.getToken()); } else { recordYieldOrAwait(ident); } } else if (isEscapedIdent(ident) && AWAIT.getName().equals(ident.getName())) { if (awaitOrModule) { throw error(AbstractParser.message(MSG_ESCAPED_KEYWORD, ident), ident.getToken()); } else { recordYieldOrAwait(ident); } } else { assert !AWAIT.getName().equals(ident.getName()); } } private static boolean isEscapedIdent(final IdentNode ident) { return ident.getName().length() != Token.descLength(ident.getToken()); } private static boolean isReservedWordSequence(final String name) { TokenType tokenType = TokenLookup.lookupKeyword(name, 0, name.length()); return (tokenType != IDENT && !tokenType.isContextualKeyword() && !tokenType.isFutureStrict()); } /** * Make sure that in strict mode, the identifier name used is allowed. * * @param ident Identifier that is verified * @param contextString String used in error message to give context to the user */ private void verifyStrictIdent(final IdentNode ident, final String contextString, final boolean bindingIdentifier) { if (isStrictMode) { if (!isValidStrictIdent(ident, bindingIdentifier)) { throw error(AbstractParser.message(MSG_STRICT_NAME, ident.getName(), contextString), ident.getToken()); } } } private void verifyStrictIdent(final IdentNode ident, final String contextString) { verifyStrictIdent(ident, contextString, true); } private static boolean isValidStrictIdent(final IdentNode ident, final boolean bindingIdentifier) { if (bindingIdentifier) { if (EVAL_NAME.equals(ident.getName()) || ARGUMENTS_NAME.equals(ident.getName())) { return false; } } return !isFutureStrictName(ident); } /** * Check if this IdentNode is a future strict name * * @return true if this is a future strict name */ private static boolean isFutureStrictName(final IdentNode ident) { if (ident.tokenType().isFutureStrict()) { return true; } else if (isEscapedIdent(ident)) { TokenType tokenType = TokenLookup.lookupKeyword(ident.getName(), 0, ident.getName().length()); return (tokenType != IDENT && tokenType.isFutureStrict()); } return false; } /** * Parse a var statement. * *
     * VariableStatement :
     *      var VariableDeclarationList ;
     *
     * VariableDeclarationList :
     *      VariableDeclaration
     *      VariableDeclarationList , VariableDeclaration
     *
     * VariableDeclaration :
     *      Identifier Initializer?
     *
     * Initializer :
     *      = AssignmentExpression
     * 
*/ private void variableStatement(final TokenType varType, boolean yield, boolean await) { variableDeclarationList(varType, true, yield, await, -1); } private static final class ForVariableDeclarationListResult { /** First missing const or binding pattern initializer. */ Expression missingAssignment; /** First declaration with an initializer. */ long declarationWithInitializerToken; /** First binding identifier or pattern. */ Expression firstBinding; /** Second binding identifier or pattern for error reporting when only one is allowed. */ Expression secondBinding; void recordMissingAssignment(Expression binding) { if (missingAssignment == null) { missingAssignment = binding; } } void recordDeclarationWithInitializer(long token) { if (declarationWithInitializerToken == 0L) { declarationWithInitializerToken = token; } } void addBinding(Expression binding) { if (firstBinding == null) { firstBinding = binding; } else if (secondBinding == null) { secondBinding = binding; } // ignore the rest } } /** * Parse VariableDeclarationList[In, Yield, Await]. * * @param isStatement Same as In flag; {@code true} if a VariableStatement, {@code false} if a * {@code for} loop VariableDeclarationList */ private ForVariableDeclarationListResult variableDeclarationList(TokenType varType, boolean isStatement, boolean yield, boolean await, int sourceOrder) { // VAR tested in caller. int varStart = Token.descPosition(token); assert varType == VAR || varType == LET || varType == CONST; next(); int varFlags = 0; if (varType == LET) { varFlags |= VarNode.IS_LET; } else if (varType == CONST) { varFlags |= VarNode.IS_CONST; } ForVariableDeclarationListResult forResult = isStatement ? null : new ForVariableDeclarationListResult(); Scope scope = lc.getCurrentScope(); while (true) { // Get starting token. final int varLine = line; final long varToken = Token.recast(token, varType); // Get name of var. final Expression binding = bindingIdentifierOrPattern(yield, await, CONTEXT_VARIABLE_NAME); final boolean isDestructuring = !(binding instanceof IdentNode); if (isDestructuring) { final int finalVarFlags = varFlags | VarNode.IS_DESTRUCTURING; verifyDestructuringBindingPattern(binding, new Consumer() { @Override public void accept(IdentNode identNode) { verifyStrictIdent(identNode, CONTEXT_VARIABLE_NAME); if (varType != VAR && identNode.getName().equals(LET.getName())) { // ES8 13.3.1.1 throw error(AbstractParser.message(MSG_LET_LEXICAL_BINDING)); } final VarNode var = new VarNode(varLine, varToken, sourceOrder, identNode.getFinish(), identNode.setIsDeclaredHere(), null, finalVarFlags); appendStatement(var); declareVar(scope, var); } }); } // Assume no init. Expression init = null; // Look for initializer assignment. if (type == ASSIGN) { if (!isStatement) { forResult.recordDeclarationWithInitializer(varToken); } next(); // Get initializer expression. Suppress IN if not statement. if (!isDestructuring) { pushDefaultName(binding); } try { init = assignmentExpression(isStatement, yield, await); } finally { if (!isDestructuring) { popDefaultName(); } } } else if (isStatement) { if (isDestructuring) { throw error(AbstractParser.message(MSG_MISSING_DESTRUCTURING_ASSIGNMENT), token); } else if (varType == CONST) { throw error(AbstractParser.message(MSG_MISSING_CONST_ASSIGNMENT, ((IdentNode) binding).getName())); } // else, if we are in a for loop, delay checking until we know the kind of loop } if (!isDestructuring) { assert init != null || varType != CONST || !isStatement; final IdentNode ident = (IdentNode) binding; if (varType != VAR && ident.getName().equals(LET.getName())) { throw error(AbstractParser.message(MSG_LET_LEXICAL_BINDING)); // ES8 13.3.1.1 } if (!isStatement) { if (init == null && varType == CONST) { forResult.recordMissingAssignment(binding); } forResult.addBinding(binding); } if (isAnonymousFunctionDefinition(init)) { init = setAnonymousFunctionName(init, ident.getNameTS()); } final VarNode var = new VarNode(varLine, varToken, sourceOrder, varStart, finish, ident.setIsDeclaredHere(), init, varFlags); appendStatement(var); declareVar(scope, var); } else { assert init != null || !isStatement; if (init != null) { final Expression assignment = verifyAssignment(Token.recast(varToken, ASSIGN_INIT), binding, init, true); appendStatement(new ExpressionStatement(varLine, assignment.getToken(), finish, assignment)); if (!isStatement) { forResult.addBinding(binding); } } else if (!isStatement) { forResult.recordMissingAssignment(binding); forResult.addBinding(binding); } } if (type != COMMARIGHT) { break; } next(); } // If is a statement then handle end of line. if (isStatement) { endOfLine(); } return forResult; } private void declareVar(Scope scope, VarNode varNode) { String name = varNode.getName().getName(); if (detectVarNameConflict(scope, varNode)) { throw error(ECMAErrors.getMessage(MSG_SYNTAX_ERROR_REDECLARE_VARIABLE, name), varNode.getToken()); } if (varNode.isBlockScoped()) { int symbolFlags = varNode.getSymbolFlags() | (scope.isSwitchBlockScope() ? Symbol.IS_DECLARED_IN_SWITCH_BLOCK : 0) | (varNode.isFunctionDeclaration() ? Symbol.IS_BLOCK_FUNCTION_DECLARATION : 0); Symbol existing = scope.putSymbol(new Symbol(varNode.getName().getNameTS(), symbolFlags)); assert existing == null || (existing.isBlockFunctionDeclaration() && varNode.isFunctionDeclaration()) : existing; if (varNode.isFunctionDeclaration() && isAnnexB()) { // Block-Level Function Declaration hoisting. // https://tc39.es/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics // Changes to FunctionDeclarationInstantiation and GlobalDeclarationInstantiation. ParserContextFunctionNode function = lc.getCurrentFunction(); Scope varScope = function.getBodyScope(); if (!function.isStrict() && scope != varScope) { assert !scope.isFunctionBodyScope() && !scope.isFunctionParameterScope(); // If we already find a conflicting declaration, we can skip this step. Symbol existingSymbol = varScope.getExistingSymbol(name); if ((existingSymbol == null || (!existingSymbol.isBlockScoped() && !existingSymbol.isParam())) && !scope.getParent().isLexicallyDeclaredName(name, true, true)) { function.recordHoistableBlockFunctionDeclaration(varNode, scope); } } } } else { // var declarations are added to the function body scope (a.k.a. VariableEnvironment). ParserContextFunctionNode function = lc.getCurrentFunction(); Scope varScope = function.getBodyScope(); int symbolFlags = varNode.getSymbolFlags() | (varNode.isHoistableDeclaration() ? Symbol.IS_HOISTABLE_DECLARATION : 0) | (varScope.isGlobalScope() ? Symbol.IS_GLOBAL : 0); // if the var name appears in a non-simple parameter list, we need to copy its value. if (function.hasParameterExpressions()) { if (function.getParameterScope().hasSymbol(name)) { symbolFlags |= Symbol.IS_VAR_REDECLARED_HERE; } else if (!varNode.isHoistableDeclaration() && !function.isArrow() && isArguments(varNode.getName())) { // `var arguments` implies an implicit arguments binding in the parameter scope // that is copied over to the var scope, except for hoisted var declarations. symbolFlags |= Symbol.IS_VAR_REDECLARED_HERE; } } else if (!varNode.isHoistableDeclaration() && !function.isArrow() && isArguments(varNode.getName())) { // `var arguments;` refers to the arguments object in a normal function context. // A top-level `function arguments() {}` replaces the arguments object, though. symbolFlags |= Symbol.IS_ARGUMENTS; } varScope.putSymbol(new Symbol(varNode.getName().getNameTS(), symbolFlags)); /* * Hoisted var declarations conflict with any lexical declaration of the same name in * this scope and any intermediate scopes. We cannot verify this in advance since we do * not know all the lexically declared names for the outer scopes yet, if any. * * e.g.: `{ { var x; } let x; }` * * So unless we are in the var declaration (a.k.a. top-level) scope, remember that there * has been a declaration in this scope and defer the duplicate check until the var * declaration scope is finalized. */ if (scope != varScope) { assert scope.isBlockScope(); function.recordHoistedVarDeclaration(varNode, scope); } } } private boolean detectVarNameConflict(Scope scope, VarNode varNode) { String varName = varNode.getName().getName(); if (varNode.isBlockScoped()) { Scope currentScope = scope; Symbol existingSymbol = currentScope.getExistingSymbol(varName); if (existingSymbol != null) { // B.3.3.4 Changes to Block Static Semantics: Early Errors // In non-strict mode, allow duplicate function declarations in a block. if (existingSymbol.isBlockFunctionDeclaration() && !isStrictMode && isAnnexB() && varNode.isFunctionDeclaration()) { return false; } else { return true; } } else { Scope parentScope = scope.getParent(); if (parentScope != null && (parentScope.isCatchParameterScope() || parentScope.isFunctionParameterScope())) { existingSymbol = parentScope.getExistingSymbol(varName); if (existingSymbol != null && !existingSymbol.isArguments()) { return true; } } return false; } } else { return scope.isLexicallyDeclaredName(varName, isAnnexB(), false); } } private boolean isAnnexB() { return env.annexB; } private boolean isIdentifier() { return type == IDENT || type.isContextualKeyword() || isNonStrictModeIdent(); } private IdentNode identifier(boolean yield, boolean await, String contextString, boolean bindingIdentifier) { final IdentNode ident = getIdent(); verifyIdent(ident, yield, await); verifyStrictIdent(ident, contextString, bindingIdentifier); return ident; } private IdentNode identifierReference(boolean yield, boolean await) { IdentNode ident = identifier(yield, await, CONTEXT_IDENTIFIER_REFERENCE, false); addIdentifierReference(ident.getName()); return ident; } private IdentNode labelIdentifier(boolean yield, boolean await) { return identifier(yield, await, CONTEXT_LABEL_IDENTIFIER, false); } private boolean isBindingIdentifier() { return type == IDENT || type.isContextualKeyword() || isNonStrictModeIdent(); } private IdentNode bindingIdentifier(boolean yield, boolean await, String contextString) { IdentNode ident = identifier(yield, await, contextString, true); addIdentifierReference(ident.getName()); return ident; } private void addIdentifierReference(String name) { Scope currentScope = lc.getCurrentScope(); currentScope.addIdentifierReference(name); } private Expression bindingPattern(boolean yield, boolean await) { if (type == LBRACKET) { return arrayLiteral(yield, await, CoverExpressionError.IGNORE); } else if (type == LBRACE) { return objectLiteral(yield, await, CoverExpressionError.IGNORE); } else { throw error(AbstractParser.message(MSG_EXPECTED_BINDING)); } } private Expression bindingIdentifierOrPattern(boolean yield, boolean await, String contextString) { if (isBindingIdentifier() || !(ES6_DESTRUCTURING && isES6())) { return bindingIdentifier(yield, await, contextString); } else { return bindingPattern(yield, await); } } private abstract class VerifyDestructuringPatternNodeVisitor extends NodeVisitor { VerifyDestructuringPatternNodeVisitor(LexicalContext lc) { super(lc); } @Override public boolean enterLiteralNode(LiteralNode literalNode) { if (literalNode.isArray()) { if (literalNode.isParenthesized()) { throw error(AbstractParser.message(MSG_INVALID_LVALUE), literalNode.getToken()); } if (((ArrayLiteralNode) literalNode).hasSpread() && ((ArrayLiteralNode) literalNode).hasTrailingComma()) { throw error("Rest element must be last", literalNode.getElementExpressions().get(literalNode.getElementExpressions().size() - 1).getToken()); } boolean restElement = false; for (Expression element : literalNode.getElementExpressions()) { if (element != null) { if (restElement) { throw error("Unexpected element after rest element", element.getToken()); } if (element.isTokenType(SPREAD_ARRAY)) { restElement = true; Expression lvalue = ((UnaryNode) element).getExpression(); verifySpreadElement(lvalue); } else { element.accept(this); } } } return false; } else { return enterDefault(literalNode); } } protected abstract void verifySpreadElement(Expression lvalue); @Override public boolean enterObjectNode(ObjectNode objectNode) { if (objectNode.isParenthesized()) { throw error(AbstractParser.message(MSG_INVALID_LVALUE), objectNode.getToken()); } boolean restElement = false; for (PropertyNode property : objectNode.getElements()) { if (property != null) { if (restElement) { throw error("Unexpected element after rest element", property.getToken()); } Expression key = property.getKey(); if (key.isTokenType(SPREAD_OBJECT)) { restElement = true; Expression lvalue = ((UnaryNode) key).getExpression(); verifySpreadElement(lvalue); } else { property.accept(this); } } } return false; } @Override public boolean enterPropertyNode(PropertyNode propertyNode) { if (propertyNode.getValue() != null) { propertyNode.getValue().accept(this); return false; } else { return enterDefault(propertyNode); } } @Override public boolean enterBinaryNode(BinaryNode binaryNode) { if (binaryNode.isTokenType(ASSIGN)) { binaryNode.getLhs().accept(this); // Initializer(rhs) can be any AssignmentExpression return false; } else { return enterDefault(binaryNode); } } } /** * Verify destructuring variable declaration binding pattern and extract bound variable * declarations. */ private void verifyDestructuringBindingPattern(Expression pattern, Consumer identifierCallback) { assert pattern instanceof ObjectNode || pattern instanceof ArrayLiteralNode; pattern.accept(new VerifyDestructuringPatternNodeVisitor(new LexicalContext()) { @Override protected void verifySpreadElement(Expression lvalue) { if (lvalue instanceof IdentNode) { enterIdentNode((IdentNode) lvalue); } else if (isDestructuringLhs(lvalue)) { verifyDestructuringBindingPattern(lvalue, identifierCallback); } else { throw error("Expected a valid binding identifier", lvalue.getToken()); } } @Override public boolean enterIdentNode(IdentNode identNode) { if (identNode.isParenthesized()) { throw error("Expected a valid binding identifier", identNode.getToken()); } identifierCallback.accept(identNode); return false; } @Override protected boolean enterDefault(Node node) { throw error(String.format("unexpected node in BindingPattern: %s", node)); } }); } /** * Parse an empty statement ({@code ;}). */ private void emptyStatement() { if (env.emptyStatements) { appendStatement(new EmptyNode(line, token, Token.descPosition(token) + Token.descLength(token))); } // SEMICOLON checked in caller. next(); } /** * ExpressionStatement : Expression ; // [lookahead ~( or function )] * * See 12.4 * * Parse an expression used in a statement block. */ private void expressionStatement(boolean yield, boolean await) { // Lookahead checked in caller. final int expressionLine = line; final long expressionToken = token; // Get expression and add as statement. final Expression expression = expression(yield, await); if (expression != null) { endOfLine(); ExpressionStatement expressionStatement = new ExpressionStatement(expressionLine, expressionToken, finish, expression); appendStatement(expressionStatement); } else { expect(null); endOfLine(); } } /** * Parse an if statement. * *
     * IfStatement :
     *      if ( Expression ) Statement else Statement
     *      if ( Expression ) Statement
     * 
*/ private void ifStatement(boolean yield, boolean await) { // Capture IF token. final int ifLine = line; final long ifToken = token; // IF tested in caller. next(); expect(LPAREN); final Expression test = expression(yield, await); expect(RPAREN); final Block pass = getStatement(yield, await, false, true, false); Block fail = null; if (type == ELSE) { next(); fail = getStatement(yield, await, false, true, false); } appendStatement(new IfNode(ifLine, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail)); } /** * Parse a {@code for} IterationStatement. */ private void forStatement(boolean yield, boolean await) { final long forToken = token; final int forLine = line; // start position of this for statement. This is used // for sort order for variables declared in the initializer // part of this 'for' statement (if any). final int forStart = Token.descPosition(forToken); // When ES6 for-let is enabled we create a container block to capture the LET. ParserContextBlockNode outer; if (useBlockScope()) { outer = newBlock(newBlockScope()); outer.setFlag(Block.IS_SYNTHETIC); } else { outer = null; } // Create FOR node, capturing FOR token. final ParserContextLoopNode forNode = new ParserContextLoopNode(); lc.push(forNode); Block body; Expression init = null; JoinPredecessorExpression test = null; JoinPredecessorExpression modify = null; ForVariableDeclarationListResult varDeclList = null; CoverExpressionError initCoverExpr = CoverExpressionError.DENY; int flags = 0; boolean isForOf = false; boolean isForAwaitOf = false; // used for lookahead != let checks in "for (await) of" grammar boolean initStartsWithLet = false; // used for "lookahead != async of" check in "for of" grammar boolean initStartsWithAsyncOf = false; try { try { // FOR tested in caller. next(); // Nashorn extension: for each expression. // iterate property values rather than property names. if (env.syntaxExtensions && type == IDENT && lexer.checkIdentForKeyword(token, "each")) { flags |= ForNode.IS_FOR_EACH; next(); } else if (ES8_FOR_AWAIT_OF && type == AWAIT) { if (!await) { throw error(AbstractParser.message(MSG_INVALID_FOR_AWAIT_OF), token); } isForAwaitOf = true; next(); } expect(LPAREN); TokenType varType = null; switch (type) { case VAR: // Var declaration captured in for outer block. varType = type; varDeclList = variableDeclarationList(varType, false, yield, await, forStart); break; case SEMICOLON: break; default: if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration() || type == CONST)) { // LET/CONST declaration captured in container block created above. varType = type; varDeclList = variableDeclarationList(varType, false, yield, await, forStart); if (varType == LET) { // Per-iteration scope not needed if BindingPattern is empty if (!forNode.getStatements().isEmpty()) { flags |= ForNode.PER_ITERATION_SCOPE; } } break; } if (env.constAsVar && type == CONST) { // Var declaration captured in for outer block. varType = TokenType.VAR; varDeclList = variableDeclarationList(varType, false, yield, await, forStart); break; } initStartsWithLet = (type == LET); initStartsWithAsyncOf = (type == ASYNC && !isForAwaitOf && lookaheadIsOf()); initCoverExpr = new CoverExpressionError(); init = expression(false, yield, await, initCoverExpr); break; } switch (type) { case SEMICOLON: // for (init; test; modify) if (varDeclList != null) { assert init == null; // init has already been hoisted to the surrounding (declaration) block. // late check for missing assignment, now we know it's a // for (init; test; modify) loop if (varDeclList.missingAssignment != null) { if (varDeclList.missingAssignment instanceof IdentNode) { throw error(AbstractParser.message(MSG_MISSING_CONST_ASSIGNMENT, ((IdentNode) varDeclList.missingAssignment).getName())); } else { throw error(AbstractParser.message(MSG_MISSING_DESTRUCTURING_ASSIGNMENT), varDeclList.missingAssignment.getToken()); } } } else if (init != null) { verifyExpression(initCoverExpr); } // for each (init; test; modify) is invalid if ((flags & ForNode.IS_FOR_EACH) != 0) { throw error(AbstractParser.message(MSG_FOR_EACH_WITHOUT_IN), token); } expect(SEMICOLON); if (type != SEMICOLON) { test = joinPredecessorExpression(yield, await); } expect(SEMICOLON); if (type != RPAREN) { modify = joinPredecessorExpression(yield, await); } break; case OF: if (ES8_FOR_AWAIT_OF && isForAwaitOf && !initStartsWithLet) { // fall through } else if (ES6_FOR_OF && !initStartsWithLet && !initStartsWithAsyncOf) { isForOf = true; // fall through } else { expect(SEMICOLON); // fail with expected message break; } case IN: if (isForAwaitOf) { expectDontAdvance(OF); flags |= ForNode.IS_FOR_AWAIT_OF; } else { flags |= isForOf ? ForNode.IS_FOR_OF : ForNode.IS_FOR_IN; } test = new JoinPredecessorExpression(); if (varDeclList != null) { // for (var|let|const ForBinding in|of expression) if (varDeclList.secondBinding != null) { // for (var i, j in obj) is invalid throw error(AbstractParser.message(MSG_MANY_VARS_IN_FOR_IN_LOOP, isForOf || isForAwaitOf ? CONTEXT_OF : CONTEXT_IN), varDeclList.secondBinding.getToken()); } init = varDeclList.firstBinding; assert init instanceof IdentNode || isDestructuringLhs(init) : init; if (varDeclList.declarationWithInitializerToken != 0 && (isStrictMode || type != IN || varType != VAR || isDestructuringLhs(init))) { /* * ES5 legacy: for (var i = AssignmentExpressionNoIn in Expression). * Invalid in ES6, but allowed in non-strict mode if no ES6 features * are used, i.e., it is a syntax error in strict mode, for-of * loops, let/const, or destructuring. */ throw error(AbstractParser.message(MSG_FOR_IN_LOOP_INITIALIZER, isForOf || isForAwaitOf ? CONTEXT_OF : CONTEXT_IN), varDeclList.declarationWithInitializerToken); } if (varType == CONST || varType == LET) { flags |= ForNode.PER_ITERATION_SCOPE; } } else { // for (LeftHandSideExpression in|of expression) assert init != null : "for..in/of init expression cannot be null here"; // check if initial expression is a valid L-value if (!checkValidLValue(init, isForOf || isForAwaitOf ? CONTEXT_FOR_OF_ITERATOR : CONTEXT_FOR_IN_ITERATOR)) { throw error(AbstractParser.message(MSG_NOT_LVALUE_FOR_IN_LOOP, isForOf || isForAwaitOf ? CONTEXT_OF : CONTEXT_IN), init.getToken()); } } next(); // For-of only allows AssignmentExpression. modify = isForOf || isForAwaitOf ? new JoinPredecessorExpression(assignmentExpression(true, yield, await)) : joinPredecessorExpression(yield, await); break; default: expect(SEMICOLON); break; } expect(RPAREN); // Set the for body. body = getStatement(yield, await); } finally { lc.pop(forNode); } boolean skipVars = (flags & ForNode.PER_ITERATION_SCOPE) != 0 && (isForOf || isForAwaitOf || (flags & ForNode.IS_FOR_IN) != 0); if (!skipVars) { // Variable declaration and initialization statements. // e.g.: `for (let [a, b] = c, d = e; ...; ...)` => // `let a; let b; [a, b] := c; let d = e;` for (final Statement var : forNode.getStatements()) { appendStatement(var); } } appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); } finally { if (outer != null) { restoreBlock(outer); } } if (outer != null) { outer.getScope().close(); appendStatement(new BlockStatement(forLine, new Block(outer.getToken(), body.getFinish(), 0, outer.getScope(), outer.getStatements()))); } } private boolean checkValidLValue(Expression init, String contextString) { if (init instanceof IdentNode) { IdentNode ident = (IdentNode) init; if (!checkIdentLValue(ident)) { return false; } if (ident.isMetaProperty()) { return false; } verifyStrictIdent(ident, contextString); return true; } else if (init instanceof AccessNode || init instanceof IndexNode) { return !((BaseNode) init).isOptional(); } else if (isDestructuringLhs(init)) { verifyDestructuringAssignmentPattern(init, contextString); return true; } else { return false; } } private boolean lookaheadIsLetDeclaration() { return lookaheadOfLetDeclaration() != null; } private TokenType lookaheadOfLetDeclaration() { assert type == LET; for (int i = 1;; i++) { TokenType t = T(k + i); switch (t) { case EOL: case COMMENT: continue; case OF: case IDENT: case LBRACKET: case LBRACE: return t; default: // accept future strict tokens in non-strict mode (including LET) if (t.isContextualKeyword() || (!isStrictMode && t.isFutureStrict())) { return t; } return null; } } } private boolean lookaheadIsOf() { for (int i = 1;; i++) { TokenType t = T(k + i); switch (t) { case EOL: case COMMENT: continue; case OF: return true; default: return false; } } } /** * Parse a {@code while} IterationStatement. */ private void whileStatement(boolean yield, boolean await) { // Capture WHILE token. final long whileToken = token; final int whileLine = line; // WHILE tested in caller. next(); final ParserContextLoopNode whileNode = new ParserContextLoopNode(); lc.push(whileNode); JoinPredecessorExpression test = null; Block body = null; try { expect(LPAREN); test = joinPredecessorExpression(yield, await); expect(RPAREN); body = getStatement(yield, await); } finally { lc.pop(whileNode); } if (body != null) { appendStatement(new WhileNode(whileLine, whileToken, body.getFinish(), false, test, body)); } } /** * Parse a {@code do while} IterationStatement. */ private void doStatement(boolean yield, boolean await) { // Capture DO token. final long doToken = token; int doLine = 0; // DO tested in the caller. next(); final ParserContextLoopNode doWhileNode = new ParserContextLoopNode(); lc.push(doWhileNode); Block body = null; JoinPredecessorExpression test = null; try { // Get DO body. body = getStatement(yield, await); expect(WHILE); expect(LPAREN); doLine = line; test = joinPredecessorExpression(yield, await); expect(RPAREN); if (type == SEMICOLON) { endOfLine(); } } finally { lc.pop(doWhileNode); } appendStatement(new WhileNode(doLine, doToken, finish, true, test, body)); } /** * Parse continue statement. * *
     * ContinueStatement :
     *      continue ;
     *      continue [no LineTerminator here] LabelIdentifier ;
     * 
*/ private void continueStatement(boolean yield, boolean await) { // Capture CONTINUE token. final int continueLine = line; final long continueToken = token; // CONTINUE tested in caller. nextOrEOL(); boolean seenEOL = (type == EOL); if (seenEOL) { next(); } ParserContextLabelNode labelNode = null; // SEMICOLON or label. switch (type) { case RBRACE: case SEMICOLON: case EOF: break; default: if (seenEOL) { break; } final IdentNode ident = labelIdentifier(yield, await); labelNode = lc.findLabel(ident.getName()); if (labelNode == null) { throw error(AbstractParser.message(MSG_UNDEFINED_LABEL, ident), ident.getToken()); } break; } final String labelName = labelNode == null ? null : labelNode.getLabelName(); final ParserContextLoopNode targetNode = lc.getContinueTo(labelName); if (targetNode == null) { throw error(AbstractParser.message(MSG_ILLEGAL_CONTINUE_STMT), continueToken); } endOfLine(); // Construct and add CONTINUE node. appendStatement(new ContinueNode(continueLine, continueToken, finish, labelName)); } /** * Parse break statement. * *
     * BreakStatement :
     *      break ;
     *      break [no LineTerminator here] LabelIdentifier ;
     * 
*/ private void breakStatement(boolean yield, boolean await) { // Capture BREAK token. final int breakLine = line; final long breakToken = token; // BREAK tested in caller. nextOrEOL(); boolean seenEOL = (type == EOL); if (seenEOL) { next(); } ParserContextLabelNode labelNode = null; // SEMICOLON or label. switch (type) { case RBRACE: case SEMICOLON: case EOF: break; default: if (seenEOL) { break; } final IdentNode ident = labelIdentifier(yield, await); labelNode = lc.findLabel(ident.getName()); if (labelNode == null) { throw error(AbstractParser.message(MSG_UNDEFINED_LABEL, ident), ident.getToken()); } break; } // either an explicit label - then get its node or just a "break" - get first breakable // targetNode is what we are breaking out from. final String labelName = labelNode == null ? null : labelNode.getLabelName(); final ParserContextBreakableNode targetNode = lc.getBreakable(labelName); if (targetNode == null) { throw error(AbstractParser.message(MSG_ILLEGAL_BREAK_STMT), breakToken); } endOfLine(); // Construct and add BREAK node. appendStatement(new BreakNode(breakLine, breakToken, finish, labelName)); } /** * Parse return statement. * *
     * ReturnStatement :
     *      return ;
     *      return [no LineTerminator here] Expression ;
     * 
*/ private void returnStatement(boolean yield, boolean await) { // check for return outside function ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction.isScriptOrModule() || currentFunction.isClassStaticBlock()) { throw error(AbstractParser.message(MSG_INVALID_RETURN)); } // Capture RETURN token. final int returnLine = line; final long returnToken = token; // RETURN tested in caller. nextOrEOL(); boolean seenEOL = (type == EOL); if (seenEOL) { next(); } Expression expression = null; // SEMICOLON or expression. switch (type) { case RBRACE: case SEMICOLON: case EOF: break; default: if (seenEOL) { break; } expression = expression(yield, await); break; } endOfLine(); // Construct and add RETURN node. appendStatement(new ReturnNode(returnLine, returnToken, finish, expression)); } /** * Parse YieldExpression. * *
     * YieldExpression[In, Await] :
     *   yield
     *   yield [no LineTerminator here] AssignmentExpression[?In, +Yield, ?Await]
     *   yield [no LineTerminator here] * AssignmentExpression[?In, +Yield, ?Await]
     * 
*/ private Expression yieldExpression(boolean in, boolean await) { assert isES6(); // Capture YIELD token. long yieldToken = token; // YIELD tested in caller. assert type == YIELD; if (inFormalParameterList()) { throw error(AbstractParser.message(MSG_UNEXPECTED_TOKEN, type.getNameOrType())); } recordYieldOrAwait(); nextOrEOL(); Expression expression = null; boolean yieldAsterisk = false; if (type == MUL) { yieldAsterisk = true; yieldToken = Token.recast(yieldToken, YIELD_STAR); next(); } switch (type) { case RBRACE: case SEMICOLON: case EOL: case EOF: case COMMARIGHT: case RPAREN: case RBRACKET: case COLON: if (!yieldAsterisk) { // treat (yield) as (yield void 0) expression = newUndefinedLiteral(yieldToken, finish); if (type == EOL) { next(); } break; } else { // AssignmentExpression required, fall through } default: expression = assignmentExpression(in, true, await); break; } // Construct and add YIELD node. return new UnaryNode(yieldToken, expression); } private Expression awaitExpression(boolean yield) { assert isAwait(); // Capture await token. long awaitToken = token; ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction.isClassStaticBlock() || inFormalParameterList()) { throw error(AbstractParser.message(MSG_UNEXPECTED_TOKEN, type.getNameOrType())); } recordYieldOrAwait(); next(); Expression expression = unaryExpression(yield, true, CoverExpressionError.DENY); if (isModule && currentFunction.isModule()) { // Top-level await module: mark the body of the module as async. currentFunction.setFlag(FunctionNode.IS_ASYNC); } // Construct and add AWAIT node. return new UnaryNode(Token.recast(awaitToken, AWAIT), expression); } private static UnaryNode newUndefinedLiteral(long token, int finish) { return new UnaryNode(Token.recast(token, VOID), LiteralNode.newInstance(token, finish, 0)); } private void recordYieldOrAwait() { long yieldOrAwaitToken = token; assert Token.descType(yieldOrAwaitToken) == YIELD || Token.descType(yieldOrAwaitToken) == AWAIT; recordYieldOrAwait(yieldOrAwaitToken, false); } private void recordYieldOrAwait(IdentNode ident) { recordYieldOrAwait(ident.getToken(), true); } /** * It is an early SyntaxError if arrow parameter list contains yield or await. We cannot detect * this immediately due to grammar ambiguity, so we only record the potential error to be thrown * later once the ambiguity is resolved. */ private void recordYieldOrAwait(long yieldOrAwaitToken, boolean ident) { for (Iterator iterator = lc.getFunctions(); iterator.hasNext();) { ParserContextFunctionNode fn = iterator.next(); if (fn.isCoverArrowHead()) { if (ident && !fn.isAsync()) { // await identifiers are only relevant for async arrow heads continue; } // Only record the first yield or await. if (fn.getYieldOrAwaitInParameters() == 0L) { fn.setYieldOrAwaitInParameters(yieldOrAwaitToken); } } else { break; } } } /** * Parse with statement. * *
     * WithStatement :
     *      with ( Expression ) Statement
     * 
*/ private void withStatement(boolean yield, boolean await) { // Capture WITH token. final int withLine = line; final long withToken = token; // WITH tested in caller. next(); // ECMA 12.10.1 strict mode restrictions if (isStrictMode) { throw error(AbstractParser.message(MSG_STRICT_NO_WITH), withToken); } expect(LPAREN); final Expression expression = expression(yield, await); expect(RPAREN); final Block body = getStatement(yield, await); appendStatement(new WithNode(withLine, withToken, finish, expression, body)); } /** * Parse switch statement. * *
     * SwitchStatement :
     *      switch ( Expression ) CaseBlock
     *
     * CaseBlock :
     *      { CaseClauses? }
     *      { CaseClauses? DefaultClause CaseClauses }
     *
     * CaseClauses :
     *      CaseClause
     *      CaseClauses CaseClause
     *
     * CaseClause :
     *      case Expression : StatementList?
     *
     * DefaultClause :
     *      default : StatementList?
     * 
*/ private void switchStatement(boolean yield, boolean await) { final int switchLine = line; final long switchToken = token; // Block around the switch statement with a variable capturing the switch expression value. final ParserContextBlockNode outerBlock; if (useBlockScope()) { outerBlock = newBlock(newBlockScope()); outerBlock.setFlag(Block.IS_SYNTHETIC); } else { outerBlock = null; } ParserContextBlockNode switchBlock; SwitchNode switchStatement; try { assert type == TokenType.SWITCH; // tested in caller. next(); /* * Note: Identifier references in the switch expression need to be resolved in the scope * of the outer block, so we must parse the expression before pushing the switch scope. */ expect(LPAREN); int expressionLine = line; Expression expression = expression(yield, await); expect(RPAREN); // Desugar expression to a synthetic let variable assignment in the outer block. // This simplifies lexical scope analysis (the expression is outside the switch // block). // e.g.: let x = 1; switch (x) { case 0: let x = 2; } => // let x = 1; { let :switch = x; { let x; switch (:switch) { case 0: x = 2; } } } if (useBlockScope()) { IdentNode switchExprName = new IdentNode(Token.recast(expression.getToken(), IDENT), expression.getFinish(), lexer.stringIntern(SWITCH_BINDING_NAME)); VarNode varNode = new VarNode(expressionLine, Token.recast(expression.getToken(), LET), expression.getFinish(), switchExprName, expression, VarNode.IS_LET); outerBlock.appendStatement(varNode); declareVar(outerBlock.getScope(), varNode); expression = switchExprName; } expectDontAdvance(LBRACE); // Block to capture variables declared inside the switch statement. switchBlock = newBlock(Scope.createSwitchBlock(lc.getCurrentScope())); switchBlock.setFlag(Block.IS_SYNTHETIC | Block.IS_SWITCH_BLOCK); assert type == LBRACE; next(); // Create and add switch statement. final ParserContextSwitchNode switchNode = new ParserContextSwitchNode(); lc.push(switchNode); int defaultCaseIndex = -1; // Prepare to accumulate cases. final ArrayList cases = new ArrayList<>(); try { while (type != RBRACE) { // Prepare for next case. Expression caseExpression = null; final long caseToken = token; switch (type) { case CASE: next(); caseExpression = expression(yield, await); break; case DEFAULT: if (defaultCaseIndex != -1) { throw error(AbstractParser.message(MSG_DUPLICATE_DEFAULT_IN_SWITCH)); } next(); break; default: // Force an error. expect(CASE); break; } expect(COLON); // Get CASE body. List statements = caseStatementList(yield, await); final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements); if (caseExpression == null) { assert defaultCaseIndex == -1; defaultCaseIndex = cases.size(); } cases.add(caseNode); } assert type == RBRACE; next(); switchStatement = new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCaseIndex); } finally { lc.pop(switchNode); restoreBlock(switchBlock); } switchBlock.getScope().close(); appendStatement(new BlockStatement(switchLine, new Block(switchToken, switchStatement.getFinish(), switchBlock.getFlags(), switchBlock.getScope(), List.of(switchStatement)))); } finally { if (outerBlock != null) { restoreBlock(outerBlock); } } if (outerBlock != null) { outerBlock.getScope().close(); appendStatement(new BlockStatement(switchLine, new Block(switchToken, switchStatement.getFinish(), outerBlock.getFlags(), outerBlock.getScope(), outerBlock.getStatements()))); } } /** * Parse label statement. * *
     * LabelledStatement :
     *      Identifier : Statement
     * 
*/ private void labelStatement(boolean yield, boolean await, boolean mayBeFunctionDeclaration) { // Capture label token. final long labelToken = token; // Get label ident. final IdentNode ident = labelIdentifier(yield, await); expect(COLON); if (lc.findLabel(ident.getName()) != null) { throw error(AbstractParser.message(MSG_DUPLICATE_LABEL, ident), labelToken); } final ParserContextLabelNode labelNode = new ParserContextLabelNode(ident.getName()); Block body = null; try { lc.push(labelNode); body = getStatement(yield, await, true, mayBeFunctionDeclaration); } finally { lc.pop(labelNode); } appendStatement(new LabelNode(line, labelToken, finish, ident.getName(), body)); } /** * Parse throw statement. * *
     * 
     * ThrowStatement:
     *      throw Expression; // [no LineTerminator here]
     * 
     * 
*/ private void throwStatement(boolean yield, boolean await) { // Capture THROW token. final int throwLine = line; final long throwToken = token; // THROW tested in caller. nextOrEOL(); Expression expression = null; // SEMICOLON or expression. switch (type) { case RBRACE: case SEMICOLON: case EOL: break; default: expression = expression(yield, await); break; } if (expression == null) { throw error(AbstractParser.message(MSG_EXPECTED_OPERAND, type.getNameOrType())); } endOfLine(); appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, false)); } /** * Parse try statement. * *
     * TryStatement :
     *      try Block Catch
     *      try Block Finally
     *      try Block Catch Finally
     *
     * Catch :
     *      catch( Identifier if Expression ) Block
     *      catch( Identifier ) Block
     *
     * Finally :
     *      finally Block
     * 
*/ private void tryStatement(boolean yield, boolean await) { // Capture TRY token. final int tryLine = line; final long tryToken = token; // TRY tested in caller. next(); // Container block needed to act as target for labeled break statements final int startLine = line; final ParserContextBlockNode outer = newBlock(newBlockScope()); // Create try. try { final Block tryBody = getBlock(yield, await, true); final ArrayList catchBlocks = new ArrayList<>(); while (type == CATCH) { final int catchLine = line; final long catchToken = token; next(); boolean optionalCatchBinding = type == LBRACE && ES2019_OPTIONAL_CATCH_BINDING; if (!optionalCatchBinding) { expect(LPAREN); } final Scope catchParameterScope = Scope.createCatchParameter(lc.getCurrentScope()); final ParserContextBlockNode catchBlock = newBlock(catchParameterScope); final Expression ifExpression; try { final IdentNode exception; final Expression pattern; if (optionalCatchBinding) { exception = null; pattern = null; ifExpression = null; } else { if (isBindingIdentifier() || !(ES6_DESTRUCTURING && isES6())) { pattern = null; IdentNode catchParameter = bindingIdentifier(yield, await, CONTEXT_CATCH_PARAMETER); exception = catchParameter.setIsCatchParameter(); } else { pattern = bindingPattern(yield, await); exception = new IdentNode(Token.recast(pattern.getToken(), IDENT), pattern.getFinish(), lexer.stringIntern(ERROR_BINDING_NAME)).setIsCatchParameter(); } // Nashorn extension: catch clause can have optional // condition. So, a single try can have more than one // catch clause each with it's own condition. if (env.syntaxExtensions && type == IF) { next(); // Get the exception condition. ifExpression = expression(yield, await); } else { ifExpression = null; } expect(RPAREN); } final CatchNode catchNode = catchBody(yield, await, catchToken, catchLine, exception, pattern, ifExpression); appendStatement(catchNode); } finally { restoreBlock(catchBlock); } catchParameterScope.close(); int catchFinish = Math.max(finish, Token.descPosition(catchBlock.getToken())); Block catchBlockNode = new Block(catchBlock.getToken(), catchFinish, catchBlock.getFlags() | Block.IS_SYNTHETIC, catchParameterScope, catchBlock.getStatements()); catchBlocks.add(catchBlockNode); // If unconditional catch then should to be the end. if (ifExpression == null) { break; } } // Prepare to capture finally statement. Block finallyStatements = null; if (type == FINALLY) { next(); finallyStatements = getBlock(yield, await, true); } // Need at least one catch or a finally. if (catchBlocks.isEmpty() && finallyStatements == null) { throw error(AbstractParser.message(MSG_MISSING_CATCH_OR_FINALLY), tryToken); } final TryNode tryNode = new TryNode(tryLine, tryToken, finish, tryBody, catchBlocks, finallyStatements); // Add try. assert lc.peek() == outer; appendStatement(tryNode); } finally { restoreBlock(outer); } outer.getScope().close(); appendStatement(new BlockStatement(startLine, new Block(tryToken, finish, outer.getFlags() | Block.IS_SYNTHETIC, outer.getScope(), outer.getStatements()))); } private CatchNode catchBody(boolean yield, boolean await, long catchToken, int catchLine, IdentNode exception, Expression pattern, Expression ifExpression) { if (exception != null) { final Scope catchScope = lc.getCurrentScope(); assert catchScope.isCatchParameterScope(); VarNode exceptionVar = new VarNode(catchLine, Token.recast(exception.getToken(), LET), exception.getFinish(), exception.setIsDeclaredHere(), null, VarNode.IS_LET); appendStatement(exceptionVar); declareVar(catchScope, exceptionVar); if (pattern != null) { verifyDestructuringBindingPattern(pattern, new Consumer() { @Override public void accept(IdentNode identNode) { verifyStrictIdent(identNode, CONTEXT_CATCH_PARAMETER); final int varFlags = VarNode.IS_LET | VarNode.IS_DESTRUCTURING; final VarNode var = new VarNode(catchLine, Token.recast(identNode.getToken(), LET), identNode.getFinish(), identNode.setIsDeclaredHere(), null, varFlags); appendStatement(var); declareVar(catchScope, var); } }); } } final Block catchBody = getBlock(yield, await, true); return new CatchNode(catchLine, catchToken, finish, exception, pattern, ifExpression, catchBody, false); } /** * Parse debugger statement. */ private void debuggerStatement() { // Capture DEBUGGER token. final int debuggerLine = line; final long debuggerToken = token; // DEBUGGER tested in caller. next(); endOfLine(); appendStatement(new DebuggerNode(debuggerLine, debuggerToken, finish)); } /** * Parse primary expression. * *
     * PrimaryExpression :
     *      this
     *      IdentifierReference
     *      Literal
     *      ArrayLiteral
     *      ObjectLiteral
     *      RegularExpressionLiteral
     *      TemplateLiteral
     *      CoverParenthesizedExpressionAndArrowParameterList
     *
     * CoverParenthesizedExpressionAndArrowParameterList :
     *      ( Expression )
     *      ( )
     *      ( ... BindingIdentifier )
     *      ( Expression , ... BindingIdentifier )
     * 
* * @return Expression node. */ @SuppressWarnings("fallthrough") private Expression primaryExpression(boolean yield, boolean await, CoverExpressionError coverExpression) { // Capture first token. final long primaryToken = token; switch (type) { case THIS: { final TruffleString name = type.getNameTS(); next(); markThis(); return new IdentNode(primaryToken, finish, lexer.stringIntern(name)).setIsThis(); } case IDENT: { final IdentNode ident = identifierReference(yield, await); if (ident == null) { break; } return detectSpecialProperty(ident); } case NON_OCTAL_DECIMAL: if (isStrictMode) { throw error(AbstractParser.message(MSG_STRICT_NO_NONOCTALDECIMAL), token); } case OCTAL_LEGACY: if (isStrictMode) { throw error(AbstractParser.message(MSG_STRICT_NO_OCTAL), token); } case STRING: case ESCSTRING: case DECIMAL: case HEXADECIMAL: case OCTAL: case BINARY_NUMBER: case BIGINT: case FLOATING: case REGEX: return getLiteral(); case FALSE: next(); return LiteralNode.newInstance(primaryToken, finish, false); case TRUE: next(); return LiteralNode.newInstance(primaryToken, finish, true); case NULL: next(); return LiteralNode.newInstance(primaryToken, finish); case LBRACKET: return arrayLiteral(yield, await, coverExpression); case LBRACE: return objectLiteral(yield, await, coverExpression); case LPAREN: return parenthesizedExpressionAndArrowParameterList(yield, await); case TEMPLATE: case TEMPLATE_HEAD: return templateLiteralOrExecString(yield, await, primaryToken); case MOD: if (isV8Intrinsics() && lookaheadIsIdentAndLParen()) { long v8IntrinsicToken = Token.recast(token, TokenType.IDENT); next(); IdentNode ident = getIdent(); if (isV8Intrinsic(ident.getName())) { String v8IntrinsicName = "%" + ident.getName(); addIdentifierReference(v8IntrinsicName); TruffleString v8IntrinsicNameTS = lexer.stringIntern(v8IntrinsicName); return createIdentNode(v8IntrinsicToken, ident.getFinish(), v8IntrinsicNameTS); } } default: // In this context some operator tokens mark the start of a literal. if (lexer.scanLiteral(primaryToken, type, lineInfoReceiver)) { next(); return getLiteral(); } if (type.isContextualKeyword() || isNonStrictModeIdent()) { return identifierReference(yield, await); } break; } throw error(AbstractParser.message(MSG_EXPECTED_OPERAND, type.getNameOrType())); } // We don't (want to) pass the list of V8 intrinsics to the parser. // So, we use a simple heuristics instead. private static boolean isV8Intrinsic(String name) { for (char c : name.toCharArray()) { if (c >= 128) { return false; } } return name.length() > 2; } private boolean lookaheadIsIdentAndLParen() { boolean seenIdent = false; for (int i = 1;; i++) { long currentToken = getToken(k + i); TokenType t = Token.descType(currentToken); switch (t) { case COMMENT: continue; case IDENT: if (seenIdent) { return false; } else { seenIdent = true; } continue; case LPAREN: return seenIdent; default: return false; } } } private boolean isV8Intrinsics() { return env.v8Intrinsics; } private boolean isPrivateFieldsIn() { return env.privateFieldsIn; } /** * Parse ArrayLiteral. * *
     * ArrayLiteral :
     *      [ Elision? ]
     *      [ ElementList ]
     *      [ ElementList , Elision? ]
     *      [ expression for (LeftHandExpression in expression) ( (if ( Expression ) )? ]
     *
     * ElementList : Elision? AssignmentExpression
     *      ElementList , Elision? AssignmentExpression
     *
     * Elision :
     *      ,
     *      Elision ,
     * 
* * @return Expression node. */ private LiteralNode arrayLiteral(boolean yield, boolean await, CoverExpressionError coverExpression) { // Capture LBRACKET token. final long arrayToken = token; // LBRACKET tested in caller. next(); // Prepare to accumulate elements. final ArrayList elements = new ArrayList<>(); // Track elisions. boolean elision = true; boolean hasSpread = false; loop: while (true) { long spreadToken = 0; switch (type) { case RBRACKET: next(); break loop; case COMMARIGHT: next(); // If no prior expression if (elision) { elements.add(null); } elision = true; break; case ELLIPSIS: if (ES6_SPREAD_ARRAY) { hasSpread = true; spreadToken = token; next(); } // fall through default: if (!elision) { throw error(AbstractParser.message(MSG_EXPECTED_COMMA, type.getNameOrType())); } // Add expression element. Expression expression = assignmentExpression(true, yield, await, coverExpression); if (expression != null) { if (spreadToken != 0) { expression = new UnaryNode(Token.recast(spreadToken, SPREAD_ARRAY), expression); } elements.add(expression); } else { expect(RBRACKET); } elision = false; break; } } return LiteralNode.newInstance(arrayToken, finish, elements, hasSpread, elision); } /** * Parse an object literal. * *
     * ObjectLiteral :
     *      { }
     *      {  PropertyDefinitionList }
     *      {  PropertyDefinitionList , }
     * 
* * @return Expression node. */ private ObjectNode objectLiteral(boolean yield, boolean await, CoverExpressionError coverExpression) { // Capture LBRACE token. final long objectToken = token; // LBRACE tested in caller. next(); // Object context. // Prepare to accumulate elements. final ArrayList elements = new ArrayList<>(); final Map propertyNameMapES5 = isES6() ? null : new HashMap<>(); // Create a block for the object literal. boolean commaSeen = true; boolean hasDuplicateProto = false; boolean hasProto = false; loop: while (true) { switch (type) { case RBRACE: next(); break loop; case COMMARIGHT: if (commaSeen) { throw error(AbstractParser.message(MSG_EXPECTED_PROPERTY_ID, type.getNameOrType())); } next(); commaSeen = true; break; default: if (!commaSeen) { throw error(AbstractParser.message(MSG_EXPECTED_COMMA, type.getNameOrType())); } commaSeen = false; // Get and add the next property. final PropertyNode property = propertyDefinition(yield, await, coverExpression); elements.add(property); hasDuplicateProto = hasProto && property.isProto(); hasProto = hasProto || property.isProto(); if (property.isComputed() || property.getKey().isTokenType(SPREAD_OBJECT)) { break; } if (isES6()) { if (hasDuplicateProto) { recordOrThrowExpressionError(MSG_MULTIPLE_PROTO_KEY, property.getToken(), coverExpression); } } else { checkES5PropertyDefinition(property, propertyNameMapES5); } break; } } return new ObjectNode(objectToken, finish, elements); } private void checkES5PropertyDefinition(PropertyNode property, Map map) { final String key = property.getKeyName(); final PropertyNode existingProperty = map.get(key); if (existingProperty == null) { map.put(key, property); } else { // ES5 section 11.1.5 Object Initialiser // point # 4 on property assignment production final Expression value = property.getValue(); final FunctionNode getter = property.getGetter(); final FunctionNode setter = property.getSetter(); final Expression prevValue = existingProperty.getValue(); final FunctionNode prevGetter = existingProperty.getGetter(); final FunctionNode prevSetter = existingProperty.getSetter(); checkPropertyRedefinition(property, value, getter, setter, prevValue, prevGetter, prevSetter); if (value == null && prevValue == null) { // Update the map with existing (merged accessor) properties // for the purpose of checkPropertyRedefinition() above if (getter != null) { assert prevGetter != null || prevSetter != null; map.put(key, existingProperty.setGetter(getter)); } else if (setter != null) { assert prevGetter != null || prevSetter != null; map.put(key, existingProperty.setSetter(setter)); } } } } private void checkPropertyRedefinition(final PropertyNode property, final Expression value, final FunctionNode getter, final FunctionNode setter, final Expression prevValue, final FunctionNode prevGetter, final FunctionNode prevSetter) { // ECMA 11.1.5 strict mode restrictions if (isStrictMode && value != null && prevValue != null) { throw error(AbstractParser.message(MSG_PROPERTY_REDEFINITON, property.getKeyName()), property.getToken()); } final boolean isPrevAccessor = prevGetter != null || prevSetter != null; final boolean isAccessor = getter != null || setter != null; // data property redefined as accessor property if (prevValue != null && isAccessor) { throw error(AbstractParser.message(MSG_PROPERTY_REDEFINITON, property.getKeyName()), property.getToken()); } // accessor property redefined as data if (isPrevAccessor && value != null) { throw error(AbstractParser.message(MSG_PROPERTY_REDEFINITON, property.getKeyName()), property.getToken()); } if (isAccessor && isPrevAccessor) { if (getter != null && prevGetter != null || setter != null && prevSetter != null) { throw error(AbstractParser.message(MSG_PROPERTY_REDEFINITON, property.getKeyName()), property.getToken()); } } } /** *
     * LiteralPropertyName :
     *      IdentifierName
     *      StringLiteral
     *      NumericLiteral
     * 
* * @return PropertyName node */ @SuppressWarnings("fallthrough") private PropertyKey literalPropertyName() { switch (type) { case IDENT: return getIdent().setIsPropertyName(); case NON_OCTAL_DECIMAL: if (isStrictMode) { throw error(AbstractParser.message(MSG_STRICT_NO_NONOCTALDECIMAL), token); } case OCTAL_LEGACY: if (isStrictMode) { throw error(AbstractParser.message(MSG_STRICT_NO_OCTAL), token); } case STRING: case ESCSTRING: case DECIMAL: case HEXADECIMAL: case OCTAL: case BINARY_NUMBER: case BIGINT: case FLOATING: return (PropertyKey) getLiteral(); default: return getIdentifierName().setIsPropertyName(); } } /** *
     * ComputedPropertyName :
     *      AssignmentExpression
     * 
* * @return PropertyName node */ private Expression computedPropertyName(boolean yield, boolean await) { expect(LBRACKET); Expression expression = assignmentExpression(true, yield, await); expect(RBRACKET); return expression; } /** *
     * PropertyName :
     *      LiteralPropertyName
     *      ComputedPropertyName
     * 
* * @return PropertyName node */ private Expression propertyName(boolean yield, boolean await) { if (ES6_COMPUTED_PROPERTY_NAME && type == LBRACKET && isES6()) { return computedPropertyName(yield, await); } else { return (Expression) literalPropertyName(); } } /** * Parse an object literal property definition. * *
     * PropertyDefinition :
     *      IdentifierReference
     *      CoverInitializedName
     *      PropertyName : AssignmentExpression
     *      MethodDefinition
     *
     * CoverInitializedName :
     *      IdentifierReference = AssignmentExpression
     * 
* * @return Property or reference node. */ private PropertyNode propertyDefinition(boolean yield, boolean await, CoverExpressionError coverExpression) { // Capture firstToken. final long propertyToken = token; final int functionLine = line; final Expression propertyName; final boolean isIdentifier; boolean async = false; if (isAsync() && lookaheadIsAsyncMethod(false)) { async = true; next(); } boolean generator = false; if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) { generator = true; next(); } final boolean computed = type == LBRACKET; if (type == IDENT || (isIdentifier() && !(type == GET || type == SET))) { isIdentifier = true; propertyName = getIdent().setIsPropertyName(); } else if (type == GET || type == SET) { final TokenType getOrSet = type; next(); if (type != COLON && type != COMMARIGHT && type != RBRACE && ((type != ASSIGN && type != LPAREN) || !isES6())) { final long getOrSetToken = propertyToken; if (getOrSet == GET) { final PropertyFunction getter = propertyGetterFunction(getOrSetToken, functionLine, yield, await, false); return new PropertyNode(propertyToken, finish, getter.key, null, getter.functionNode, null, false, getter.computed, false, false); } else if (getOrSet == SET) { final PropertyFunction setter = propertySetterFunction(getOrSetToken, functionLine, yield, await, false); return new PropertyNode(propertyToken, finish, setter.key, null, null, setter.functionNode, false, setter.computed, false, false); } } isIdentifier = true; propertyName = new IdentNode(propertyToken, finish, lexer.stringIntern(getOrSet.getNameTS())).setIsPropertyName(); } else if (type == ELLIPSIS && ES8_REST_SPREAD_PROPERTY && isES2017() && !(generator || async)) { long spreadToken = Token.recast(propertyToken, TokenType.SPREAD_OBJECT); next(); Expression assignmentExpression = assignmentExpression(true, yield, await); Expression spread = new UnaryNode(spreadToken, assignmentExpression); return new PropertyNode(propertyToken, finish, spread, null, null, null, false, false, false, false); } else { isIdentifier = false; propertyName = propertyName(yield, await); } Expression propertyValue; if (generator || async) { expectDontAdvance(LPAREN); } boolean coverInitializedName = false; boolean proto = false; boolean isAnonymousFunctionDefinition = false; if (type == LPAREN && isES6()) { propertyValue = propertyMethodFunction(propertyName, propertyToken, functionLine, generator, FunctionNode.IS_METHOD, computed, async).functionNode; } else if (isIdentifier && (type == COMMARIGHT || type == RBRACE || type == ASSIGN) && isES6()) { IdentNode ident = (IdentNode) propertyName; verifyIdent(ident, yield, await); ident = createIdentNode(propertyToken, finish, ident.getPropertyNameTS()); // IdentifierReference or CoverInitializedName if (type == ASSIGN && ES6_DESTRUCTURING) { // If not destructuring, this is a SyntaxError long assignToken = token; recordOrThrowExpressionError(MSG_INVALID_PROPERTY_INITIALIZER, assignToken, coverExpression); coverInitializedName = true; next(); Expression rhs = assignmentExpression(true, yield, await); propertyValue = verifyAssignment(assignToken, ident, rhs, true); } else { propertyValue = detectSpecialProperty(ident); } addIdentifierReference(ident.getName()); } else { expect(COLON); if (!computed && PROTO_NAME.equals(((PropertyKey) propertyName).getPropertyName())) { proto = true; } pushDefaultName(propertyName); try { propertyValue = assignmentExpression(true, yield, await, coverExpression); } finally { popDefaultName(); } if (!proto) { if (isAnonymousFunctionDefinition(propertyValue)) { if (!computed && propertyName instanceof PropertyKey) { propertyValue = setAnonymousFunctionName(propertyValue, ((PropertyKey) propertyName).getPropertyNameTS()); } else { isAnonymousFunctionDefinition = true; } } } } return new PropertyNode(propertyToken, finish, propertyName, propertyValue, null, null, false, computed, coverInitializedName, proto, false, isAnonymousFunctionDefinition); } private PropertyFunction propertyGetterFunction(long getSetToken, int functionLine, boolean yield, boolean await, boolean allowPrivate) { final boolean computed = type == LBRACKET; final Expression propertyName = classElementName(yield, await, allowPrivate); final IdentNode getterName = computed ? null : createMethodNameIdent(propertyName, GET_SPC); expect(LPAREN); expect(RPAREN); int functionFlags = FunctionNode.IS_GETTER | FunctionNode.IS_METHOD | (computed ? FunctionNode.IS_ANONYMOUS : 0); final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getterName, getSetToken, functionFlags, functionLine, List.of(), 0); lc.push(functionNode); Block functionBody; try { functionBody = functionBody(functionNode); } finally { lc.pop(functionNode); } final FunctionNode function = createFunctionNode( functionNode, getSetToken, getterName, functionLine, functionBody); return new PropertyFunction(propertyName, function, computed); } private PropertyFunction propertySetterFunction(long getSetToken, int functionLine, boolean yield, boolean await, boolean allowPrivate) { final boolean computed = type == LBRACKET; final Expression propertyName = classElementName(yield, await, allowPrivate); final IdentNode setterName = computed ? null : createMethodNameIdent(propertyName, SET_SPC); expect(LPAREN); int functionFlags = FunctionNode.IS_SETTER | FunctionNode.IS_METHOD | (computed ? FunctionNode.IS_ANONYMOUS : 0); final ParserContextFunctionNode functionNode = createParserContextFunctionNode(setterName, getSetToken, functionFlags, functionLine); lc.push(functionNode); Block functionBody; try { final ParserContextBlockNode parameterBlock = functionNode.createParameterBlock(); lc.push(parameterBlock); try { if (!env.syntaxExtensions || type != RPAREN) { formalParameter(false, false); } // else Nashorn allows no-argument setters expect(RPAREN); functionBody = functionBody(functionNode); } finally { restoreBlock(parameterBlock); } if (parameterBlock != null) { functionBody = wrapParameterBlock(parameterBlock, functionBody); } } finally { lc.pop(functionNode); } final FunctionNode function = createFunctionNode( functionNode, getSetToken, setterName, functionLine, functionBody); return new PropertyFunction(propertyName, function, computed); } private PropertyFunction propertyMethodFunction(Expression key, final long methodToken, final int methodLine, final boolean generator, final int flags, boolean computed, boolean async) { final IdentNode methodNameNode = computed ? null : createMethodNameIdent(key, ""); expect(LPAREN); int functionFlags = flags | (computed ? FunctionNode.IS_ANONYMOUS : 0) | (generator ? FunctionNode.IS_GENERATOR : 0) | (async ? FunctionNode.IS_ASYNC : 0); final ParserContextFunctionNode functionNode = createParserContextFunctionNode(methodNameNode, methodToken, functionFlags, methodLine); lc.push(functionNode); try { ParserContextBlockNode parameterBlock = functionNode.createParameterBlock(); lc.push(parameterBlock); Block functionBody; try { formalParameterList(generator, async); expect(RPAREN); functionBody = functionBody(functionNode); } finally { restoreBlock(parameterBlock); } verifyParameterList(functionNode); if (parameterBlock != null) { functionBody = wrapParameterBlock(parameterBlock, functionBody); } final FunctionNode function = createFunctionNode( functionNode, methodToken, methodNameNode, methodLine, functionBody); return new PropertyFunction(key, function, computed); } finally { lc.pop(functionNode); } } private IdentNode createMethodNameIdent(Expression propertyKey, String prefix) { TruffleString methodName; if (propertyKey instanceof IdentNode) { methodName = ((IdentNode) propertyKey).getPropertyNameTS(); } else if (propertyKey instanceof PropertyKey) { methodName = lexer.stringIntern(((PropertyKey) propertyKey).getPropertyNameTS()); } else { return null; } if (!prefix.isEmpty()) { methodName = lexer.stringIntern(prefix + methodName.toJavaStringUncached()); } return createIdentNode(propertyKey.getToken(), propertyKey.getFinish(), methodName); } private static boolean isAnonymousFunctionDefinition(Expression expression) { if (expression instanceof FunctionNode && ((FunctionNode) expression).isAnonymous()) { return true; } else if (expression instanceof ClassNode && ((ClassNode) expression).isAnonymous()) { return true; } else { return false; } } private Expression setAnonymousFunctionName(Expression expression, TruffleString functionName) { if (!isES6()) { return expression; } if (expression instanceof FunctionNode && ((FunctionNode) expression).isAnonymous()) { return ((FunctionNode) expression).setName(null, functionName); } else if (expression instanceof ClassNode && ((ClassNode) expression).isAnonymous()) { ClassNode classNode = (ClassNode) expression; FunctionNode constructorFunction = (FunctionNode) classNode.getConstructor().getValue(); return classNode.setConstructor(classNode.getConstructor().setValue(constructorFunction.setName(null, functionName))); } return expression; } private static final class PropertyFunction { final Expression key; final FunctionNode functionNode; final boolean computed; PropertyFunction(final Expression key, final FunctionNode function, final boolean computed) { this.key = key; this.functionNode = function; this.computed = computed; } } /** * Parse left hand side expression. * *
     * LeftHandSideExpression :
     *      NewExpression
     *      CallExpression
     *
     * CallExpression :
     *      MemberExpression Arguments (CoverCallExpressionAndAsyncArrowHead)
     *      SuperCall
     *      CallExpression Arguments
     *      CallExpression [ Expression ]
     *      CallExpression . IdentifierName
     *      CallExpression TemplateLiteral
     *      CallExpression . PrivateIdentifier
     *
     * SuperCall :
     *      super Arguments
     * 
* * @return Expression node. */ private Expression leftHandSideExpression(boolean yield, boolean await, CoverExpressionError coverExpression) { int callLine = line; long callToken = token; Expression lhs = memberExpression(yield, await, coverExpression); if (type == LPAREN) { boolean async = ES8_ASYNC_FUNCTION && isES2017() && lhs.isTokenType(ASYNC) && lookbehindNoLineTerminatorAfterAsync(); final List arguments = argumentList(yield, await, async, callToken, callLine); if (async) { if (type == ARROW && lookbehindNoLineTerminatorBeforeArrow()) { // async () => ... // async ( ArgumentsList ) => ... return new ExpressionList(callToken, callLine, arguments); } } // Catch special functions. boolean eval = false; boolean applyArguments = false; if (lhs instanceof IdentNode) { final IdentNode ident = (IdentNode) lhs; final String name = ident.getName(); if (EVAL_NAME.equals(name)) { markEval(); eval = true; } else if (SUPER.getName().equals(name)) { assert ident.isDirectSuper(); markSuperCall(); } } else if (lhs instanceof AccessNode && !((AccessNode) lhs).isPrivate() && arguments.size() == 2 && arguments.get(1) instanceof IdentNode && ((IdentNode) arguments.get(1)).isArguments() && APPLY_NAME.equals(((AccessNode) lhs).getProperty())) { if (markApplyArgumentsCall(lc, arguments)) { applyArguments = true; } } lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, arguments, false, false, eval, applyArguments, false); } boolean optionalChain = false; loop: while (true) { // Capture token. callLine = line; callToken = token; switch (type) { case LPAREN: { final List arguments = argumentList(yield, await); lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, arguments, false, optionalChain); break; } case LBRACKET: { next(); // Get array index. final Expression rhs = expression(true, yield, await); expect(RBRACKET); // Create indexing node. lhs = new IndexNode(callToken, finish, lhs, rhs, false, false, optionalChain); break; } case PERIOD: { next(); final boolean isPrivate = type == TokenType.PRIVATE_IDENT; final IdentNode property; if (isPrivate) { property = privateIdentifierUse(); } else { property = getIdentifierName(); } // Create property access node. lhs = new AccessNode(callToken, finish, lhs, property.getNameTS(), false, isPrivate, false, optionalChain); break; } case TEMPLATE: case TEMPLATE_HEAD: { // tagged template literal if (optionalChain) { // TemplateLiteral not allowed in OptionalChain throw error(AbstractParser.message(MSG_OPTIONAL_CHAIN_TEMPLATE)); } final List arguments = templateLiteralArgumentList(yield, await); lhs = CallNode.forTaggedTemplateLiteral(callLine, callToken, lhs.getStart(), finish, lhs, arguments); break; } case OPTIONAL_CHAIN: { next(); optionalChain = true; switch (type) { case LPAREN: { final List arguments = argumentList(yield, await); lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, arguments, true, optionalChain); break; } case LBRACKET: { next(); final Expression rhs = expression(true, yield, await); expect(RBRACKET); lhs = new IndexNode(callToken, finish, lhs, rhs, false, true, optionalChain); break; } default: { final boolean isPrivate = type == TokenType.PRIVATE_IDENT; final IdentNode property; if (isPrivate) { property = privateIdentifierUse(); } else { property = getIdentifierName(); } lhs = new AccessNode(callToken, finish, lhs, property.getNameTS(), false, isPrivate, true, optionalChain); break; } } break; } default: break loop; } } return lhs; } /** * Parse new expression. * *
     * NewExpression :
     *      MemberExpression
     *      new NewExpression
     * 
* * @return Expression node. */ private Expression newExpression(boolean yield, boolean await) { final long newToken = token; // NEW is tested in caller. assert type == TokenType.NEW; next(); if (ES6_NEW_TARGET && type == PERIOD && isES6()) { next(); if (type == IDENT && TARGET.equals(getValueNoEscape())) { next(); markNewTarget(); return new IdentNode(newToken, finish, lexer.stringIntern(NEW_TARGET_NAME)).setIsNewTarget(); } else { throw error(AbstractParser.message(MSG_EXPECTED_TARGET), token); } } else if (type == IMPORT && isES2020() && lookahead() == LPAREN) { // new cannot be used with import() throw error(AbstractParser.message(MSG_EXPECTED_OPERAND, IMPORT.getName()), token); } // Get function base. final int callLine = line; final Expression constructor = memberExpression(yield, await, CoverExpressionError.DENY); // Get arguments. List arguments; // Allow for missing arguments. if (type == LPAREN) { arguments = argumentList(yield, await); } else { arguments = new ArrayList<>(); if (type == TokenType.OPTIONAL_CHAIN) { // OptionalChain is not allowed directly after a NewExpression (without parentheses) throw error(AbstractParser.message(MSG_UNEXPECTED_TOKEN, type.getNameOrType())); } } // Nashorn extension: This is to support the following interface implementation // syntax: // // var r = new java.lang.Runnable() { // run: function() { println("run"); } // }; // // The object literal following the "new Constructor()" expression // is passed as an additional (last) argument to the constructor. if (env.syntaxExtensions && type == LBRACE) { arguments.add(objectLiteral(yield, await, CoverExpressionError.DENY)); } final Expression callNode = CallNode.forNew(callLine, newToken, Token.descPosition(newToken), finish, constructor, arguments); return new UnaryNode(newToken, callNode); } /** * Parse member expression. * *
     * MemberExpression :
     *      PrimaryExpression
     *        FunctionExpression
     *        ClassExpression
     *        GeneratorExpression
     *      MemberExpression [ Expression ]
     *      MemberExpression . IdentifierName
     *      MemberExpression TemplateLiteral
     *      SuperProperty
     *      MetaProperty
     *      new MemberExpression Arguments
     *      MemberExpression . PrivateIdentifier
     *
     * SuperProperty :
     *      super [ Expression ]
     *      super . IdentifierName
     *
     * MetaProperty :
     *      NewTarget
     * 
* * @return Expression node. */ private Expression memberExpression(boolean yield, boolean await, CoverExpressionError coverExpression) { // Prepare to build operation. Expression lhs; boolean isSuper = false; switch (type) { case NEW: // Get new expression. lhs = newExpression(yield, await); break; case FUNCTION: // Get function expression. lhs = functionExpression(); break; case CLASS: case AT: if (ES6_CLASS && isES6()) { lhs = classExpression(yield, await); break; } // fall through case SUPER: if (ES6_CLASS && isES6()) { Scope scope = lc.getCurrentScope(); if (scope.inMethod()) { long identToken = Token.recast(token, IDENT); next(); lhs = new IdentNode(identToken, finish, lexer.stringIntern(SUPER.getNameTS())).setIsSuper(); switch (type) { case LBRACKET: case PERIOD: markSuperProperty(); isSuper = true; break; case LPAREN: if (scope.inDerivedConstructor()) { lhs = ((IdentNode) lhs).setIsDirectSuper(); break; } else { // fall through to throw error } default: throw error(AbstractParser.message(MSG_INVALID_SUPER), identToken); } break; } } // fall through case ASYNC: if (isAsync() && lookaheadIsAsyncFunction()) { lhs = asyncFunctionExpression(); break; } // fall through case IMPORT: if (isES2020() && type == IMPORT) { lhs = importExpression(yield, await); break; } // fall through default: // Get primary expression. lhs = primaryExpression(yield, await, coverExpression); verifyPrimaryExpression(lhs, coverExpression); break; } loop: while (true) { // Capture token. final long callToken = token; switch (type) { case LBRACKET: { next(); // Get array index. final Expression index = expression(true, yield, await); expect(RBRACKET); // Create indexing node. lhs = new IndexNode(callToken, finish, lhs, index, isSuper, false, false); if (isSuper) { isSuper = false; } break; } case PERIOD: { next(); final boolean isPrivate = type == TokenType.PRIVATE_IDENT; final IdentNode property; if (!isSuper && isPrivate) { property = privateIdentifierUse(); } else { property = getIdentifierName(); } // Create property access node. lhs = new AccessNode(callToken, finish, lhs, property.getNameTS(), isSuper, isPrivate, false, false); if (isSuper) { isSuper = false; } break; } case TEMPLATE: case TEMPLATE_HEAD: { // tagged template literal final int callLine = line; final List arguments = templateLiteralArgumentList(yield, await); lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, arguments, false, false); break; } default: break loop; } } return lhs; } /** * To be called immediately after {@link #primaryExpression} to check if the next token still * meets the AssignmentPattern grammar requirements and if not, throws any expression error. * This helps report the first error location for cases like: ({x=i}[{y=j}]). */ private void verifyPrimaryExpression(Expression lhs, CoverExpressionError coverExpression) { if (coverExpression != CoverExpressionError.DENY && coverExpression.hasError() && isDestructuringLhs(lhs)) { /** * These token types indicate that the preceding PrimaryExpression is part of an * unfinished MemberExpression or other LeftHandSideExpression, which also means that it * cannot be a valid AssignmentPattern anymore at this point. */ switch (type) { case LPAREN: case PERIOD: case LBRACKET: case OPTIONAL_CHAIN: case TEMPLATE: case TEMPLATE_HEAD: verifyExpression(coverExpression); break; } } } /** * Parse import expression * *
     * ImportCall:
     *     import ( AssignmentExpression ,opt )
     *     import ( AssignmentExpression, AssignmentExpression ,opt )
     * 
*/ private Expression importExpression(boolean yield, boolean await) { final long importToken = token; final int importLine = line; final int importStart = start; assert type == IMPORT; next(); if (type == PERIOD) { next(); expectDontAdvance(IDENT); String meta = ((TruffleString) getValueNoEscape()).toJavaStringUncached(); if (META.equals(meta)) { if (!isModule) { throw error(AbstractParser.message(MSG_UNEXPECTED_IMPORT_META), importToken); } next(); return new IdentNode(importToken, finish, lexer.stringIntern(IMPORT_META_NAME)).setIsImportMeta(); } else { throw error(AbstractParser.message(MSG_UNEXPECTED_IDENT, meta), token); } } else if (type == LPAREN) { next(); List arguments = new ArrayList<>(); arguments.add(assignmentExpression(true, yield, await)); if (env.importAttributes && type == COMMARIGHT) { next(); if (type != RPAREN) { arguments.add(assignmentExpression(true, yield, await)); if (type == COMMARIGHT) { next(); } } } expect(RPAREN); IdentNode importIdent = new IdentNode(importToken, Token.descPosition(importToken) + Token.descLength(importToken), lexer.stringIntern(IMPORT.getNameTS())); return CallNode.forImport(importLine, importToken, importStart, finish, importIdent, arguments); } else { throw error(AbstractParser.message(MSG_EXPECTED_OPERAND, IMPORT.getName()), importToken); } } private ArrayList argumentList(boolean yield, boolean await) { return argumentList(yield, await, false, 0L, 0); } /** * Parse function call arguments. Also used to parse CoverCallExpressionAndAsyncArrowHead. * *
     * {@code
     * Arguments :
     *      ( )
     *      ( ArgumentList )
     *
     * ArgumentList :
     *      AssignmentExpression
     *      ... AssignmentExpression
     *      ArgumentList , AssignmentExpression
     *      ArgumentList , ... AssignmentExpression
     * }
     * 
* * @return Argument list. */ private ArrayList argumentList(boolean yield, boolean await, boolean coverAsyncArrow, long startToken, int startLine) { // LPAREN tested in caller. assert type == LPAREN; next(); // Prepare to accumulate list of arguments. final ArrayList nodeList = new ArrayList<>(); // Track commas. boolean first = true; ParserContextFunctionNode coverFunction = null; ParserContextBlockNode parameterBlock = null; CoverExpressionError coverExpression = CoverExpressionError.DENY; if (coverAsyncArrow) { coverFunction = createParserContextArrowFunctionNode(startToken, startLine, true, true); parameterBlock = coverFunction.createParameterBlock(); coverExpression = new CoverExpressionError(); lc.push(coverFunction); lc.push(parameterBlock); } try { while (type != RPAREN) { // Comma prior to every argument except the first. if (!first) { expect(COMMARIGHT); // Trailing comma. if (ES8_TRAILING_COMMA && isES2017() && type == RPAREN) { break; } } else { first = false; } long spreadToken = 0; if (ES6_SPREAD_ARGUMENT && type == ELLIPSIS && isES6()) { spreadToken = token; next(); } // Get argument expression. Expression expression = assignmentExpression(true, yield, await, coverExpression); if (spreadToken != 0) { expression = new UnaryNode(Token.recast(spreadToken, TokenType.SPREAD_ARGUMENT), expression); } nodeList.add(expression); } } finally { if (coverAsyncArrow) { lc.pop(parameterBlock); lc.pop(coverFunction); } } expect(RPAREN); if (coverAsyncArrow) { if (type == ARROW && lookbehindNoLineTerminatorBeforeArrow()) { commitArrowHead(coverFunction); } else { // invocation of a function named 'async' revertArrowHead(coverFunction); verifyExpression(coverExpression); } } return nodeList; } private long expectAsyncFunction() { assert isAsync() && lookaheadIsAsyncFunction(); long asyncToken = token; nextOrEOL(); return Token.recast(asyncToken, FUNCTION); } /** * Parse async (generator) function declaration. */ private Expression asyncFunctionDeclaration(final boolean isStatement, final boolean topLevel, boolean yield, boolean await, boolean isDefault) { long functionToken = expectAsyncFunction(); return functionDeclarationOrExpression(functionToken, isStatement, topLevel, true, false, true, yield, await, isDefault); } /** * Parse async (generator) function expression. */ private Expression asyncFunctionExpression() { long functionToken = expectAsyncFunction(); return functionDeclarationOrExpression(functionToken, false, false, true, false, false, false, true, true); } /** * Parse (generator) function declaration. */ private Expression functionDeclaration(final boolean isStatement, final boolean topLevel, final boolean expressionStatement, boolean yield, boolean await, boolean isDefault) { return functionDeclarationOrExpression(token, isStatement, topLevel, false, expressionStatement, true, yield, await, isDefault); } /** * Parse (generator) function expression. */ private Expression functionExpression() { return functionDeclarationOrExpression(token, false, false, false, false, false, false, false, true); } /** * Parse (async) (generator) function declaration or expression. * *
     * FunctionDeclaration[Yield, Await] :
     *      function Identifier ( FormalParameterList? ) { FunctionBody }
     *
     * FunctionExpression :
     *      function Identifier? ( FormalParameterList? ) { FunctionBody }
     * 
* * @param isStatement true if parsing in a statement context. * @param isDeclaration True if parsing a declaration, false if parsing an expression. * @param isYield Yield if parsing a declaration, otherwise ignored. * @param isAwait Await if parsing a declaration, otherwise ignored. * @param isDefault Default if parsing a declaration, otherwise ignored. * @return the function. */ private Expression functionDeclarationOrExpression(long functionToken, boolean isStatement, boolean topLevel, boolean async, boolean expressionStatement, boolean isDeclaration, boolean isYield, boolean isAwait, boolean isDefault) { final int functionLine = line; // FUNCTION is tested in caller. assert type == FUNCTION; next(); boolean generator = false; if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) { if (expressionStatement) { throw error(AbstractParser.message(MSG_EXPECTED_STMT, CONTEXT_GENERATOR_FUNCTION_DECLARATION), token); } generator = true; next(); } assert !(isDeclaration && !isDefault) || isStatement; // sanity check IdentNode name = null; boolean declared = isDeclaration; if (isBindingIdentifier()) { boolean yield = (!isDeclaration && generator) || (isDeclaration && isYield); boolean await = (!isDeclaration && async) || (isDeclaration && isAwait); name = bindingIdentifier(yield, await, CONTEXT_FUNCTION_NAME); } else if (isDeclaration && !isDefault) { // Nashorn extension: anonymous function statements. // Do not allow anonymous function statement if extensions // are not allowed. But if we are reparsing then anon function // statement is possible - because it was used as function // expression in surrounding code. if (env.syntaxExtensions) { // statement is treated like an anonymous function expression, not a declaration. declared = false; } else if (reparsedFunction == null) { expect(IDENT); } } expect(LPAREN); boolean isAnonymous = name == null; // Function declarations must be named, with the exception of default exports. assert !declared || (!isAnonymous || isDefault); int functionFlags = (generator ? FunctionNode.IS_GENERATOR : 0) | (async ? FunctionNode.IS_ASYNC : 0) | (isAnonymous ? FunctionNode.IS_ANONYMOUS : 0) | (declared ? FunctionNode.IS_DECLARED : 0) | ((isStatement && !isAnonymous) ? FunctionNode.IS_STATEMENT : 0); final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, functionFlags, functionLine); if (isAnonymous) { // name is null, generate anonymous name functionNode.setInternalName(getDefaultFunctionName()); } lc.push(functionNode); Block functionBody; // Hide the current default name across function boundaries. // E.g. "x3 = function x1() { function() {}}" // If we didn't hide the current default name, then the innermost anonymous function would // receive "x3". hideDefaultName(); try { final ParserContextBlockNode parameterBlock = functionNode.createParameterBlock(); lc.push(parameterBlock); try { formalParameterList(generator, async); expect(RPAREN); functionBody = functionBody(functionNode); } finally { restoreBlock(parameterBlock); } if (parameterBlock != null) { functionBody = wrapParameterBlock(parameterBlock, functionBody); } } finally { popDefaultName(); lc.pop(functionNode); } if ((isStatement && !isAnonymous) && !(topLevel || useBlockScope()) && (isStrictMode || env.functionStatement != ScriptEnvironment.FunctionStatementBehavior.ACCEPT)) { // For compatibility with Nashorn, we report the error after parsing the body. reportIllegalES5BlockLevelFunctionDeclaration(functionToken); } verifyParameterList(functionNode); final FunctionNode function = createFunctionNode( functionNode, functionToken, name, functionLine, functionBody); if (isStatement) { if (isAnonymous) { appendStatement(new ExpressionStatement(functionLine, functionToken, finish, function)); return function; } // mark ES6 block functions as lexically scoped Scope scope = lc.getCurrentScope(); final int varFlags = ((topLevel && !scope.isModuleScope()) || !useBlockScope()) ? 0 : VarNode.IS_LET; final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags); declareVar(scope, varNode); if (topLevel) { functionDeclarations.add(varNode); } else { appendStatement(varNode); } } return function; } private static Block wrapParameterBlock(ParserContextBlockNode parameterBlock, Block functionBody) { assert parameterBlock.getFlag(Block.IS_PARAMETER_BLOCK) != 0 && functionBody.isFunctionBody(); if (parameterBlock.getStatements().isEmpty()) { return functionBody; } else { parameterBlock.getStatements().add(new BlockStatement(functionBody.getFirstStatementLineNumber(), functionBody)); return new Block(parameterBlock.getToken(), functionBody.getFinish(), parameterBlock.getFlags(), parameterBlock.getScope(), parameterBlock.getStatements()); } } private void verifyParameterList(final ParserContextFunctionNode functionNode) { IdentNode duplicateParameter = functionNode.getDuplicateParameterBinding(); if (duplicateParameter != null) { if (functionNode.isStrict() || functionNode.isMethod() || functionNode.isArrow() || !functionNode.isSimpleParameterList()) { throw error(AbstractParser.message(MSG_STRICT_PARAM_REDEFINITION, duplicateParameter.getName()), duplicateParameter.getToken()); } final List parameters = functionNode.getParameters(); final int arity = parameters.size(); final HashSet parametersSet = new HashSet<>(arity); for (int i = arity - 1; i >= 0; i--) { final IdentNode parameter = parameters.get(i); String parameterName = parameter.getName(); if (parametersSet.contains(parameterName)) { // redefinition of parameter name, allowed in non-strict mode; // the parameter is mapped to the argument at the index of the last redefinition parameters.set(i, parameter.setIsIgnoredParameter()); } else { parametersSet.add(parameterName); } } } } private void reportIllegalES5BlockLevelFunctionDeclaration(long functionToken) { assert !isES6(); if (isStrictMode) { throw error(JSErrorType.SyntaxError, AbstractParser.message(MSG_STRICT_NO_FUNC_DECL_HERE), functionToken); } else if (env.functionStatement == ScriptEnvironment.FunctionStatementBehavior.ERROR) { throw error(JSErrorType.SyntaxError, AbstractParser.message(MSG_NO_FUNC_DECL_HERE), functionToken); } else if (env.functionStatement == ScriptEnvironment.FunctionStatementBehavior.WARNING) { warning(JSErrorType.SyntaxError, AbstractParser.message(MSG_NO_FUNC_DECL_HERE_WARN), functionToken); } } private void pushDefaultName(final Expression nameExpr) { defaultNames.add(nameExpr); } private Object popDefaultName() { return defaultNames.remove(defaultNames.size() - 1); } private TruffleString getDefaultFunctionName() { if (!defaultNames.isEmpty()) { final Object nameExpr = defaultNames.get(defaultNames.size() - 1); if (nameExpr instanceof PropertyKey) { markDefaultNameUsed(); return ((PropertyKey) nameExpr).getPropertyNameTS(); } else if (nameExpr instanceof AccessNode) { AccessNode accessNode = (AccessNode) nameExpr; markDefaultNameUsed(); if (accessNode.getBase() instanceof AccessNode) { AccessNode base = (AccessNode) accessNode.getBase(); if (base.getBase() instanceof IdentNode && !base.isPrivate() && base.getProperty().equals(PROTOTYPE_NAME)) { return lexer.stringIntern(((IdentNode) base.getBase()).getName() + '.' + accessNode.getProperty()); } } else if (accessNode.getBase() instanceof IdentNode) { return lexer.stringIntern(((IdentNode) accessNode.getBase()).getName() + '.' + accessNode.getProperty()); } return accessNode.getPropertyTS(); } } return lexer.stringIntern(ANONYMOUS_FUNCTION_NAME); } private void markDefaultNameUsed() { popDefaultName(); hideDefaultName(); } private void hideDefaultName() { // Can be any value as long as getDefaultFunctionName doesn't recognize it as something it // can extract a value from. Can't be null. defaultNames.add(""); } /** * Parse function parameter list. */ private void formalParameterList(final boolean yield, final boolean async) { formalParameterList(RPAREN, yield, async); } private boolean inFormalParameterList() { for (Iterator iterator = lc.getAllNodes(); iterator.hasNext();) { ParserContextNode node = iterator.next(); if (node instanceof ParserContextScopableNode) { Scope scope = ((ParserContextScopableNode) node).getScope(); if (scope.isFunctionBodyScope()) { return false; } if (scope.isFunctionParameterScope() && !scope.isArrowFunctionParameterScope()) { return true; } } } return false; } private void formalParameter(final boolean yield, final boolean await) { if (type == YIELD && yield || isAwait() && await) { throw error(expectMessage(IDENT)); } final ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); final long paramToken = token; final int paramLine = line; IdentNode ident; if (isBindingIdentifier() || !(ES6_DESTRUCTURING && isES6())) { ident = bindingIdentifier(yield, await, CONTEXT_FUNCTION_PARAMETER); if (type == ASSIGN && (ES6_DEFAULT_PARAMETER && isES6())) { next(); // default parameter Expression initializer = assignmentExpression(true, yield, await); if (isAnonymousFunctionDefinition(initializer)) { initializer = setAnonymousFunctionName(initializer, ident.getNameTS()); } if (currentFunction != null) { addDefaultParameter(paramToken, finish, paramLine, ident, initializer, currentFunction); } } else { if (currentFunction != null) { currentFunction.addParameter(ident); } } } else { final Expression pattern = bindingPattern(yield, await); // Introduce synthetic temporary parameter to capture the object to be destructured. verifyDestructuringParameterBindingPattern(pattern, paramToken, paramLine); Expression initializer = null; if (type == ASSIGN) { next(); // binding pattern with initializer initializer = assignmentExpression(true, yield, await); } if (currentFunction != null) { addDestructuringParameter(paramToken, finish, paramLine, pattern, initializer, currentFunction, false); } } } private void functionRestParameter(final TokenType endType, final boolean yield, final boolean await) { final long paramToken = token; final int paramLine = line; final ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); final Expression pattern = bindingIdentifierOrPattern(yield, await, CONTEXT_FUNCTION_PARAMETER); if (pattern instanceof IdentNode) { IdentNode ident = ((IdentNode) pattern).setIsRestParameter(); if (currentFunction != null) { currentFunction.addParameter(ident); } } else { verifyDestructuringParameterBindingPattern(pattern, paramToken, paramLine); if (currentFunction != null) { addDestructuringParameter(paramToken, finish, paramLine, pattern, null, currentFunction, true); } } // rest parameter must be last expectDontAdvance(endType); } /** * Parse function parameter list. Same as the other method of the same name - except that the * end token type expected is passed as argument to this method. * *
     * FormalParameterList :
     *      Identifier
     *      FormalParameterList , Identifier
     * 
*/ private void formalParameterList(final TokenType endType, final boolean yield, final boolean await) { // Track commas. boolean first = true; while (type != endType) { // Comma prior to every argument except the first. if (!first) { expect(COMMARIGHT); // Trailing comma. if (ES8_TRAILING_COMMA && isES2017() && type == endType) { break; } } else { first = false; } if (ES6_REST_PARAMETER && type == ELLIPSIS && isES6()) { next(); functionRestParameter(endType, yield, await); break; } formalParameter(yield, await); } } private static void addDefaultParameter(long paramToken, int paramFinish, int paramLine, IdentNode target, Expression initializer, ParserContextFunctionNode function) { assert target != null && initializer != null; // desugar to: let target = (param === undefined) ? initializer : param; // we use an special positional parameter node not subjected to TDZ rules; // thereby, we forego the need for a synthetic param symbol to refer to the passed value. final int paramIndex = function.getParameterCount(); final ParameterNode param = new ParameterNode(paramToken, paramFinish, paramIndex); final BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), param, newUndefinedLiteral(paramToken, paramFinish)); final Expression value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(param)); final VarNode varNode = new VarNode(paramLine, Token.recast(paramToken, LET), paramFinish, target, value, VarNode.IS_LET); function.addDefaultParameter(varNode); } private void addDestructuringParameter(long paramToken, int paramFinish, int paramLine, Expression target, Expression initializer, ParserContextFunctionNode function, boolean isRest) { assert isDestructuringLhs(target); // desugar to: target := (param === undefined) ? initializer : param; // we use an special positional parameter node not subjected to TDZ rules; // thereby, we forego the need for a synthetic param symbol to refer to the passed value. final int paramIndex = function.getParameterCount(); final ParameterNode param = new ParameterNode(paramToken, paramFinish, paramIndex, isRest); final Expression value; if (initializer == null) { value = param; // binding pattern without initializer } else { BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), param, newUndefinedLiteral(paramToken, paramFinish)); value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(param)); } BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN_INIT), target, value); function.addParameterInitialization(paramLine, assignment, initializer != null, isRest); } private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine) { verifyDestructuringBindingPattern(pattern, new Consumer() { @Override public void accept(IdentNode identNode) { verifyStrictIdent(identNode, CONTEXT_FUNCTION_PARAMETER); ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { // declare function-scope variables for destructuring bindings VarNode declaration = new VarNode(paramLine, Token.recast(paramToken, LET), pattern.getFinish(), identNode, null, VarNode.IS_LET | VarNode.IS_DESTRUCTURING); currentFunction.addParameterBindingDeclaration(declaration); // detect duplicate bound names in parameter list } } }); } /** * Parse function body. * *
     * FunctionBody :
     *      SourceElements?
     * 
* * @return function node (body.) */ private Block functionBody(final ParserContextFunctionNode functionNode) { final boolean yield = functionNode.isGenerator(); final boolean await = functionNode.isAsync() || (isTopLevelAwait() && isModule && functionNode.isModule()) || functionNode.isClassStaticBlock(); final long bodyToken = token; final int bodyFinish; final boolean parseBody; Object endParserState = null; // Create a new function block. ParserContextBlockNode body = newBlock(functionNode.createBodyScope()); try { final int functionId = functionNode.getId(); parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId(); // Nashorn extension: expression closures if ((env.syntaxExtensions || functionNode.isArrow()) && type != LBRACE) { // Example: // function square(x) x * x; // print(square(3)); // just expression as function body final Expression expr = assignmentExpression(true, yield, await); long lastToken = previousToken; functionNode.setLastToken(previousToken); assert lc.getCurrentBlock().getScope().isFunctionBodyScope(); // EOL uses length field to store the line number final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken)); /* * Only create the return node if we aren't skipping nested functions. Note that we * aren't skipping parsing of these extended functions; they're considered to be * small anyway. Also, they don't end with a single well known token, so it'd be * very hard to get correctly (see the note below for reasoning on skipping * happening before instead of after RBRACE for details). */ if (parseBody) { final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); appendStatement(returnNode); } bodyFinish = finish; } else { expectDontAdvance(LBRACE); if (parseBody || !skipFunctionBody(functionNode)) { next(); // Gather the function elements. final List prevFunctionDecls = functionDeclarations; functionDeclarations = new ArrayList<>(); try { sourceElements(yield, await, 0); addFunctionDeclarations(functionNode); } finally { functionDeclarations = prevFunctionDecls; } if (parseBody) { //@formatter:off // Since the lexer can read ahead and lexify some number of tokens in advance and have // them buffered in the TokenStream, we need to produce a lexer state as it was just // before it lexified RBRACE, and not whatever is its current (quite possibly well read // ahead) state. endParserState = new ParserState(Token.descPosition(token), line, linePosition); // NOTE: you might wonder why do we capture/restore parser state before RBRACE instead of // after RBRACE; after all, we could skip the below "expect(RBRACE);" if we captured the // state after it. The reason is that RBRACE is a well-known token that we can expect and // will never involve us getting into a weird lexer state, and as such is a great reparse // point. Typical example of a weird lexer state after RBRACE would be: // function this_is_skipped() { ... } "use strict"; // because lexer is doing weird off-by-one maneuvers around string literal quotes. Instead // of compensating for the possibility of a string literal (or similar) after RBRACE, // we'll rather just restart parsing from this well-known, friendly token instead. //@formatter:on } } bodyFinish = Token.descPosition(token) + Token.descLength(token); functionNode.setLastToken(token); expect(RBRACE); } functionNode.finishBodyScope(lexer); } finally { restoreBlock(body); } lc.propagateFunctionFlags(); // NOTE: we can only do alterations to the function node after restoreFunctionNode. if (parseBody) { functionNode.setEndParserState(endParserState); } else if (!body.getStatements().isEmpty()) { /* * This is to ensure the body is empty when !parseBody but we couldn't skip parsing it * (see skipFunctionBody() for possible reasons). While it is not strictly necessary for * correctness to enforce empty bodies in nested functions that were supposed to be * skipped, we do assert it as an invariant in few places in the compiler pipeline, so * for consistency's sake we'll throw away nested bodies early if we were supposed to * skip 'em. */ body.setStatements(List.of()); } if (reparsedFunction != null) { /* * We restore the flags stored in the function's ScriptFunctionData that we got when we * first eagerly parsed the code. We're doing it because some flags would be set based * on the content of the function, or even content of its nested functions, most of * which are normally skipped during an on-demand compilation. */ final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId()); if (data != null) { // Data can be null if when we originally parsed the file, we removed the function // declaration as it was dead code. functionNode.setFlag(data.getFunctionFlags()); // This compensates for missing markEval() in case the function contains an inner // function that contains eval(), that now we didn't discover since we skipped the // inner function. if (functionNode.hasNestedEval()) { assert functionNode.hasScopeBlock(); body.setFlag(Block.NEEDS_SCOPE); } } } return new Block(bodyToken, bodyFinish, body.getFlags() | Block.IS_BODY, body.getScope(), body.getStatements()); } private boolean skipFunctionBody(final ParserContextFunctionNode functionNode) { if (reparsedFunction == null) { // Not reparsing, so don't skip any function body. return false; } // Skip to the RBRACE of this function, and continue parsing from there. final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId()); if (data == null) { /* * Nested function is not known to the reparsed function. This can happen if the * FunctionNode was in dead code that was removed. Both FoldConstants and Lower prune * dead code. In that case, the FunctionNode was dropped before a * RecompilableScriptFunctionData could've been created for it. */ return false; } final ParserState parserState = (ParserState) data.getEndParserState(); assert parserState != null; if (k < stream.last() && start < parserState.position && parserState.position <= Token.descPosition(stream.get(stream.last()))) { // RBRACE is already in the token stream, so fast forward to it for (; k < stream.last(); k++) { long nextToken = stream.get(k + 1); if (Token.descPosition(nextToken) == parserState.position && Token.descType(nextToken) == RBRACE) { token = stream.get(k); type = Token.descType(token); next(); assert type == RBRACE && start == parserState.position; return true; } } } stream.reset(); lexer = parserState.createLexer(source, lexer, stream, scripting, env.ecmaScriptVersion, shebang, isModule, allowBigInt, env.annexB); line = parserState.line; linePosition = parserState.linePosition; // Doesn't really matter, but it's safe to treat it as if there were a semicolon before // the RBRACE. type = SEMICOLON; scanFirstToken(); return true; } /** * Encapsulates part of the state of the parser, enough to reconstruct the state of both parser * and lexer for resuming parsing after skipping a function body. */ private static class ParserState { private final int position; private final int line; private final int linePosition; ParserState(final int position, final int line, final int linePosition) { this.position = position; this.line = line; this.linePosition = linePosition; } Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting, final int ecmaScriptVersion, final boolean shebang, final boolean isModule, final boolean allowBigInt, final boolean annexB) { final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, ecmaScriptVersion, shebang, isModule, true, allowBigInt, annexB); newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON)); return newLexer; } } private void addFunctionDeclarations(final ParserContextFunctionNode functionNode) { VarNode lastDecl = null; for (int i = functionDeclarations.size() - 1; i >= 0; i--) { Statement decl = functionDeclarations.get(i); if (lastDecl == null && decl instanceof VarNode) { decl = lastDecl = ((VarNode) decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION); functionNode.setFlag(FunctionNode.HAS_FUNCTION_DECLARATIONS); } prependStatement(decl); } } private ParserException invalidLHSError(final Expression lhs) { JSErrorType errorType = isES2020() ? JSErrorType.SyntaxError : JSErrorType.ReferenceError; return error(errorType, AbstractParser.message(MSG_INVALID_LVALUE), lhs.getToken()); } /** * Parse unary expression. * *
     * PostfixExpression :
     *      LeftHandSideExpression
     *      LeftHandSideExpression ++ // [no LineTerminator here]
     *      LeftHandSideExpression -- // [no LineTerminator here]
     *
     * UnaryExpression :
     *      PostfixExpression
     *      delete UnaryExpression
     *      void UnaryExpression
     *      typeof UnaryExpression
     *      ++ UnaryExpression
     *      -- UnaryExpression
     *      + UnaryExpression
     *      - UnaryExpression
     *      ~ UnaryExpression
     *      ! UnaryExpression
     * 
* * @return Expression node. */ private Expression unaryExpression(boolean yield, boolean await, CoverExpressionError coverExpression) { final long unaryToken = token; switch (type) { case DELETE: { next(); final Expression expr = unaryExpression(yield, await, CoverExpressionError.DENY); if (type == TokenType.EXP) { throw error(AbstractParser.message(MSG_UNEXPECTED_TOKEN, type.getNameOrType())); } return verifyDeleteExpression(unaryToken, expr); } case VOID: case TYPEOF: case ADD: case SUB: case BIT_NOT: case NOT: next(); final Expression expr = unaryExpression(yield, await, CoverExpressionError.DENY); if (type == TokenType.EXP) { throw error(AbstractParser.message(MSG_UNEXPECTED_TOKEN, type.getNameOrType())); } return new UnaryNode(unaryToken, expr); case INCPREFIX: case DECPREFIX: final TokenType opType = type; next(); final Expression lhs = unaryExpression(yield, await, CoverExpressionError.DENY); return verifyIncDecExpression(unaryToken, opType, lhs, false); default: if (isAwait() && await) { return awaitExpression(yield); } break; } Expression expression = leftHandSideExpression(yield, await, coverExpression); if (last != EOL) { switch (type) { case INCPREFIX: case DECPREFIX: final long opToken = token; final TokenType opType = type; final Expression lhs = expression; next(); return verifyIncDecExpression(opToken, opType, lhs, true); default: break; } } return expression; } private Expression verifyDeleteExpression(final long unaryToken, final Expression expr) { if (expr instanceof BaseNode || expr instanceof IdentNode) { if (isStrictMode) { if (expr instanceof IdentNode) { IdentNode ident = (IdentNode) expr; if (!ident.isThis() && !ident.isMetaProperty()) { throw error(AbstractParser.message(MSG_STRICT_CANT_DELETE_IDENT, ident), unaryToken); } } else if (expr instanceof AccessNode && ((AccessNode) expr).isPrivate()) { throw error(AbstractParser.message(MSG_STRICT_CANT_DELETE_PRIVATE), unaryToken); } } } return new UnaryNode(unaryToken, expr); } private Expression verifyIncDecExpression(final long unaryToken, final TokenType opType, final Expression lhs, final boolean isPostfix) { assert lhs != null; if (lhs instanceof IdentNode) { IdentNode ident = (IdentNode) lhs; if (!checkIdentLValue(ident) || ident.isMetaProperty()) { throw invalidLHSError(lhs); } assert opType == TokenType.INCPREFIX || opType == TokenType.DECPREFIX; String contextString = opType == TokenType.INCPREFIX ? CONTEXT_OPERAND_FOR_INC_OPERATOR : CONTEXT_OPERAND_FOR_DEC_OPERATOR; verifyStrictIdent((IdentNode) lhs, contextString); } else if (!(lhs instanceof AccessNode || lhs instanceof IndexNode) || ((BaseNode) lhs).isOptional()) { throw invalidLHSError(lhs); } return incDecExpression(unaryToken, opType, lhs, isPostfix); } /** * Parse Expression. * * {@code * MultiplicativeExpression : * UnaryExpression * MultiplicativeExpression * UnaryExpression * MultiplicativeExpression / UnaryExpression * MultiplicativeExpression % UnaryExpression * * See 11.5 * * AdditiveExpression : * MultiplicativeExpression * AdditiveExpression + MultiplicativeExpression * AdditiveExpression - MultiplicativeExpression * * See 11.6 * * ShiftExpression : * AdditiveExpression * ShiftExpression << AdditiveExpression * ShiftExpression >> AdditiveExpression * ShiftExpression >>> AdditiveExpression * * See 11.7 * * RelationalExpression : * ShiftExpression * RelationalExpression < ShiftExpression * RelationalExpression > ShiftExpression * RelationalExpression <= ShiftExpression * RelationalExpression >= ShiftExpression * RelationalExpression instanceof ShiftExpression * RelationalExpression in ShiftExpression // if !noIf * * See 11.8 * * RelationalExpression * EqualityExpression == RelationalExpression * EqualityExpression != RelationalExpression * EqualityExpression === RelationalExpression * EqualityExpression !== RelationalExpression * * See 11.9 * * BitwiseANDExpression : * EqualityExpression * BitwiseANDExpression & EqualityExpression * * BitwiseXORExpression : * BitwiseANDExpression * BitwiseXORExpression ^ BitwiseANDExpression * * BitwiseORExpression : * BitwiseXORExpression * BitwiseORExpression | BitwiseXORExpression * * See 11.10 * * LogicalANDExpression : * BitwiseORExpression * LogicalANDExpression && BitwiseORExpression * * LogicalORExpression : * LogicalANDExpression * LogicalORExpression || LogicalANDExpression * * See 11.11 * * ConditionalExpression : * LogicalORExpression * LogicalORExpression ? AssignmentExpression : AssignmentExpression * * See 11.12 * * AssignmentExpression : * ConditionalExpression * LeftHandSideExpression AssignmentOperator AssignmentExpression * * AssignmentOperator : * = *= /= %= += -= <<= >>= >>>= &= ^= |= * * See 11.13 * * Expression : * AssignmentExpression * Expression , AssignmentExpression * * See 11.14 * } * * @return Expression node. */ private Expression expression(boolean in, boolean yield, boolean await) { return expression(in, yield, await, CoverExpressionError.DENY); } private Expression expression(boolean yield, boolean await) { return expression(true, yield, await); } private Expression expression(boolean in, boolean yield, boolean await, CoverExpressionError coverExpression) { Expression assignmentExpression = assignmentExpression(in, yield, await, coverExpression); while (type == COMMARIGHT) { long commaToken = token; next(); Expression rhs = assignmentExpression(in, yield, await); assignmentExpression = new BinaryNode(commaToken, assignmentExpression, rhs); } return assignmentExpression; } private Expression parenthesizedExpressionAndArrowParameterList(boolean yield, boolean await) { long primaryToken = token; int startLine = line; assert type == LPAREN; next(); boolean canBeArrowParameterList = true; if (ES6_ARROW_FUNCTION && isES6() && type == RPAREN) { // () nextOrEOL(); expectDontAdvance(ARROW); return new ExpressionList(primaryToken, finish, List.of()); } else if (type == FUNCTION || type == LPAREN) { // Token after `(` is not valid in an arrow parameter list; therefore, it can only be a // ParenthesizedExpression and we can parse it directly without the cover grammar. // For simplicity, we only handle a few common cases here. // e.g.: `(function(){})`, (()=>{}), ((42)), etc. canBeArrowParameterList = false; } Expression assignmentExpression = null; boolean hasRestParameter = false; long commaToken = 0L; ParserContextFunctionNode coverFunction = null; ParserContextBlockNode parameterBlock = null; CoverExpressionError coverExpression = CoverExpressionError.DENY; if (canBeArrowParameterList) { coverFunction = lc.push(createParserContextArrowFunctionNode(primaryToken, startLine, false, true)); parameterBlock = lc.push(coverFunction.createParameterBlock()); coverExpression = new CoverExpressionError(); } try { while (true) { if (ES6_ARROW_FUNCTION && ES6_REST_PARAMETER && isES6() && type == ELLIPSIS) { // (a, b, ...rest) is not a valid expression, but a valid arrow function // parameter list. Since the rest parameter is always last, we know that the // cover expression has to end here with a binding identifier or pattern // followed by `)` [NoLineTerminator] `=>`. assignmentExpression = arrowFunctionRestParameter(assignmentExpression, commaToken, yield, await); hasRestParameter = true; break; } else if (ES6_ARROW_FUNCTION && ES8_TRAILING_COMMA && isES2017() && type == RPAREN && lookaheadIsArrow()) { // Trailing comma at end of arrow function parameter list break; } Expression rhs = assignmentExpression(true, yield, await, coverExpression); if (assignmentExpression == null) { assignmentExpression = rhs; } else { assert Token.descType(commaToken) == COMMARIGHT; assignmentExpression = new BinaryNode(commaToken, assignmentExpression, rhs); } if (type != COMMARIGHT) { break; } commaToken = token; next(); } } finally { if (canBeArrowParameterList) { lc.pop(parameterBlock); lc.pop(coverFunction); } } boolean arrowAhead = lookaheadIsArrow(); if (canBeArrowParameterList && !(type == RPAREN && arrowAhead)) { verifyExpression(coverExpression); } if (hasRestParameter) { // Rest parameter must be last and followed by `)` [NoLineTerminator] `=>`. expectDontAdvance(RPAREN); nextOrEOL(); expectDontAdvance(ARROW); } else { expect(RPAREN); } boolean parenthesized = true; if (canBeArrowParameterList) { if (arrowAhead) { // arrow parameter list commitArrowHead(coverFunction); parenthesized = false; } else { // parenthesized expression revertArrowHead(coverFunction); } } if (parenthesized) { assignmentExpression.makeParenthesized(Token.descPosition(primaryToken), finish); } return assignmentExpression; } private void commitArrowHead(ParserContextFunctionNode cover) { assert coverArrowFunction == null; if (cover.getYieldOrAwaitInParameters() != 0L) { throw error(AbstractParser.message(MSG_INVALID_ARROW_PARAMETER), cover.getYieldOrAwaitInParameters()); } coverArrowFunction = cover; } private void revertArrowHead(ParserContextFunctionNode cover) { cover.getParameterScope().kill(); // merge flags gathered during expression parsing into the current function's flags. lc.setCurrentFunctionFlag(cover.getFlags() & FunctionNode.ARROW_HEAD_FLAGS); } private Expression arrowFunctionRestParameter(Expression paramListExpr, long commaToken, final boolean yield, final boolean await) { final long ellipsisToken = token; assert type == ELLIPSIS; next(); final Expression pattern = bindingIdentifierOrPattern(yield, await, CONTEXT_FUNCTION_PARAMETER); final Expression restParam; if (pattern instanceof IdentNode) { restParam = ((IdentNode) pattern).setIsRestParameter(); } else { restParam = new UnaryNode(Token.recast(ellipsisToken, SPREAD_ARGUMENT), pattern); } if (paramListExpr == null) { return restParam; } else { assert Token.descType(commaToken) == COMMARIGHT; return new BinaryNode(commaToken, paramListExpr, restParam); } } private Expression expression(int minPrecedence, boolean in, boolean yield, boolean await, CoverExpressionError coverExpression) { Expression lhs; if (in && type == PRIVATE_IDENT && isPrivateFieldsIn() && lookahead() == IN) { lhs = privateIdentifierUse().setIsPrivateInCheck(); } else { lhs = unaryExpression(yield, await, coverExpression); } return expression(lhs, minPrecedence, in, yield, await); } private JoinPredecessorExpression joinPredecessorExpression(boolean yield, boolean await) { return new JoinPredecessorExpression(expression(yield, await)); } private Expression expression(Expression exprLhs, int minPrecedence, boolean in, boolean yield, boolean await) { // Get the precedence of the next operator. int precedence = type.getPrecedence(); Expression lhs = exprLhs; // While greater precedence. while (type.isOperator(in) && precedence >= minPrecedence) { // Capture the operator token. final long op = token; if (type == TERNARY) { // Skip operator. next(); // Pass expression. Middle expression of a conditional expression can be a "in" // expression - even in the contexts where "in" is not permitted. final Expression trueExpr = assignmentExpression(true, yield, await); expect(COLON); // Fail expression. final Expression falseExpr = assignmentExpression(in, yield, await); // Build up node. lhs = new TernaryNode(op, lhs, new JoinPredecessorExpression(trueExpr), new JoinPredecessorExpression(falseExpr)); } else { final TokenType opType = type; // Skip operator. next(); assert !Token.descType(op).isAssignment(); // Get the next primary expression. Expression rhs; if (in && type == PRIVATE_IDENT && isPrivateFieldsIn() && lookahead() == IN && precedence < IN.getPrecedence()) { assert opType != IN; rhs = privateIdentifierUse().setIsPrivateInCheck(); } else { rhs = unaryExpression(yield, await, CoverExpressionError.DENY); } // Get precedence of next operator. int nextPrecedence = type.getPrecedence(); // Subtask greater precedence. while (type.isOperator(in) && (nextPrecedence > precedence || (nextPrecedence == precedence && !type.isLeftAssociative()))) { rhs = expression(rhs, nextPrecedence, in, yield, await); nextPrecedence = type.getPrecedence(); } lhs = newBinaryExpression(op, lhs, rhs); } precedence = type.getPrecedence(); } return lhs; } private boolean isStartOfAssignmentPattern() { return type == LBRACKET || type == LBRACE; } private Expression assignmentExpression(boolean in, boolean yield, boolean await) { return assignmentExpression(in, yield, await, CoverExpressionError.DENY); } /** * AssignmentExpression. * *
     * AssignmentExpression[In, Yield, Await] :
     *   ConditionalExpression
     *   [+Yield] YieldExpression
     *   ArrowFunction
     *   AsyncArrowFunction
     *   LeftHandSideExpression = AssignmentExpression
     *   LeftHandSideExpression AssignmentOperator AssignmentExpression
     * 
*/ private Expression assignmentExpression(boolean in, boolean yield, boolean await, CoverExpressionError coverExpression) { if (type == YIELD && yield) { return yieldExpression(in, await); } boolean asyncArrow = isAsync() && lookaheadIsAsyncArrowParameterListStart(); // If true, one of: // 1. async ident // .. This case is handled below. // 2. async ( // .. May turn out to be a CallExpression instead of an AsyncArrowFunction; this is handled // .. in leftHandSideExpression() which returns an ExpressionList if an AsyncArrowFunction. final long startToken = token; final int startLine = line; boolean canBeAssignmentPattern = isStartOfAssignmentPattern(); CoverExpressionError coverExprLhs = canBeAssignmentPattern ? new CoverExpressionError() : CoverExpressionError.DENY; Expression exprLhs = conditionalExpression(in, yield, await, coverExprLhs); if (asyncArrow && exprLhs instanceof IdentNode && isBindingIdentifier() && lookaheadIsArrow()) { // async ident => exprLhs = primaryExpression(yield, await, CoverExpressionError.DENY); if (exprLhs instanceof IdentNode && AWAIT.getName().equals(((IdentNode) exprLhs).getName())) { throw error(AbstractParser.message(MSG_INVALID_ARROW_PARAMETER), exprLhs.getToken()); } } if (ES6_ARROW_FUNCTION && type == ARROW && isES6()) { // Look behind to check there's no LineTerminator between IDENT/RPAREN and ARROW if (lookbehindNoLineTerminatorBeforeArrow()) { return arrowFunction(startToken, startLine, exprLhs, asyncArrow); } } assert !(exprLhs instanceof ExpressionList); if (type.isAssignment()) { if (canBeAssignmentPattern && !isDestructuringLhs(exprLhs)) { // If LHS is not an AssignmentPattern, verify that it is a valid expression. verifyExpression(coverExprLhs); } final boolean isAssign = type == ASSIGN; if (isAssign) { pushDefaultName(exprLhs); } try { long assignToken = token; next(); Expression exprRhs = assignmentExpression(in, yield, await); return verifyAssignment(assignToken, exprLhs, exprRhs, coverExpression != CoverExpressionError.DENY); } finally { if (isAssign) { popDefaultName(); } } } else { if (canBeAssignmentPattern) { if (coverExpression != CoverExpressionError.DENY) { coverExpression.recordErrorFrom(coverExprLhs); } else { verifyExpression(coverExprLhs); } } return exprLhs; } } /** * ConditionalExpression. */ private Expression conditionalExpression(boolean in, boolean yield, boolean await, CoverExpressionError coverExpression) { return expression(TERNARY.getPrecedence(), in, yield, await, coverExpression); } private void verifyExpression(CoverExpressionError coverExpression) { if (coverExpression.hasError()) { throwExpressionError(coverExpression); } } private void throwExpressionError(CoverExpressionError coverExpression) { assert coverExpression.hasError(); throw error(AbstractParser.message(coverExpression.getErrorMessage()), coverExpression.getErrorToken()); } private void recordOrThrowExpressionError(String msgId, long assignToken, CoverExpressionError coverExpression) { if (coverExpression != CoverExpressionError.DENY) { coverExpression.recordExpressionError(msgId, assignToken); } else { throw error(AbstractParser.message(msgId), assignToken); } } /** * ArrowFunction. * * @param startToken start token of the ArrowParameters expression * @param functionLine start line of the arrow function * @param paramListExpr ArrowParameters expression or {@code null} for {@code ()} (empty list) */ private Expression arrowFunction(final long startToken, final int functionLine, final Expression paramListExpr, boolean async) { // caller needs to check that there's no LineTerminator between parameter list and arrow assert type != ARROW || lookbehindNoLineTerminatorBeforeArrow(); expect(ARROW); final ParserContextFunctionNode functionNode; if (coverArrowFunction == null) { functionNode = createParserContextArrowFunctionNode(startToken, functionLine, async, false); } else { functionNode = coverArrowFunction; functionNode.setCoverArrowHead(false); coverArrowFunction = null; } assert functionNode.isArrow() && !functionNode.isCoverArrowHead(); functionNode.setInternalName(lexer.stringIntern(ARROW_FUNCTION_NAME)); functionNode.setFlag(FunctionNode.IS_ANONYMOUS); lc.push(functionNode); try { final ParserContextBlockNode parameterBlock = functionNode.createParameterBlock(); lc.push(parameterBlock); Block functionBody; try { convertArrowFunctionParameterList(paramListExpr, functionNode); assert functionNode.isAsync() == async; functionBody = functionBody(functionNode); } finally { restoreBlock(parameterBlock); } verifyParameterList(functionNode); if (parameterBlock != null) { functionBody = wrapParameterBlock(parameterBlock, functionBody); } final FunctionNode function = createFunctionNode( functionNode, functionNode.getFirstToken(), functionNode.getIdent(), functionLine, functionBody); return function; } finally { lc.pop(functionNode); } } private ParserContextFunctionNode createParserContextArrowFunctionNode(long startToken, int startLine, boolean async, boolean cover) { final long functionToken = Token.recast(startToken, ARROW); final IdentNode name = null; ParserContextFunctionNode function = createParserContextFunctionNode(name, functionToken, FunctionNode.IS_ARROW, startLine); if (async) { function.setFlag(FunctionNode.IS_ASYNC); } if (cover) { function.setCoverArrowHead(true); assert coverArrowFunction == null; } return function; } private static Expression convertExpressionListToExpression(ExpressionList exprList) { if (exprList.getExpressions().isEmpty()) { return null; } else if (exprList.getExpressions().size() == 1) { return exprList.getExpressions().get(0); } else { long recastToken = Token.recast(exprList.getToken(), COMMARIGHT); Expression result = null; for (Expression expression : exprList.getExpressions()) { result = result == null ? expression : new BinaryNode(recastToken, result, expression); } return result; } } private void convertArrowFunctionParameterList(Expression paramList, ParserContextFunctionNode function) { Expression paramListExpr = paramList; if (paramListExpr instanceof ExpressionList) { // see parenthesizedExpressionAndArrowParameterList() and leftHandSideExpression() paramListExpr = convertExpressionListToExpression((ExpressionList) paramListExpr); } if (paramListExpr == null) { // empty parameter list, i.e. () => return; } final int functionLine = function.getLineNumber(); if (paramListExpr instanceof IdentNode || paramListExpr.isTokenType(ASSIGN) || isDestructuringLhs(paramListExpr) || paramListExpr.isTokenType(SPREAD_ARGUMENT)) { convertArrowParameter(paramListExpr, 0, functionLine, function); } else if (paramListExpr instanceof BinaryNode && Token.descType(paramListExpr.getToken()) == COMMARIGHT) { List params = new ArrayList<>(); Expression car = paramListExpr; do { Expression cdr = ((BinaryNode) car).getRhs(); params.add(cdr); car = ((BinaryNode) car).getLhs(); } while (car instanceof BinaryNode && Token.descType(car.getToken()) == COMMARIGHT); params.add(car); for (int i = params.size() - 1, pos = 0; i >= 0; i--, pos++) { Expression param = params.get(i); if (i != 0 && param.isTokenType(SPREAD_ARGUMENT)) { throw error(AbstractParser.message(MSG_INVALID_ARROW_PARAMETER), param.getToken()); } else { convertArrowParameter(param, pos, functionLine, function); } } } else { throw error(AbstractParser.message(MSG_EXPECTED_ARROW_PARAMETER), paramListExpr.getToken()); } } private void convertArrowParameter(Expression param, int index, int paramLine, ParserContextFunctionNode currentFunction) { assert index == currentFunction.getParameterCount(); if (param instanceof IdentNode) { IdentNode ident = (IdentNode) param; verifyStrictIdent(ident, CONTEXT_FUNCTION_PARAMETER); if (ident.isParenthesized()) { throw error(AbstractParser.message(MSG_INVALID_ARROW_PARAMETER), param.getToken()); } assert !(currentFunction.isAsync() && AWAIT.getName().equals(ident.getName())); currentFunction.addParameter(ident); return; } if (param.isTokenType(ASSIGN)) { Expression lhs = ((BinaryNode) param).getLhs(); long paramToken = lhs.getToken(); Expression initializer = ((BinaryNode) param).getRhs(); assert !(initializer instanceof IdentNode && currentFunction.isAsync() && AWAIT.getName().equals(((IdentNode) initializer).getName())); if (lhs instanceof IdentNode && !lhs.isParenthesized()) { // default parameter IdentNode ident = (IdentNode) lhs; if (isAnonymousFunctionDefinition(initializer)) { initializer = setAnonymousFunctionName(initializer, ident.getNameTS()); } addDefaultParameter(paramToken, param.getFinish(), paramLine, ident, initializer, currentFunction); return; } else if (isDestructuringLhs(lhs)) { // binding pattern with initializer verifyDestructuringParameterBindingPattern(lhs, paramToken, paramLine); addDestructuringParameter(paramToken, param.getFinish(), paramLine, lhs, initializer, currentFunction, false); } else { throw error(AbstractParser.message(MSG_INVALID_ARROW_PARAMETER), paramToken); } } else if (isDestructuringLhs(param)) { // binding pattern long paramToken = param.getToken(); verifyDestructuringParameterBindingPattern(param, paramToken, paramLine); addDestructuringParameter(paramToken, param.getFinish(), paramLine, param, null, currentFunction, false); } else if (param.isTokenType(SPREAD_ARGUMENT)) { // rest parameter if (lookbehindIsTrailingCommaInArrowParameters()) { throw error(AbstractParser.message(MSG_INVALID_ARROW_PARAMETER), param.getToken()); } Expression restParam = ((UnaryNode) param).getExpression(); if (restParam instanceof IdentNode) { IdentNode ident = ((IdentNode) restParam).setIsRestParameter(); convertArrowParameter(ident, index, paramLine, currentFunction); } else if (isDestructuringLhs(restParam)) { verifyDestructuringParameterBindingPattern(restParam, restParam.getToken(), paramLine); addDestructuringParameter(restParam.getToken(), restParam.getFinish(), paramLine, restParam, null, currentFunction, true); } else { throw error(AbstractParser.message(MSG_INVALID_ARROW_PARAMETER), param.getToken()); } } else { throw error(AbstractParser.message(MSG_INVALID_ARROW_PARAMETER), param.getToken()); } } // Checks whether arrow function parameters end with a trailing comma // (which is not allowed if rest parameter is present). private boolean lookbehindIsTrailingCommaInArrowParameters() { int idx = k - 1; while (true) { idx--; TokenType t = T(idx); switch (t) { case COMMENT: case EOL: case ARROW: case RPAREN: continue; case COMMARIGHT: return true; default: return false; } } } private boolean lookbehindNoLineTerminatorBeforeArrow() { assert type == ARROW; if (last == RPAREN) { return true; } else if (last == IDENT) { return true; } for (int i = k - 1; i >= 0; i--) { TokenType t = T(i); switch (t) { case RPAREN: case IDENT: return true; case EOL: return false; case COMMENT: continue; default: return (t.isContextualKeyword() || t.isFutureStrict()); } } return false; } private boolean lookbehindNoLineTerminatorAfterAsync() { assert type == LPAREN; return last == ASYNC; } private boolean lookaheadIsArrow() { // find ARROW, skipping over COMMENT int i = 1; for (;;) { TokenType t = T(k + i++); if (t == ARROW) { break; } else if (t == COMMENT) { continue; } else { return false; } } return true; } /** * Parse an end of line. */ private void endOfLine() { switch (type) { case SEMICOLON: case EOL: next(); break; case RPAREN: case RBRACKET: case RBRACE: case EOF: break; default: if (last == EOL) { // automatic semicolon insertion break; } /* * Provide a more helpful error message for what looks like an await expression * outside of a valid async function or top-level module scope. * * e.g.: `let test = await import('test');` */ if (last == AWAIT && isES2017()) { throw error(AbstractParser.message(MSG_INVALID_AWAIT), previousToken); } throw error(expectMessage(SEMICOLON)); } } /** * Parse an untagged template literal, or an "exec string" in scripting mode. */ private Expression templateLiteralOrExecString(boolean yield, boolean await, long primaryToken) { final int primaryLine = line; Expression templateLiteral = templateLiteral(yield, await); if (scripting) { // Synthesize a call to $EXEC(templateLiteral). final IdentNode execIdent = createIdentNode(primaryToken, finish, lexer.stringIntern(EXEC_NAME)); addIdentifierReference(EXEC_NAME); return CallNode.forCall(primaryLine, primaryToken, templateLiteral.getStart(), finish, execIdent, List.of(templateLiteral)); } return templateLiteral; } /** * Parse untagged template literal as string concatenation. */ private Expression templateLiteral(boolean yield, boolean await) { assert type == TEMPLATE || type == TEMPLATE_HEAD; final boolean noSubstitutionTemplate = type == TEMPLATE; final long startToken = token; boolean previousPauseOnRightBrace = lexer.pauseOnRightBrace; try { lexer.pauseOnRightBrace = true; LiteralNode literal = getLiteral(); if (noSubstitutionTemplate) { return literal; } List expressions = new ArrayList<>(); expressions.add(literal); TokenType lastLiteralType; do { Expression expression = templateLiteralExpression(yield, await); expressions.add(expression); lastLiteralType = type; literal = getLiteral(); expressions.add(literal); } while (lastLiteralType == TEMPLATE_MIDDLE); return TemplateLiteralNode.newUntagged(startToken, literal.getFinish(), expressions); } finally { lexer.pauseOnRightBrace = previousPauseOnRightBrace; } } /** * Parse expression inside a template literal. */ private Expression templateLiteralExpression(boolean yield, boolean await) { assert lexer.pauseOnRightBrace; Expression expression = expression(true, yield, await); if (type != RBRACE) { throw error(AbstractParser.message(MSG_UNTERMINATED_TEMPLATE_EXPRESSION), token); } lexer.scanTemplateSpan(); next(); assert type == TEMPLATE_MIDDLE || type == TEMPLATE_TAIL; return expression; } /** * Parse tagged template literal as argument list. * * @return argument list for a tag function call (template object, ...substitutions) */ private List templateLiteralArgumentList(boolean yield, boolean await) { assert type == TEMPLATE || type == TEMPLATE_HEAD; final ArrayList argumentList = new ArrayList<>(); final ArrayList rawStrings = new ArrayList<>(); final ArrayList cookedStrings = new ArrayList<>(); argumentList.add(null); // filled at the end final long templateToken = token; final boolean hasSubstitutions = type == TEMPLATE_HEAD; boolean previousPauseOnRightBrace = lexer.pauseOnRightBrace; try { lexer.pauseOnRightBrace = true; addTemplateLiteralString(rawStrings, cookedStrings); if (hasSubstitutions) { TokenType lastLiteralType; do { Expression expression = templateLiteralExpression(yield, await); argumentList.add(expression); lastLiteralType = type; addTemplateLiteralString(rawStrings, cookedStrings); } while (lastLiteralType == TEMPLATE_MIDDLE); } final Expression templateObject = TemplateLiteralNode.newTagged(templateToken, rawStrings.get(rawStrings.size() - 1).getFinish(), rawStrings, cookedStrings); argumentList.set(0, templateObject); return argumentList; } finally { lexer.pauseOnRightBrace = previousPauseOnRightBrace; } } private void addTemplateLiteralString(final ArrayList rawStrings, final ArrayList cookedStrings) { final long stringToken = token; final TruffleString rawString = lexer.valueOfRawString(stringToken); final TruffleString cookedString = lexer.valueOfTaggedTemplateString(stringToken); next(); Expression cookedExpression; if (cookedString == null) { // A tagged template string with an invalid escape sequence has value 'undefined' cookedExpression = newUndefinedLiteral(stringToken, finish); } else { cookedExpression = LiteralNode.newInstance(stringToken, cookedString); } rawStrings.add(LiteralNode.newInstance(stringToken, rawString)); cookedStrings.add(cookedExpression); } /** * Parse a module. * *
     * Module :
     *      ModuleBody?
     *
     * ModuleBody :
     *      ModuleItemList
     * 
*/ private FunctionNode module(final String moduleName) { // Make a pseudo-token for the script holding its start and length. int functionStart = Math.min(Token.descPosition(Token.withDelimiter(token)), finish); final long functionToken = Token.toDesc(FUNCTION, functionStart, source.getLength() - functionStart); final int functionLine = line; final Scope moduleScope = Scope.createModule(); final IdentNode ident = null; final ParserContextFunctionNode script = createParserContextFunctionNode( ident, functionToken, FunctionNode.IS_MODULE, functionLine, List.of(), 0, moduleScope); script.setInternalName(lexer.stringIntern(moduleName)); lc.push(script); final ParserContextModuleNode module = new ParserContextModuleNode(moduleName, moduleScope, this); final ParserContextBlockNode body = newBlock(moduleScope); functionDeclarations = new ArrayList<>(); try { moduleBody(module); // Insert a synthetic yield before the module body but after any function declarations. long yieldToken = Token.toDesc(YIELD, functionStart, 0); prependStatement(new ExpressionStatement(functionLine, yieldToken, functionLine, new UnaryNode(yieldToken, newUndefinedLiteral(yieldToken, finish)))); script.setFlag(FunctionNode.IS_GENERATOR); addFunctionDeclarations(script); } finally { functionDeclarations = null; restoreBlock(body); lc.pop(script); } moduleScope.close(); body.setFlag(Block.NEEDS_SCOPE); final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY | Block.IS_MODULE_BODY, body.getScope(), body.getStatements()); script.setLastToken(token); expect(EOF); script.setModule(module.createModule()); return createFunctionNode(script, functionToken, ident, functionLine, programBody); } /** * Parse module body. * *
     * ModuleBody :
     *      ModuleItemList
     *
     * ModuleItemList :
     *      ModuleItem
     *      ModuleItemList ModuleItem
     *
     * ModuleItem :
     *      ImportDeclaration
     *      ExportDeclaration
     *      StatementListItem
     * 
*/ private void moduleBody(ParserContextModuleNode module) { loop: while (type != EOF) { switch (type) { case EOF: break loop; case EXPORT: exportDeclaration(module); break; case IMPORT: // Ensure we are parsing an import declaration and not import.meta or import(). if (!isImportExpression()) { importDeclaration(module); break; } // fall through default: // StatementListItem boolean await = isTopLevelAwait(); statement(false, await, true, 0, false, false, false); break; } } } private boolean isTopLevelAwait() { return ES2022_TOP_LEVEL_AWAIT && env.topLevelAwait; } private boolean isImportExpression() { assert type == IMPORT; if (!isES2020()) { return false; } TokenType la = lookahead(); return la == PERIOD || la == LPAREN; } private void declareImportBinding(IdentNode ident, boolean star) { Scope moduleScope = lc.getCurrentBlock().getScope(); assert moduleScope.isModuleScope(); if (moduleScope.hasSymbol(ident.getName())) { throw error(ECMAErrors.getMessage(MSG_SYNTAX_ERROR_REDECLARE_VARIABLE, ident.getName()), ident.getToken()); } moduleScope.putSymbol(new Symbol(ident.getNameTS(), Symbol.IS_CONST | Symbol.HAS_BEEN_DECLARED | (star ? 0 : Symbol.IS_IMPORT_BINDING))); } private void declareImportBinding(IdentNode ident) { declareImportBinding(ident, false); } private void declareImportStarBinding(IdentNode ident) { declareImportBinding(ident, true); } private IdentNode importedBindingIdentifier() { return bindingIdentifier(false, isTopLevelAwait(), CONTEXT_IMPORTED_BINDING); } /** * Parse import declaration. * *
     * ImportDeclaration :
     *     import ImportClause FromClause ;
     *     import ModuleSpecifier ;
     *     import ImportClause FromClause [no LineTerminator here] AssertClause ;
     *     import ModuleSpecifier [no LineTerminator here] AssertClause ;
     * ImportClause :
     *     ImportedDefaultBinding
     *     NameSpaceImport
     *     NamedImports
     *     ImportedDefaultBinding , NameSpaceImport
     *     ImportedDefaultBinding , NamedImports
     * ImportedDefaultBinding :
     *     ImportedBinding
     * ModuleSpecifier :
     *     StringLiteral
     * ImportedBinding :
     *     BindingIdentifier
     * 
*/ private void importDeclaration(ParserContextModuleNode module) { final long importToken = token; expect(IMPORT); if (type == STRING || type == ESCSTRING) { // import ModuleSpecifier ; TruffleString moduleSpecifier = (TruffleString) getValue(); long specifierToken = token; next(); LiteralNode specifier = LiteralNode.newInstance(specifierToken, moduleSpecifier); Map attributes = Map.of(); if (env.importAttributes && ((type == WITH) || (type == ASSERT && last != EOL))) { attributes = withClause(); } module.addModuleRequest(ModuleRequest.create(moduleSpecifier, attributes)); module.addImport(new ImportNode(importToken, Token.descPosition(importToken), finish, specifier)); } else { // import ImportClause FromClause ; final List importEntries = new ArrayList<>(); final ImportClauseNode importClause; final long startToken = token; if (type == MUL) { NameSpaceImportNode namespaceNode = nameSpaceImport(); importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, namespaceNode); importEntries.add(ImportEntry.importStarAsNameSpaceFrom(namespaceNode.getBindingIdentifier().getNameTS())); } else if (type == LBRACE) { NamedImportsNode namedImportsNode = namedImports(importEntries); importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, namedImportsNode); } else if (isBindingIdentifier()) { // ImportedDefaultBinding IdentNode importedDefaultBinding = importedBindingIdentifier(); declareImportBinding(importedDefaultBinding); ImportEntry defaultImport = ImportEntry.importDefault(importedDefaultBinding.getNameTS()); importEntries.add(defaultImport); if (type == COMMARIGHT) { next(); if (type == MUL) { NameSpaceImportNode namespaceNode = nameSpaceImport(); importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding, namespaceNode); importEntries.add(ImportEntry.importStarAsNameSpaceFrom(namespaceNode.getBindingIdentifier().getNameTS())); } else if (type == LBRACE) { NamedImportsNode namedImportsNode = namedImports(importEntries); importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding, namedImportsNode); } else { // expected NameSpaceImport or NamedImports throw error(AbstractParser.message(MSG_EXPECTED_NAMED_IMPORT)); } } else { importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding); } } else { // expected ImportClause or ModuleSpecifier throw error(AbstractParser.message(MSG_EXPECTED_IMPORT)); } FromNode fromNode = fromClause(); Map attributes = Map.of(); if (env.importAttributes && ((type == WITH) || (type == ASSERT && last != EOL))) { attributes = withClause(); } module.addImport(new ImportNode(importToken, Token.descPosition(importToken), finish, importClause, fromNode)); TruffleString moduleSpecifier = fromNode.getModuleSpecifier().getValue(); ModuleRequest moduleRequest = ModuleRequest.create(moduleSpecifier, attributes); module.addModuleRequest(moduleRequest); for (int i = 0; i < importEntries.size(); i++) { module.addImportEntry(importEntries.get(i).withFrom(moduleRequest)); } } endOfLine(); } /** * Parse assert clause. * *
     *     AttributesKeyword { }
     *     AttributesKeyword { WithEntries ,opt }
     * 
*/ private Map withClause() { assert (type == ASSERT || type == WITH); next(); expect(LBRACE); Map entries = withEntries(); expect(RBRACE); return entries; } /** * Parse assert entry. * *
     * WithEntries:
     *     AttributeKey : StringLiteral
     *     AttributeKey : StringLiteral , WithEntries
     * AttributeKey:
     *     IdentifierName
     *     StringLiteral
     * 
*/ private Map withEntries() { Map entries = new LinkedHashMap<>(); while (type != RBRACE) { final long errorToken = token; TruffleString attributeKey; if (type == STRING || type == ESCSTRING) { attributeKey = (TruffleString) getValue(); next(); } else { attributeKey = getIdentifierName().getNameTS(); } expect(COLON); TruffleString value = null; if (type == STRING || type == ESCSTRING) { value = (TruffleString) getValue(); next(); } else { expect(STRING); } if (entries.containsKey(attributeKey)) { throw error(AbstractParser.message(MSG_DUPLICATE_IMPORT_ATTRIBUTE, attributeKey.toJavaStringUncached()), errorToken); } else { entries.put(attributeKey, value); } if (type == COMMARIGHT) { next(); } else { break; } } return entries; } /** *
     * NameSpaceImport :
     *      * as ImportedBinding
     * 
* * @return imported binding identifier */ private NameSpaceImportNode nameSpaceImport() { final long startToken = token; assert type == MUL; next(); expect(AS); IdentNode localNameSpace = importedBindingIdentifier(); declareImportStarBinding(localNameSpace); return new NameSpaceImportNode(startToken, Token.descPosition(startToken), finish, localNameSpace); } /** *
     * NamedImports :
     *     { }
     *     { ImportsList }
     *     { ImportsList , }
     * ImportsList :
     *     ImportSpecifier
     *     ImportsList , ImportSpecifier
     * ImportSpecifier :
     *     ImportedBinding
     *     ModuleExportName as ImportedBinding
     * ImportedBinding :
     *     BindingIdentifier
     * 
*/ private NamedImportsNode namedImports(List importEntries) { final long startToken = token; assert type == LBRACE; next(); List importSpecifiers = new ArrayList<>(); while (type != RBRACE) { boolean bindingIdentifier = isBindingIdentifier(); long nameToken = token; PropertyKey importedName = moduleExportName(); if (type == AS) { next(); IdentNode localName = importedBindingIdentifier(); importSpecifiers.add(new ImportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, importedName)); declareImportBinding(localName); importEntries.add(ImportEntry.importSpecifier(importedName.getPropertyNameTS(), localName.getNameTS())); } else if (bindingIdentifier) { IdentNode importName = (IdentNode) importedName; verifyIdent(importName, false, false); verifyStrictIdent(importName, CONTEXT_IMPORTED_BINDING); importSpecifiers.add(new ImportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, importName, null)); declareImportBinding(importName); importEntries.add(ImportEntry.importSpecifier(importName.getNameTS())); } else { // expected BindingIdentifier throw error(AbstractParser.message(MSG_EXPECTED_BINDING_IDENTIFIER), nameToken); } if (type == COMMARIGHT) { next(); } else { break; } } expect(RBRACE); return new NamedImportsNode(startToken, Token.descPosition(startToken), finish, importSpecifiers); } /** *
     * FromClause :
     *      from ModuleSpecifier
     * 
*/ private FromNode fromClause() { int fromStart = start; long fromToken = token; expect(FROM); if (type == STRING || type == ESCSTRING) { TruffleString moduleSpecifier = (TruffleString) getValue(); long specifierToken = token; next(); LiteralNode specifier = LiteralNode.newInstance(specifierToken, moduleSpecifier); return new FromNode(fromToken, fromStart, finish, specifier); } else { throw error(expectMessage(STRING)); } } /** * Parse export declaration. * *
     * ExportDeclaration :
     *     export ExportFromClause FromClause ;
     *     export ExportFromClause FromClause [no LineTerminator here] AssertClause;
     *     export NamedExports ;
     *     export VariableStatement
     *     export Declaration
     *     export default HoistableDeclaration[Default]
     *     export default ClassDeclaration[Default]
     *     export default [lookahead !in {function, class}] AssignmentExpression[In] ;
     * 
*/ private void exportDeclaration(ParserContextModuleNode module) { final long exportToken = token; Map attributes = Map.of(); expect(EXPORT); final boolean yield = false; final boolean await = isTopLevelAwait(); switch (type) { case MUL: { next(); PropertyKey exportName = null; if (type == AS && isES2020()) { next(); exportName = moduleExportName(); } FromNode from = fromClause(); if (env.importAttributes && ((type == WITH) || (type == ASSERT && last != EOL))) { attributes = withClause(); } TruffleString moduleRequest = from.getModuleSpecifier().getValue(); module.addModuleRequest(ModuleRequest.create(moduleRequest, attributes)); module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, exportName, from, attributes)); endOfLine(); break; } case LBRACE: { NamedExportsNode exportClause = namedExports(); FromNode from = null; if (type == FROM) { from = fromClause(); if (env.importAttributes && ((type == WITH) || (type == ASSERT && last != EOL))) { attributes = withClause(); } TruffleString moduleRequest = from.getModuleSpecifier().getValue(); module.addModuleRequest(ModuleRequest.create(moduleRequest, attributes)); } else { for (ExportSpecifierNode export : exportClause.getExportSpecifiers()) { if (!(export.getIdentifier() instanceof IdentNode)) { throw error("ReferencedBindings of NamedExports cannot contain StringLiteral", ((Node) export.getIdentifier()).getToken()); } } } module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, exportClause, from, attributes)); endOfLine(); break; } case DEFAULT: { next(); Expression assignmentExpression; IdentNode ident = null; int lineNumber = line; long rhsToken = token; boolean hoistableDeclaration = false; switch (type) { case FUNCTION: assignmentExpression = functionDeclaration(false, true, false, yield, await, true); hoistableDeclaration = true; break; case CLASS: case AT: assignmentExpression = classDeclaration(yield, await, true); ident = ((ClassNode) assignmentExpression).getIdent(); break; default: if (isAsync() && lookaheadIsAsyncFunction()) { assignmentExpression = asyncFunctionDeclaration(false, true, yield, await, true); hoistableDeclaration = true; break; } assignmentExpression = assignmentExpression(true, yield, await); endOfLine(); break; } if (hoistableDeclaration) { FunctionNode functionNode = (FunctionNode) assignmentExpression; assert functionNode.isDeclared(); if (!functionNode.isAnonymous()) { ident = functionNode.getIdent(); } } if (ident == null) { ident = new IdentNode(Token.recast(rhsToken, IDENT), finish, Module.DEFAULT_EXPORT_BINDING_NAME); if (isAnonymousFunctionDefinition(assignmentExpression)) { assignmentExpression = setAnonymousFunctionName(assignmentExpression, Module.DEFAULT_NAME); } } VarNode varNode = new VarNode(lineNumber, Token.recast(rhsToken, hoistableDeclaration ? VAR : LET), finish, ident, assignmentExpression, (hoistableDeclaration ? 0 : VarNode.IS_LET) | VarNode.IS_EXPORT); declareVar(lc.getCurrentScope(), varNode); if (hoistableDeclaration) { functionDeclarations.add(varNode); } else { lc.appendStatementToCurrentNode(varNode); } module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, ident, assignmentExpression, true)); break; } case VAR: case LET: case CONST: { List statements = lc.getCurrentBlock().getStatements(); int previousEnd = statements.size(); variableStatement(type, yield, await); for (Statement statement : statements.subList(previousEnd, statements.size())) { if (statement instanceof VarNode) { VarNode varNode = (VarNode) statement; module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, varNode.getName(), varNode)); } } break; } case CLASS: case AT: { ClassNode classDeclaration = classDeclaration(yield, await, false); module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, classDeclaration.getIdent(), classDeclaration, false)); break; } case FUNCTION: { FunctionNode functionDeclaration = (FunctionNode) functionDeclaration(true, true, false, yield, await, false); module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, functionDeclaration.getIdent(), functionDeclaration, false)); break; } default: if (isAsync() && lookaheadIsAsyncFunction()) { FunctionNode functionDeclaration = (FunctionNode) asyncFunctionDeclaration(true, true, yield, await, false); module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, functionDeclaration.getIdent(), functionDeclaration, false)); break; } throw error(AbstractParser.message(MSG_INVALID_EXPORT), token); } } /** * Parse a module export name. * *
     * ModuleExportName :
     *     IdentifierName
     *     StringLiteral
     * 
*/ private PropertyKey moduleExportName() { if (type == STRING || type == ESCSTRING) { TruffleString name = (TruffleString) getValue(); long nameToken = token; if (!TruffleString.IsValidNode.getUncached().execute(name, TruffleString.Encoding.UTF_16)) { throw error("Malformed export name", nameToken); } next(); return LiteralNode.newInstance(nameToken, name); } else { return getIdentifierName(); } } /** * Parse a named exports clause. * *
     * NamedExports :
     *     { }
     *     { ExportsList }
     *     { ExportsList , }
     * ExportsList :
     *     ExportSpecifier
     *     ExportsList , ExportSpecifier
     * ExportSpecifier :
     *     ModuleExportName
     *     ModuleExportName as ModuleExportName
     * 
* * @return a list of ExportSpecifiers */ private NamedExportsNode namedExports() { final long startToken = token; assert type == LBRACE; next(); List exports = new ArrayList<>(); long reservedWordToken = 0L; while (type != RBRACE) { long nameToken = token; TokenType nameType = type; PropertyKey localName = moduleExportName(); if (isReservedWord(nameType) || (localName instanceof IdentNode && isEscapedIdent((IdentNode) localName) && (isReservedWordSequence(localName.getPropertyName()) || isFutureStrictName((IdentNode) localName)))) { // Reserved words are allowed iff the ExportClause is followed by a FromClause. // Remember the first reserved word and throw an error if this is not the case. if (reservedWordToken == 0L) { reservedWordToken = nameToken; } } if (type == AS) { next(); PropertyKey exportName = moduleExportName(); exports.add(new ExportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, exportName)); } else { exports.add(new ExportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, null)); } if (type == COMMARIGHT) { next(); } else { break; } } expect(RBRACE); if (reservedWordToken != 0L && type != FROM) { throw error(expectMessage(IDENT, reservedWordToken), reservedWordToken); } return new NamedExportsNode(startToken, Token.descPosition(startToken), finish, exports); } private static boolean isReservedWord(TokenType type) { return type.getKind() == TokenKind.KEYWORD || type.getKind() == TokenKind.FUTURE || type.getKind() == TokenKind.FUTURESTRICT; } @Override public String toString() { return "'JavaScript Parsing'"; } private void markEval() { lc.setCurrentFunctionFlag(FunctionNode.HAS_EVAL | FunctionNode.HAS_SCOPE_BLOCK); lc.getCurrentScope().setHasEval(); } private void prependStatement(final Statement statement) { lc.prependStatementToCurrentNode(statement); } private void appendStatement(final Statement statement) { lc.appendStatementToCurrentNode(statement); } private void markSuperProperty() { ParserContextFunctionNode currentFunction = lc.getCurrentNonArrowFunction(); if (currentFunction.isMethod()) { currentFunction.setFlag(FunctionNode.USES_SUPER); addIdentifierReference(SUPER.getName()); addIdentifierReference(THIS.getName()); } } private void markSuperCall() { ParserContextFunctionNode fn = lc.getCurrentNonArrowFunction(); if (!fn.isProgram()) { assert fn.isDerivedConstructor(); fn.setFlag(FunctionNode.HAS_DIRECT_SUPER); addIdentifierReference(THIS.getName()); } } private void markThis() { lc.setCurrentFunctionFlag(FunctionNode.USES_THIS); addIdentifierReference(THIS.getName()); } private void markNewTarget() { if (!lc.getCurrentScope().inFunction()) { // `new.target` in script or module (may be wrapped in an arrow function or eval). throw error(AbstractParser.message(MSG_NEW_TARGET_IN_FUNCTION), token); } ParserContextFunctionNode fn = lc.getCurrentNonArrowFunction(); if (!fn.isProgram()) { fn.setFlag(FunctionNode.USES_NEW_TARGET); } addIdentifierReference(NEW_TARGET_NAME); } private static boolean markApplyArgumentsCall(final ParserContext lc, List arguments) { assert arguments.size() == 2 && arguments.get(1) instanceof IdentNode && ((IdentNode) arguments.get(1)).isArguments(); ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (!currentFunction.isArrow()) { currentFunction.setFlag(FunctionNode.HAS_APPLY_ARGUMENTS_CALL); arguments.set(1, ((IdentNode) arguments.get(1)).setIsApplyArguments()); return true; } return false; } private boolean isAwait() { return ES8_ASYNC_FUNCTION && isES2017() && type == AWAIT; } private boolean isAsync() { return ES8_ASYNC_FUNCTION && isES2017() && type == ASYNC; } private boolean lookaheadIsAsyncArrowParameterListStart() { assert isAsync(); // find [no LineTerminator here] (IDENT || LPAREN) int i = 1; for (;;) { TokenType t = T(k + i++); if (t == LPAREN) { break; } else if (t == IDENT) { break; } else if (t.isContextualKeyword()) { break; } else if (t == COMMENT) { continue; } else { return false; } } return true; } private boolean lookaheadIsAsyncFunction() { assert isAsync(); // find [no LineTerminator here] FUNCTION for (int i = 1;; i++) { long currentToken = getToken(k + i); TokenType t = Token.descType(currentToken); switch (t) { case COMMENT: continue; case FUNCTION: return true; default: return false; } } } private boolean lookaheadIsAsyncMethod(boolean allowPrivate) { assert isAsync(); // find [no LineTerminator here] ClassElementName // find [no LineTerminator here] * for (int i = 1;; i++) { long currentToken = getToken(k + i); TokenType t = Token.descType(currentToken); if (t == COMMENT) { continue; } else if (t == EOL) { return false; } else { return isPropertyName(currentToken) || t == MUL || (allowPrivate && t == TokenType.PRIVATE_IDENT); } } } /** * Parse a decorator list. * *
     * DecoratorList[Yield, Await] :
     *      DecoratorList[?Yield, ?Await]opt Decorator[?Yield, ?Await]
     *
     * Decorator[Yield, Await] :
     *      {@literal @} DecoratorMemberExpression[?Yield, ?Await]
     *      {@literal @} DecoratorParenthesizedExpression[?Yield, ?Await]
     *      {@literal @} DecoratorCallExpression[?Yield, ?Await]
     *
     * DecoratorMemberExpression[Yield, Await] :
     *      IdentifierReference[?Yield, ?Await]
     *      PrivateIdentifier
     *      DecoratorMemberExpression[?Yield, ?Await] . IdentifierName
     *
     * DecoratorCallExpression[Yield, Await] :
     *      DecoratorMemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await]
     *
     * DecoratorParenthesizedExpression[Yield, Await] :
     *      ( Expression[+In, ?Yield, ?Await] )
     *
     * ClassDeclaration[Yield, Await, Default] :
     *      DecoratorList[?Yield, ?Await]opt class BindingIdentifier[?Yield, ?Await] ClassTail[?Yield, ?Await]
     *      [+Default] DecoratorList[?Yield, ?Await]opt class ClassTail[?Yield, ?Await]
     * 
*/ public List decoratorList(boolean yield, boolean await) { assert isClassDecorators(); List decoratorList = new ArrayList<>(); while (type == AT) { next(); Expression decoratorExpression; if (type == LPAREN) { next(); decoratorExpression = expression(true, yield, await); expect(RPAREN); decoratorList.add(decoratorExpression); continue; } else { if (type == PRIVATE_IDENT) { decoratorExpression = privateIdentifierUse(); } else { decoratorExpression = identifierReference(yield, await); } } long callToken = token; while (type == PERIOD) { next(); final boolean isPrivate = type == TokenType.PRIVATE_IDENT; final IdentNode property; if (type == PRIVATE_IDENT) { property = privateIdentifierUse(); } else { property = getIdentifierName(); } decoratorExpression = new AccessNode(callToken, finish, decoratorExpression, property.getNameTS(), false, isPrivate, false, false); } if (type == LPAREN) { final int callLine = line; callToken = token; final List arguments = argumentList(yield, await); decoratorExpression = CallNode.forCall(callLine, callToken, decoratorExpression.getStart(), finish, decoratorExpression, arguments); } decoratorList.add(decoratorExpression); } return decoratorList; } /** * Parse and return an expression. Errors will be thrown and the error manager will contain * information if parsing should fail. * * @return expression node resulting from successful parse */ public Expression parseExpression() { try { prepareLexer(0, source.getLength()); scanFirstToken(); final long functionToken = Token.toDesc(FUNCTION, 0, 0); final int functionLine = line; final Scope dummyFunctionScope = Scope.createFunctionBody(null); final ParserContextFunctionNode dummyFunction = createParserContextFunctionNode( null, functionToken, FunctionNode.IS_ANONYMOUS, functionLine, List.of(), 0, dummyFunctionScope); lc.push(dummyFunction); final ParserContextBlockNode body = newBlock(dummyFunctionScope); try { return expression(false, false); } finally { restoreBlock(body); lc.pop(dummyFunction); } } catch (ParserException e) { handleParseException(e); return null; } } }