/js/src/frontend/Parser.cpp
C++ | 7255 lines | 5014 code | 831 blank | 1410 comment | 1491 complexity | f2af8849c9c54a8bfe4d66838e654ac0 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause, GPL-2.0, Apache-2.0, MIT, JSON, 0BSD, BSD-2-Clause, LGPL-3.0, AGPL-1.0
Large files files are truncated, but you can click here to view the full file
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=99:
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- /*
- * JS parser.
- *
- * This is a recursive-descent parser for the JavaScript language specified by
- * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
- * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
- * induced by the recursive parsing (not precise syntax trees, see Parser.h).
- * After tree construction, it rewrites trees to fold constants and evaluate
- * compile-time expressions. Finally, it calls js::frontend::EmitTree (see
- * BytecodeEmitter.h) to generate bytecode.
- *
- * This parser attempts no error recovery.
- */
- #include "frontend/Parser.h"
- #include <stdlib.h>
- #include <string.h>
- #include "jstypes.h"
- #include "jsutil.h"
- #include "jsapi.h"
- #include "jsarray.h"
- #include "jsatom.h"
- #include "jscntxt.h"
- #include "jsversion.h"
- #include "jsfun.h"
- #include "jsgc.h"
- #include "jsinterp.h"
- #include "jsiter.h"
- #include "jslock.h"
- #include "jsnum.h"
- #include "jsobj.h"
- #include "jsopcode.h"
- #include "jsscope.h"
- #include "jsscript.h"
- #include "jsstr.h"
- #include "frontend/BytecodeEmitter.h"
- #include "frontend/FoldConstants.h"
- #include "frontend/ParseMaps.h"
- #include "frontend/TokenStream.h"
- #include "gc/Marking.h"
- #if JS_HAS_XML_SUPPORT
- #include "jsxml.h"
- #endif
- #include "jsatominlines.h"
- #include "jsscriptinlines.h"
- #include "frontend/BytecodeEmitter-inl.h"
- #include "frontend/ParseMaps-inl.h"
- #include "frontend/ParseNode-inl.h"
- #include "vm/RegExpObject-inl.h"
- using namespace js;
- using namespace js::gc;
- using namespace js::frontend;
- /*
- * Insist that the next token be of type tt, or report errno and return null.
- * NB: this macro uses cx and ts from its lexical environment.
- */
- #define MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, __flags) \
- JS_BEGIN_MACRO \
- if (tokenStream.getToken((__flags)) != tt) { \
- reportErrorNumber(NULL, JSREPORT_ERROR, errno); \
- return NULL; \
- } \
- JS_END_MACRO
- #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
- Parser::Parser(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
- StackFrame *cfp, bool foldConstants)
- : AutoGCRooter(cx, PARSER),
- context(cx),
- tokenStream(cx, prin, originPrin),
- principals(NULL),
- originPrincipals(NULL),
- callerFrame(cfp),
- callerVarObj(cfp ? &cfp->varObj() : NULL),
- allocator(cx),
- functionCount(0),
- traceListHead(NULL),
- tc(NULL),
- keepAtoms(cx->runtime),
- foldConstants(foldConstants)
- {
- cx->activeCompilations++;
- PodArrayZero(tempFreeList);
- setPrincipals(prin, originPrin);
- JS_ASSERT_IF(cfp, cfp->isScriptFrame());
- }
- bool
- Parser::init(const jschar *base, size_t length, const char *filename, unsigned lineno,
- JSVersion version)
- {
- JSContext *cx = context;
- if (!cx->ensureParseMapPool())
- return false;
- tempPoolMark = cx->tempLifoAlloc().mark();
- if (!tokenStream.init(base, length, filename, lineno, version)) {
- cx->tempLifoAlloc().release(tempPoolMark);
- return false;
- }
- return true;
- }
- Parser::~Parser()
- {
- JSContext *cx = context;
- if (principals)
- JS_DropPrincipals(cx->runtime, principals);
- if (originPrincipals)
- JS_DropPrincipals(cx->runtime, originPrincipals);
- cx->tempLifoAlloc().release(tempPoolMark);
- cx->activeCompilations--;
- }
- void
- Parser::setPrincipals(JSPrincipals *prin, JSPrincipals *originPrin)
- {
- JS_ASSERT(!principals && !originPrincipals);
- principals = prin;
- if (principals)
- JS_HoldPrincipals(principals);
- originPrincipals = originPrin;
- if (originPrincipals)
- JS_HoldPrincipals(originPrincipals);
- }
- ObjectBox *
- Parser::newObjectBox(JSObject *obj)
- {
- JS_ASSERT(obj && !IsPoisonedPtr(obj));
- /*
- * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
- * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
- * arenas containing the entries must be alive until we are done with
- * scanning, parsing and code generation for the whole script or top-level
- * function.
- */
- ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>();
- if (!objbox) {
- js_ReportOutOfMemory(context);
- return NULL;
- }
- objbox->traceLink = traceListHead;
- traceListHead = objbox;
- objbox->emitLink = NULL;
- objbox->object = obj;
- objbox->isFunctionBox = false;
- return objbox;
- }
- FunctionBox *
- Parser::newFunctionBox(JSObject *obj, ParseNode *fn, TreeContext *tc)
- {
- JS_ASSERT(obj && !IsPoisonedPtr(obj));
- JS_ASSERT(obj->isFunction());
- /*
- * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
- * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
- * arenas containing the entries must be alive until we are done with
- * scanning, parsing and code generation for the whole script or top-level
- * function.
- */
- FunctionBox *funbox = context->tempLifoAlloc().newPod<FunctionBox>();
- if (!funbox) {
- js_ReportOutOfMemory(context);
- return NULL;
- }
- funbox->traceLink = traceListHead;
- traceListHead = funbox;
- funbox->emitLink = NULL;
- funbox->object = obj;
- funbox->isFunctionBox = true;
- funbox->node = fn;
- funbox->siblings = tc->functionList;
- tc->functionList = funbox;
- ++tc->parser->functionCount;
- funbox->kids = NULL;
- funbox->parent = tc->funbox;
- new (&funbox->bindings) Bindings(context);
- funbox->queued = false;
- funbox->inLoop = false;
- for (StmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
- if (STMT_IS_LOOP(stmt)) {
- funbox->inLoop = true;
- break;
- }
- }
- funbox->level = tc->staticLevel;
- funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
- if (tc->innermostWith)
- funbox->tcflags |= TCF_IN_WITH;
- if (!tc->inFunction()) {
- JSObject *scope = tc->scopeChain();
- while (scope) {
- if (scope->isWith())
- funbox->tcflags |= TCF_IN_WITH;
- scope = scope->enclosingScope();
- }
- }
- return funbox;
- }
- void
- Parser::trace(JSTracer *trc)
- {
- ObjectBox *objbox = traceListHead;
- while (objbox) {
- MarkObjectRoot(trc, &objbox->object, "parser.object");
- if (objbox->isFunctionBox)
- static_cast<FunctionBox *>(objbox)->bindings.trace(trc);
- objbox = objbox->traceLink;
- }
- for (TreeContext *tc = this->tc; tc; tc = tc->parent)
- tc->trace(trc);
- }
- static bool
- GenerateBlockIdForStmtNode(ParseNode *pn, TreeContext *tc)
- {
- JS_ASSERT(tc->topStmt);
- JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
- JS_ASSERT(pn->isKind(PNK_STATEMENTLIST) || pn->isKind(PNK_LEXICALSCOPE));
- if (!GenerateBlockId(tc, tc->topStmt->blockid))
- return false;
- pn->pn_blockid = tc->topStmt->blockid;
- return true;
- }
- /*
- * Parse a top-level JS script.
- */
- ParseNode *
- Parser::parse(JSObject *chain)
- {
- /*
- * Protect atoms from being collected by a GC activation, which might
- * - nest on this thread due to out of memory (the so-called "last ditch"
- * GC attempted within js_NewGCThing), or
- * - run for any reason on another thread if this thread is suspended on
- * an object lock before it finishes generating bytecode into a script
- * protected from the GC by a root or a stack frame reference.
- */
- TreeContext globaltc(this);
- if (!globaltc.init(context))
- return NULL;
- globaltc.setScopeChain(chain);
- if (!GenerateBlockId(&globaltc, globaltc.bodyid))
- return NULL;
- ParseNode *pn = statements();
- if (pn) {
- if (!tokenStream.matchToken(TOK_EOF)) {
- reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
- pn = NULL;
- } else if (foldConstants) {
- if (!FoldConstants(context, pn, &globaltc))
- pn = NULL;
- }
- }
- return pn;
- }
- JS_STATIC_ASSERT(UpvarCookie::FREE_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
- /*
- * Insist on a final return before control flows out of pn. Try to be a bit
- * smart about loops: do {...; return e2;} while(0) at the end of a function
- * that contains an early return e1 will get a strict warning. Similarly for
- * iloops: while (true){...} is treated as though ... returns.
- */
- #define ENDS_IN_OTHER 0
- #define ENDS_IN_RETURN 1
- #define ENDS_IN_BREAK 2
- static int
- HasFinalReturn(ParseNode *pn)
- {
- ParseNode *pn2, *pn3;
- unsigned rv, rv2, hasDefault;
- switch (pn->getKind()) {
- case PNK_STATEMENTLIST:
- if (!pn->pn_head)
- return ENDS_IN_OTHER;
- return HasFinalReturn(pn->last());
- case PNK_IF:
- if (!pn->pn_kid3)
- return ENDS_IN_OTHER;
- return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
- case PNK_WHILE:
- pn2 = pn->pn_left;
- if (pn2->isKind(PNK_TRUE))
- return ENDS_IN_RETURN;
- if (pn2->isKind(PNK_NUMBER) && pn2->pn_dval)
- return ENDS_IN_RETURN;
- return ENDS_IN_OTHER;
- case PNK_DOWHILE:
- pn2 = pn->pn_right;
- if (pn2->isKind(PNK_FALSE))
- return HasFinalReturn(pn->pn_left);
- if (pn2->isKind(PNK_TRUE))
- return ENDS_IN_RETURN;
- if (pn2->isKind(PNK_NUMBER)) {
- if (pn2->pn_dval == 0)
- return HasFinalReturn(pn->pn_left);
- return ENDS_IN_RETURN;
- }
- return ENDS_IN_OTHER;
- case PNK_FOR:
- pn2 = pn->pn_left;
- if (pn2->isArity(PN_TERNARY) && !pn2->pn_kid2)
- return ENDS_IN_RETURN;
- return ENDS_IN_OTHER;
- case PNK_SWITCH:
- rv = ENDS_IN_RETURN;
- hasDefault = ENDS_IN_OTHER;
- pn2 = pn->pn_right;
- if (pn2->isKind(PNK_LEXICALSCOPE))
- pn2 = pn2->expr();
- for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
- if (pn2->isKind(PNK_DEFAULT))
- hasDefault = ENDS_IN_RETURN;
- pn3 = pn2->pn_right;
- JS_ASSERT(pn3->isKind(PNK_STATEMENTLIST));
- if (pn3->pn_head) {
- rv2 = HasFinalReturn(pn3->last());
- if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
- /* Falling through to next case or default. */;
- else
- rv &= rv2;
- }
- }
- /* If a final switch has no default case, we judge it harshly. */
- rv &= hasDefault;
- return rv;
- case PNK_BREAK:
- return ENDS_IN_BREAK;
- case PNK_WITH:
- return HasFinalReturn(pn->pn_right);
- case PNK_RETURN:
- return ENDS_IN_RETURN;
- case PNK_COLON:
- case PNK_LEXICALSCOPE:
- return HasFinalReturn(pn->expr());
- case PNK_THROW:
- return ENDS_IN_RETURN;
- case PNK_TRY:
- /* If we have a finally block that returns, we are done. */
- if (pn->pn_kid3) {
- rv = HasFinalReturn(pn->pn_kid3);
- if (rv == ENDS_IN_RETURN)
- return rv;
- }
- /* Else check the try block and any and all catch statements. */
- rv = HasFinalReturn(pn->pn_kid1);
- if (pn->pn_kid2) {
- JS_ASSERT(pn->pn_kid2->isArity(PN_LIST));
- for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
- rv &= HasFinalReturn(pn2);
- }
- return rv;
- case PNK_CATCH:
- /* Check this catch block's body. */
- return HasFinalReturn(pn->pn_kid3);
- case PNK_LET:
- /* Non-binary let statements are let declarations. */
- if (!pn->isArity(PN_BINARY))
- return ENDS_IN_OTHER;
- return HasFinalReturn(pn->pn_right);
- default:
- return ENDS_IN_OTHER;
- }
- }
- static JSBool
- ReportBadReturn(JSContext *cx, TreeContext *tc, ParseNode *pn, unsigned flags, unsigned errnum,
- unsigned anonerrnum)
- {
- JSAutoByteString name;
- if (tc->fun()->atom) {
- if (!js_AtomToPrintableString(cx, tc->fun()->atom, &name))
- return false;
- } else {
- errnum = anonerrnum;
- }
- return ReportCompileErrorNumber(cx, TS(tc->parser), pn, flags, errnum, name.ptr());
- }
- static JSBool
- CheckFinalReturn(JSContext *cx, TreeContext *tc, ParseNode *pn)
- {
- JS_ASSERT(tc->inFunction());
- return HasFinalReturn(pn) == ENDS_IN_RETURN ||
- ReportBadReturn(cx, tc, pn, JSREPORT_WARNING | JSREPORT_STRICT,
- JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
- }
- /*
- * Check that it is permitted to assign to lhs. Strict mode code may not
- * assign to 'eval' or 'arguments'.
- */
- static bool
- CheckStrictAssignment(JSContext *cx, TreeContext *tc, ParseNode *lhs)
- {
- if (tc->needStrictChecks() && lhs->isKind(PNK_NAME)) {
- JSAtom *atom = lhs->pn_atom;
- JSAtomState *atomState = &cx->runtime->atomState;
- if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
- JSAutoByteString name;
- if (!js_AtomToPrintableString(cx, atom, &name) ||
- !ReportStrictModeError(cx, TS(tc->parser), tc, lhs, JSMSG_DEPRECATED_ASSIGN,
- name.ptr())) {
- return false;
- }
- }
- }
- return true;
- }
- /*
- * Check that it is permitted to introduce a binding for atom. Strict mode
- * forbids introducing new definitions for 'eval', 'arguments', or for any
- * strict mode reserved keyword. Use pn for reporting error locations, or use
- * tc's token stream if pn is NULL.
- */
- bool
- CheckStrictBinding(JSContext *cx, TreeContext *tc, PropertyName *name, ParseNode *pn)
- {
- if (!tc->needStrictChecks())
- return true;
- JSAtomState *atomState = &cx->runtime->atomState;
- if (name == atomState->evalAtom ||
- name == atomState->argumentsAtom ||
- FindKeyword(name->charsZ(), name->length()))
- {
- JSAutoByteString bytes;
- if (!js_AtomToPrintableString(cx, name, &bytes))
- return false;
- return ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, bytes.ptr());
- }
- return true;
- }
- static bool
- ReportBadParameter(JSContext *cx, TreeContext *tc, JSAtom *name, unsigned errorNumber)
- {
- Definition *dn = tc->decls.lookupFirst(name);
- JSAutoByteString bytes;
- return js_AtomToPrintableString(cx, name, &bytes) &&
- ReportStrictModeError(cx, TS(tc->parser), tc, dn, errorNumber, bytes.ptr());
- }
- /*
- * In strict mode code, all parameter names must be distinct, must not be
- * strict mode reserved keywords, and must not be 'eval' or 'arguments'. We
- * must perform these checks here, and not eagerly during parsing, because a
- * function's body may turn on strict mode for the function head.
- */
- static bool
- CheckStrictParameters(JSContext *cx, TreeContext *tc)
- {
- JS_ASSERT(tc->inFunction());
- if (!tc->needStrictChecks() || tc->bindings.numArgs() == 0)
- return true;
- JSAtom *argumentsAtom = cx->runtime->atomState.argumentsAtom;
- JSAtom *evalAtom = cx->runtime->atomState.evalAtom;
- /* name => whether we've warned about the name already */
- HashMap<JSAtom *, bool> parameters(cx);
- if (!parameters.init(tc->bindings.numArgs()))
- return false;
- /* Start with lastVariable(), not lastArgument(), for destructuring. */
- for (Shape::Range r = tc->bindings.lastVariable(); !r.empty(); r.popFront()) {
- jsid id = r.front().propid();
- if (!JSID_IS_ATOM(id))
- continue;
- JSAtom *name = JSID_TO_ATOM(id);
- if (name == argumentsAtom || name == evalAtom) {
- if (!ReportBadParameter(cx, tc, name, JSMSG_BAD_BINDING))
- return false;
- }
- if (tc->inStrictMode() && FindKeyword(name->charsZ(), name->length())) {
- /*
- * JSOPTION_STRICT is supposed to warn about future keywords, too,
- * but we took care of that in the scanner.
- */
- JS_ALWAYS_TRUE(!ReportBadParameter(cx, tc, name, JSMSG_RESERVED_ID));
- return false;
- }
- /*
- * Check for a duplicate parameter: warn or report an error exactly
- * once for each duplicated parameter.
- */
- if (HashMap<JSAtom *, bool>::AddPtr p = parameters.lookupForAdd(name)) {
- if (!p->value && !ReportBadParameter(cx, tc, name, JSMSG_DUPLICATE_FORMAL))
- return false;
- p->value = true;
- } else {
- if (!parameters.add(p, name, false))
- return false;
- }
- }
- return true;
- }
- static bool
- BindLocalVariable(JSContext *cx, TreeContext *tc, ParseNode *pn, BindingKind kind)
- {
- JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
- unsigned index = tc->bindings.numVars();
- if (!tc->bindings.add(cx, RootedVarAtom(cx, pn->pn_atom), kind))
- return false;
- pn->pn_cookie.set(tc->staticLevel, index);
- pn->pn_dflags |= PND_BOUND;
- return true;
- }
- ParseNode *
- Parser::functionBody(FunctionBodyType type)
- {
- JS_ASSERT(tc->inFunction());
- StmtInfo stmtInfo(context);
- PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
- stmtInfo.flags = SIF_BODY_BLOCK;
- unsigned oldflags = tc->flags;
- tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
- ParseNode *pn;
- if (type == StatementListBody) {
- pn = statements();
- } else {
- JS_ASSERT(type == ExpressionBody);
- JS_ASSERT(JS_HAS_EXPR_CLOSURES);
- pn = UnaryNode::create(PNK_RETURN, tc);
- if (pn) {
- pn->pn_kid = assignExpr();
- if (!pn->pn_kid) {
- pn = NULL;
- } else {
- if (tc->flags & TCF_FUN_IS_GENERATOR) {
- ReportBadReturn(context, tc, pn, JSREPORT_ERROR,
- JSMSG_BAD_GENERATOR_RETURN,
- JSMSG_BAD_ANON_GENERATOR_RETURN);
- pn = NULL;
- } else {
- pn->setOp(JSOP_RETURN);
- pn->pn_pos.end = pn->pn_kid->pn_pos.end;
- }
- }
- }
- }
- if (pn) {
- JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
- PopStatementTC(tc);
- /* Check for falling off the end of a function that returns a value. */
- if (context->hasStrictOption() && (tc->flags & TCF_RETURN_EXPR) &&
- !CheckFinalReturn(context, tc, pn)) {
- pn = NULL;
- }
- }
- /*
- * Check CheckStrictParameters before arguments logic below adds
- * 'arguments' to bindings.
- */
- if (!CheckStrictParameters(context, tc))
- return NULL;
- RootedVar<PropertyName*> const arguments(context, context->runtime->atomState.argumentsAtom);
- /*
- * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope
- * operation which means it aliases any bindings with the same name.
- * Due to the implicit declaration mechanism (below), 'arguments' will not
- * have decls and, even if it did, they will not be noted as closed in the
- * emitter. Thus, in the corner case of function-statement-overridding-
- * arguments, flag the whole scope as dynamic.
- */
- if (FuncStmtSet *set = tc->funcStmts) {
- for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) {
- PropertyName *name = r.front()->asPropertyName();
- if (name == arguments)
- tc->noteBindingsAccessedDynamically();
- else if (Definition *dn = tc->decls.lookupFirst(name))
- dn->pn_dflags |= PND_CLOSED;
- }
- }
- /*
- * As explained by the TCF_ARGUMENTS_HAS_LOCAL_BINDING comment, turn uses
- * of 'arguments' into bindings. Use of 'arguments' should never escape a
- * nested function as an upvar.
- */
- for (AtomDefnRange r = tc->lexdeps->all(); !r.empty(); r.popFront()) {
- JSAtom *atom = r.front().key();
- Definition *dn = r.front().value();
- JS_ASSERT(dn->isPlaceholder());
- if (atom == arguments) {
- /*
- * Turn 'dn' into a proper definition so uses will be bound as
- * GETLOCAL in the emitter.
- */
- if (!BindLocalVariable(context, tc, dn, VARIABLE))
- return NULL;
- dn->setOp(JSOP_GETLOCAL);
- dn->pn_dflags &= ~PND_PLACEHOLDER;
- /* NB: this leaves r invalid so we must break immediately. */
- tc->lexdeps->remove(arguments);
- break;
- }
- }
- /*
- * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
- * forces an 'arguments' binding.
- */
- if (tc->bindingsAccessedDynamically() && !tc->bindings.hasBinding(context, arguments)) {
- if (!tc->bindings.addVariable(context, arguments))
- return NULL;
- }
- /*
- * Now that all possible 'arguments' bindings have been added, note whether
- * 'arguments' has a local binding and whether it unconditionally needs an
- * arguments object.
- */
- BindingKind bindKind = tc->bindings.lookup(context, arguments, NULL);
- if (bindKind == VARIABLE || bindKind == CONSTANT) {
- tc->noteArgumentsHasLocalBinding();
- /* Dynamic scope access destroys all hope of optimization. */
- if (tc->bindingsAccessedDynamically())
- tc->noteDefinitelyNeedsArgsObj();
- /*
- * Check whether any parameters have been assigned within this
- * function. In strict mode parameters do not alias arguments[i], and
- * to make the arguments object reflect initial parameter values prior
- * to any mutation we create it eagerly whenever parameters are (or
- * might, in the case of calls to eval) be assigned.
- */
- if (tc->inStrictMode()) {
- AtomDeclsIter iter(&tc->decls);
- while (Definition *dn = iter.next()) {
- if (dn->kind() == Definition::ARG && dn->isAssigned()) {
- tc->noteDefinitelyNeedsArgsObj();
- break;
- }
- }
- }
- }
- tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
- return pn;
- }
- /* Create a placeholder Definition node for |atom|. */
- static Definition *
- MakePlaceholder(ParseNode *pn, TreeContext *tc)
- {
- Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, tc);
- if (!dn)
- return NULL;
- dn->setOp(JSOP_NOP);
- dn->setDefn(true);
- dn->pn_dflags |= PND_PLACEHOLDER;
- return dn;
- }
- static bool
- Define(ParseNode *pn, JSAtom *atom, TreeContext *tc, bool let = false)
- {
- JS_ASSERT(!pn->isUsed());
- JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
- bool foundLexdep = false;
- Definition *dn = NULL;
- if (let)
- dn = tc->decls.lookupFirst(atom);
- if (!dn) {
- dn = tc->lexdeps.lookupDefn(atom);
- foundLexdep = !!dn;
- }
- if (dn && dn != pn) {
- ParseNode **pnup = &dn->dn_uses;
- ParseNode *pnu;
- unsigned start = let ? pn->pn_blockid : tc->bodyid;
- while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
- JS_ASSERT(pnu->isUsed());
- pnu->pn_lexdef = (Definition *) pn;
- pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
- pnup = &pnu->pn_link;
- }
- if (pnu != dn->dn_uses) {
- *pnup = pn->dn_uses;
- pn->dn_uses = dn->dn_uses;
- dn->dn_uses = pnu;
- if ((!pnu || pnu->pn_blockid < tc->bodyid) && foundLexdep)
- tc->lexdeps->remove(atom);
- }
- pn->pn_dflags |= dn->pn_dflags & PND_CLOSED;
- }
- Definition *toAdd = (Definition *) pn;
- bool ok = let ? tc->decls.addShadow(atom, toAdd) : tc->decls.addUnique(atom, toAdd);
- if (!ok)
- return false;
- pn->setDefn(true);
- pn->pn_dflags &= ~PND_PLACEHOLDER;
- if (!tc->parent)
- pn->pn_dflags |= PND_TOPLEVEL;
- return true;
- }
- static void
- ForgetUse(ParseNode *pn)
- {
- if (!pn->isUsed()) {
- JS_ASSERT(!pn->isDefn());
- return;
- }
- ParseNode **pnup = &pn->lexdef()->dn_uses;
- ParseNode *pnu;
- while ((pnu = *pnup) != pn)
- pnup = &pnu->pn_link;
- *pnup = pn->pn_link;
- pn->setUsed(false);
- }
- static ParseNode *
- MakeAssignment(ParseNode *pn, ParseNode *rhs, TreeContext *tc)
- {
- ParseNode *lhs = tc->parser->cloneNode(*pn);
- if (!lhs)
- return NULL;
- if (pn->isUsed()) {
- Definition *dn = pn->pn_lexdef;
- ParseNode **pnup = &dn->dn_uses;
- while (*pnup != pn)
- pnup = &(*pnup)->pn_link;
- *pnup = lhs;
- lhs->pn_link = pn->pn_link;
- pn->pn_link = NULL;
- }
- pn->setKind(PNK_ASSIGN);
- pn->setOp(JSOP_NOP);
- pn->setArity(PN_BINARY);
- pn->setInParens(false);
- pn->setUsed(false);
- pn->setDefn(false);
- pn->pn_left = lhs;
- pn->pn_right = rhs;
- return lhs;
- }
- static ParseNode *
- MakeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom, TreeContext *tc)
- {
- /*
- * If dn is arg, or in [var, const, let] and has an initializer, then we
- * must rewrite it to be an assignment node, whose freshly allocated
- * left-hand side becomes a use of pn.
- */
- if (dn->isBindingForm()) {
- ParseNode *rhs = dn->expr();
- if (rhs) {
- ParseNode *lhs = MakeAssignment(dn, rhs, tc);
- if (!lhs)
- return NULL;
- //pn->dn_uses = lhs;
- dn = (Definition *) lhs;
- }
- dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME);
- } else if (dn->kind() == Definition::FUNCTION) {
- JS_ASSERT(dn->isOp(JSOP_NOP));
- tc->parser->prepareNodeForMutation(dn);
- dn->setKind(PNK_NAME);
- dn->setArity(PN_NAME);
- dn->pn_atom = atom;
- }
- /* Now make dn no longer a definition, rather a use of pn. */
- JS_ASSERT(dn->isKind(PNK_NAME));
- JS_ASSERT(dn->isArity(PN_NAME));
- JS_ASSERT(dn->pn_atom == atom);
- for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
- JS_ASSERT(pnu->isUsed());
- JS_ASSERT(!pnu->isDefn());
- pnu->pn_lexdef = (Definition *) pn;
- pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
- }
- pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
- pn->dn_uses = dn;
- dn->setDefn(false);
- dn->setUsed(true);
- dn->pn_lexdef = (Definition *) pn;
- dn->pn_cookie.makeFree();
- dn->pn_dflags &= ~PND_BOUND;
- return dn;
- }
- bool
- js::DefineArg(ParseNode *pn, JSAtom *atom, unsigned i, TreeContext *tc)
- {
- /*
- * Make an argument definition node, distinguished by being in tc->decls
- * but having PNK_NAME kind and JSOP_NOP op. Insert it in a PNK_ARGSBODY
- * list node returned via pn->pn_body.
- */
- ParseNode *argpn = NameNode::create(PNK_NAME, atom, tc);
- if (!argpn)
- return false;
- JS_ASSERT(argpn->isKind(PNK_NAME) && argpn->isOp(JSOP_NOP));
- /* Arguments are initialized by definition. */
- argpn->pn_dflags |= PND_INITIALIZED;
- if (!Define(argpn, atom, tc))
- return false;
- ParseNode *argsbody = pn->pn_body;
- if (!argsbody) {
- argsbody = ListNode::create(PNK_ARGSBODY, tc);
- if (!argsbody)
- return false;
- argsbody->setOp(JSOP_NOP);
- argsbody->makeEmpty();
- pn->pn_body = argsbody;
- }
- argsbody->append(argpn);
- argpn->setOp(JSOP_GETARG);
- argpn->pn_cookie.set(tc->staticLevel, i);
- argpn->pn_dflags |= PND_BOUND;
- return true;
- }
- /*
- * Parameter block types for the several Binder functions. We use a common
- * helper function signature in order to share code among destructuring and
- * simple variable declaration parsers. In the destructuring case, the binder
- * function is called indirectly from the variable declaration parser by way
- * of CheckDestructuring and its friends.
- */
- typedef JSBool
- (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc);
- static JSBool
- BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc);
- static JSBool
- BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc);
- struct BindData {
- BindData(JSContext *cx) : let(cx), fresh(true) {}
- ParseNode *pn; /* name node for definition processing and
- error source coordinates */
- JSOp op; /* prolog bytecode or nop */
- Binder binder; /* binder, discriminates u */
- struct LetData {
- LetData(JSContext *cx) : blockObj(cx) {}
- VarContext varContext;
- RootedVar<StaticBlockObject*> blockObj;
- unsigned overflow;
- } let;
- bool fresh;
- void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) {
- this->pn = NULL;
- this->op = JSOP_NOP;
- this->binder = BindLet;
- this->let.varContext = varContext;
- this->let.blockObj = &blockObj;
- this->let.overflow = overflow;
- }
- void initVarOrConst(JSOp op) {
- this->op = op;
- this->binder = BindVarOrConst;
- }
- };
- #if JS_HAS_DESTRUCTURING
- static JSBool
- BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc)
- {
- JS_ASSERT(tc->inFunction());
- /*
- * NB: Check tc->decls rather than tc->bindings, because destructuring
- * bindings aren't added to tc->bindings until after all arguments have
- * been parsed.
- */
- if (tc->decls.lookupFirst(atom)) {
- ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
- JSMSG_DESTRUCT_DUP_ARG);
- return JS_FALSE;
- }
- ParseNode *pn = data->pn;
- /*
- * Distinguish destructured-to binding nodes as vars, not args, by setting
- * pn_op to JSOP_SETLOCAL. Parser::functionDef checks for this pn_op value
- * when processing the destructuring-assignment AST prelude induced by such
- * destructuring args in Parser::functionArguments.
- *
- * We must set the PND_BOUND flag too to prevent pn_op from being reset to
- * JSOP_SETNAME by BindDestructuringVar. The only field not initialized is
- * pn_cookie; it gets set in functionDef in the first "if (prelude)" block.
- * We have to wait to set the cookie until we can call JSFunction::addLocal
- * with kind = JSLOCAL_VAR, after all JSLOCAL_ARG locals have been added.
- *
- * Thus a destructuring formal parameter binds an ARG (as in arguments[i]
- * element) with a null atom name for the object or array passed in to be
- * destructured, and zero or more VARs (as in named local variables) for
- * the destructured-to identifiers in the property value positions within
- * the object or array destructuring pattern, and all ARGs for the formal
- * parameter list bound as locals before any VAR for a destructured name.
- */
- pn->setOp(JSOP_SETLOCAL);
- pn->pn_dflags |= PND_BOUND;
- return Define(pn, atom, tc);
- }
- #endif /* JS_HAS_DESTRUCTURING */
- JSFunction *
- Parser::newFunction(TreeContext *tc, JSAtom *atom, FunctionSyntaxKind kind)
- {
- JS_ASSERT_IF(kind == Statement, atom != NULL);
- /*
- * Find the global compilation context in order to pre-set the newborn
- * function's parent slot to tc->scopeChain. If the global context is a
- * compile-and-go one, we leave the pre-set parent intact; otherwise we
- * clear parent and proto.
- */
- while (tc->parent)
- tc = tc->parent;
- RootedVarObject parent(context);
- parent = tc->inFunction() ? NULL : tc->scopeChain();
- RootedVarFunction fun(context);
- fun = js_NewFunction(context, NULL, NULL, 0,
- JSFUN_INTERPRETED | (kind == Expression ? JSFUN_LAMBDA : 0),
- parent, atom);
- if (fun && !tc->compileAndGo()) {
- if (!fun->clearParent(context))
- return NULL;
- if (!fun->clearType(context))
- return NULL;
- fun->setEnvironment(NULL);
- }
- return fun;
- }
- static JSBool
- MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
- {
- TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
- if (tt == TOK_ERROR)
- return JS_FALSE;
- if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
- /* Advance the scanner for proper error location reporting. */
- ts->getToken(TSF_OPERAND);
- ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT);
- return JS_FALSE;
- }
- (void) ts->matchToken(TOK_SEMI);
- return JS_TRUE;
- }
- static FunctionBox *
- EnterFunction(ParseNode *fn, TreeContext *funtc, JSAtom *funAtom = NULL,
- FunctionSyntaxKind kind = Expression)
- {
- TreeContext *tc = funtc->parent;
- JSFunction *fun = tc->parser->newFunction(tc, funAtom, kind);
- if (!fun)
- return NULL;
- /* Create box for fun->object early to protect against last-ditch GC. */
- FunctionBox *funbox = tc->parser->newFunctionBox(fun, fn, tc);
- if (!funbox)
- return NULL;
- /* Initialize non-default members of funtc. */
- funtc->flags |= funbox->tcflags;
- funtc->blockidGen = tc->blockidGen;
- if (!GenerateBlockId(funtc, funtc->bodyid))
- return NULL;
- funtc->setFunction(fun);
- funtc->funbox = funbox;
- if (!SetStaticLevel(funtc, tc->staticLevel + 1))
- return NULL;
- return funbox;
- }
- static bool
- DeoptimizeUsesWithin(Definition *dn, const TokenPos &pos)
- {
- unsigned ndeoptimized = 0;
- for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
- JS_ASSERT(pnu->isUsed());
- JS_ASSERT(!pnu->isDefn());
- if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) {
- pnu->pn_dflags |= PND_DEOPTIMIZED;
- ++ndeoptimized;
- }
- }
- return ndeoptimized != 0;
- }
- /*
- * Beware: this function is called for functions nested in other functions or
- * global scripts but not for functions compiled through the Function
- * constructor or JSAPI. To always execute code when a function has finished
- * parsing, use Parser::functionBody.
- */
- static bool
- LeaveFunction(ParseNode *fn, TreeContext *funtc, PropertyName *funName = NULL,
- FunctionSyntaxKind kind = Expression)
- {
- TreeContext *tc = funtc->parent;
- tc->blockidGen = funtc->blockidGen;
- FunctionBox *funbox = fn->pn_funbox;
- funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
- fn->pn_dflags |= PND_INITIALIZED;
- if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
- fn->pn_dflags |= PND_BLOCKCHILD;
- /*
- * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
- * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
- * params and body. We do this only if there are lexical dependencies not
- * satisfied by the function's declarations, to avoid penalizing functions
- * that use only their arguments and other local bindings.
- */
- if (funtc->lexdeps->count()) {
- int foundCallee = 0;
- for (AtomDefnRange r = funtc->lexdeps->all(); !r.empty(); r.popFront()) {
- JSAtom *atom = r.front().key();
- Definition *dn = r.front().value();
- JS_ASSERT(dn->isPlaceholder());
- if (atom == funName && kind == Expression) {
- dn->setOp(JSOP_CALLEE);
- dn->pn_cookie.set(funtc->staticLevel, UpvarCookie::CALLEE_SLOT);
- dn->pn_dflags |= PND_BOUND;
- foundCallee = 1;
- continue;
- }
- Definition *outer_dn = tc->decls.lookupFirst(atom);
- /*
- * Make sure to deoptimize lexical dependencies that are polluted
- * by eval (approximated by bindingsAccessedDynamically) or with, to
- * safely bind globals (see bug 561923).
- */
- if (funtc->bindingsAccessedDynamically() ||
- (outer_dn && tc->innermostWith &&
- outer_dn->pn_pos < tc->innermostWith->pn_pos)) {
- DeoptimizeUsesWithin(dn, fn->pn_pos);
- }
- if (!outer_dn) {
- AtomDefnAddPtr p = tc->lexdeps->lookupForAdd(atom);
- if (p) {
- outer_dn = p.value();
- } else {
- /*
- * Create a new placeholder for our outer lexdep. We could
- * simply re-use the inner placeholder, but that introduces
- * subtleties in the case where we find a later definition
- * that captures an existing lexdep. For example:
- *
- * function f() { function g() { x; } let x; }
- *
- * Here, g's TOK_UPVARS node lists the placeholder for x,
- * which must be captured by the 'let' declaration later,
- * since 'let's are hoisted. Taking g's placeholder as our
- * own would work fine. But consider:
- *
- * function f() { x; { function g() { x; } let x; } }
- *
- * Here, the 'let' must not capture all the uses of f's
- * lexdep entry for x, but it must capture the x node
- * referred to from g's TOK_UPVARS node. Always turning
- * inherited lexdeps into uses of a new outer definition
- * allows us to handle both these cases in a natural way.
- */
- outer_dn = MakePlaceholder(dn, tc);
- if (!outer_dn || !tc->lexdeps->add(p, atom, outer_dn))
- return false;
- }
- }
- /*
- * Insert dn's uses list at the front of outer_dn's list.
- *
- * Without loss of generality or correctness, we allow a dn to
- * be in inner and outer lexdeps, since the purpose of lexdeps
- * is one-pass coordination of name use and definition across
- * functions, and if different dn's are used we'll merge lists
- * when leaving the inner function.
- *
- * The dn == outer_dn case arises with generator expressions
- * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
- * case), and nowhere else, currently.
- */
- if (dn != outer_dn) {
- ParseNode **pnup = &dn->dn_uses;
- ParseNode *pnu;
- while ((pnu = *pnup) != NULL) {
- pnu->pn_lexdef = outer_dn;
- pnup = &pnu->pn_link;
- }
- /*
- * Make dn be a use that redirects to outer_dn, because we
- * can't replace dn with outer_dn in all the pn_namesets in
- * the AST where it may be. Instead we make it forward to
- * outer_dn. See Definition::resolve.
- */
- *pnup = outer_dn->dn_uses;
- outer_dn->dn_uses = dn;
- outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
- dn->setDefn(false);
- dn->setUsed(true);
- dn->pn_lexdef = outer_dn;
- }
- /* Mark the outer dn as escaping. */
- outer_dn->pn_dflags |= PND_CLOSED;
- }
- if (funtc->lexdeps->count() - foundCallee != 0) {
- ParseNode *body = fn->pn_body;
- fn->pn_body = NameSetNode::create(PNK_UPVARS, tc);
- if (!fn->pn_body)
- return false;
- fn->pn_body->pn_pos = body->pn_pos;
- if (foundCallee)
- funtc->lexdeps->remove(funName);
- /* Transfer ownership of the lexdep map to the parse node. */
- fn->pn_body->pn_names = funtc->lexdeps;
- funtc->lexdeps.clearMap();
- fn->pn_body->pn_tree = body;
- } else {
- funtc->lexdeps.releaseMap(funtc->parser->context);
- }
- }
- funbox->bindings.transfer(funtc->parser->context, &funtc->bindings);
- return true;
- }
- static bool
- DefineGlobal(ParseNode *pn, BytecodeEmitter *bce, Handle<PropertyName*> name);
- /*
- * FIXME? this Parser method was factored from Parser::functionDef with minimal
- * change, hence the funtc ref param and funbox. It probably should match
- * functionBody, etc., and use tc and tc->funbox instead of taking explicit
- * parameters.
- */
- bool
- Parser::functionArguments(TreeContext &funtc, FunctionBox *funbox, ParseNode **listp)
- {
- if (tokenStream.getToken() != TOK_LP) {
- reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
- return false;
- }
- if (!tokenStream.matchToken(TOK_RP)) {
- #if JS_HAS_DESTRUCTURING
- JSAtom *duplicatedArg = NULL;
- bool destructuringArg = false;
- ParseNode *list = NULL;
- #endif
- do {
- switch (TokenKind tt = tokenStream.getToken()) {
- #if JS_HAS_DESTRUCTURING
- case TOK_LB:
- case TOK_LC:
- {
- /* See comment below in the TOK_NAME case. */
- if (duplicatedArg)
- goto report_dup_and_destructuring;
- destructuringArg = true;
- /*
- * A destructuring formal parameter turns into one or more
- * local variables initialized from properties of a single
- * anonymous positional parameter, so here we must tweak our
- * binder and its data.
- */
- BindData data(context);
- data.pn = NULL;
- data.op = JSOP_DEFVAR;
- data.binder = BindDestructuringArg;
- ParseNode *lhs = destructuringExpr(&data, tt);
- if (!lhs)
- return false;
- /*
- * Adjust fun->nargs to count the single anonymous positional
- * parameter that is to be destructured.
- */
- uint16_t slot;
- if (!funtc.bindings.addDestructuring(context, &slot))
- return false;
- /*
- * Synthesize a destructuring assignment from the single
- * anonymous positional parameter into the destructuring
- * left-hand-side expression and accumulate it in list.
- */
- ParseNode *rhs =
- NameNode::create(PNK_NAME, context->runtime->atomState.emptyAtom, &funtc);
- if (!rhs)
- return false;
- rhs->setOp(JSOP_GETARG);
- rhs->pn_cookie.set(funtc.staticLevel, slot);
- rhs->pn_dflags |= PND_BOUND;
- rhs->setDefn(true);
- ParseNode *item = new_<BinaryNode>(PNK_ASSIGN, JSOP_NOP, lhs->pn_pos, lhs, rhs);
- if (!item)
- return false;
- if (!list) {
- list = ListNode::create(PNK_VAR, &funtc);
- if (!list)
- return false;
- list->makeEmpty();
- *listp = list;
- }
- list->append(item);
- break;
- }
- #endif /* JS_HAS_DESTRUCTURING */
- case TOK_NAME:
- {
- RootedVar<PropertyName*> name(context, tokenStream.currentToken().name());
- #ifdef JS_HAS_DESTRUCTURING
- /*
- * ECMA-262 requires us to support duplicate parameter names,
- * but if the parameter list includes destructuring, we
- * consider the code to have "opted in" to higher standards and
- * forbid duplicates. We may see a destructuring parameter
- * later, so always note duplicates now.
- *
- * Duplicates are warned about (strict option) or cause errors
- * (strict mode code), but we do those tests in one place
- * below, after having parsed the body in case it begins with a
- * "use strict"; directive.
- *
- * NB: Check funtc.decls rather than funtc.bindings, because
- * destructuring bindings aren't added to funtc.bindings
- * until after all arguments have been parsed.
- */
- if (funtc.decls.lookupFirst(name)) {
- funtc.bindings.noteDup();
- duplicatedArg = name;
- if (destructuringArg)
- goto report_dup_and_destructuring;
- }
- #endif
- uint16_t slot;
- if (!funtc.bindings.addArgument(context, name, &slot))
- return false;
- if (!DefineArg(funbox->node, name, slot, &funtc))
- return false;
- break;
- }
- default:
- reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL);
- /* FALL THROUGH */
- case TOK_ERROR:
- return false;
- #if JS_HAS_DESTRUCTURING
- report_dup_and_destructuring:
- Definition *dn = funtc.decls.lookupFirst(duplicatedArg);
- reportErrorNumber(dn, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
- return false;
- #endif
- }
- } while (tokenStream.matchToken(TOK_COMMA));
- if (tokenStream.getToken() != TOK_RP) {
- reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_FORMAL);
- return false;
- }
- }
- return true;
- }
- ParseNode *
- Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSyntaxKind kind)
- {
- JS_ASSERT_IF(kind == Statement, funName);
- /* Make a TOK_FUNCTION node. */
- ParseNode *pn = FunctionNode::create(PNK_FUNCTION, tc);
- if (!pn)
- return NULL;
- pn->pn_body = NULL;
- pn->pn_cookie.makeFree();
- pn->pn_dflags = 0;
- /*
- * Record names for function statements in tc->decls so we know when to
- * avoid optimizing variable references that might name a function.
- */
- bool bodyLevel = tc->atBodyLevel();
- if (kind == Statement) {
- if (Definition *dn = tc->decls.lookupFirst(funName)) {
- Definition::Kind dn_kind = dn->kind();
- JS_ASSERT(!dn->isUsed());
- JS_ASSERT(dn->isDefn());
- if (context->hasStrictOption() || dn_kind == Definition::CONST) {
- JSAutoByteString name;
- if (!js_AtomToPrintableString(context, funName, &name) ||
- !reportErrorNumber(NULL,
- (dn_kind != Definition::CONST)
- ? JSREPORT_WARNING | JSREPORT_STRICT
- : JSREPORT_ERROR,
- JSMSG_REDECLARED_VAR,
- Definition::kindString(dn_kind),
- name.ptr())) {
- return NULL;
- }
- }
- if (bodyLevel) {
- tc->decls.updateFirst(funName, (Definition *) pn);
- pn->setDefn(true);
- pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
- if (!MakeDefIntoUse(dn, pn, funName, tc))
- return NULL;
- }
- } else if (bodyLevel) {
- /*
- * If this function was used before it was defined, claim the
- * pre-created definition node for this function that primaryExpr
- * put in tc->lexdeps on first forward reference, and recycle pn.
- */
- if (Definition *fn = tc->lexdeps.lookupDefn(funName)) {
- …
Large files files are truncated, but you can click here to view the full file