1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
44 * This is a recursive-descent parser for the JavaScript language specified by
45 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
46 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
47 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
48 * After tree construction, it rewrites trees to fold constants and evaluate
49 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
52 * This parser attempts no error recovery.
59 #include "jsarena.h" /* Added by JSIFY */
60 #include "jsutil.h" /* Added by JSIFY */
65 #include "jsversion.h"
79 #include "jsstaticcheck.h"
80 #include "jslibmath.h"
83 #if JS_HAS_XML_SUPPORT
87 #if JS_HAS_DESTRUCTURING
92 * Asserts to verify assumptions behind pn_ macros.
94 #define pn_offsetof(m) offsetof(JSParseNode, m)
96 JS_STATIC_ASSERT(pn_offsetof(pn_link
) == pn_offsetof(dn_uses
));
97 JS_STATIC_ASSERT(pn_offsetof(pn_u
.name
.atom
) == pn_offsetof(pn_u
.apair
.atom
));
102 * JS parsers, from lowest to highest precedence.
104 * Each parser takes a context, a token stream, and a tree context struct.
105 * Each returns a parse node tree or null on error.
108 typedef JSParseNode
*
109 JSParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
);
111 typedef JSParseNode
*
112 JSVariablesParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
115 typedef JSParseNode
*
116 JSMemberParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
117 JSBool allowCallSyntax
);
119 typedef JSParseNode
*
120 JSPrimaryParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
121 JSTokenType tt
, JSBool afterDot
);
123 typedef JSParseNode
*
124 JSParenParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
125 JSParseNode
*pn1
, JSBool
*genexp
);
127 static JSParser FunctionStmt
;
128 static JSParser FunctionExpr
;
129 static JSParser Statements
;
130 static JSParser Statement
;
131 static JSVariablesParser Variables
;
132 static JSParser Expr
;
133 static JSParser AssignExpr
;
134 static JSParser CondExpr
;
135 static JSParser OrExpr
;
136 static JSParser AndExpr
;
137 static JSParser BitOrExpr
;
138 static JSParser BitXorExpr
;
139 static JSParser BitAndExpr
;
140 static JSParser EqExpr
;
141 static JSParser RelExpr
;
142 static JSParser ShiftExpr
;
143 static JSParser AddExpr
;
144 static JSParser MulExpr
;
145 static JSParser UnaryExpr
;
146 static JSMemberParser MemberExpr
;
147 static JSPrimaryParser PrimaryExpr
;
148 static JSParenParser ParenExpr
;
150 static bool RecognizeDirectivePrologue(JSContext
*cx
, JSTokenStream
*ts
,
151 JSTreeContext
*tc
, JSParseNode
*pn
);
154 * Insist that the next token be of type tt, or report errno and return null.
155 * NB: this macro uses cx and ts from its lexical environment.
157 #define MUST_MATCH_TOKEN(tt, errno) \
159 if (js_GetToken(cx, ts) != tt) { \
160 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
165 #ifdef METER_PARSENODES
166 static uint32 parsenodes
= 0;
167 static uint32 maxparsenodes
= 0;
168 static uint32 recyclednodes
= 0;
172 JSParseNode::become(JSParseNode
*pn2
)
175 JS_ASSERT(!pn2
->pn_defn
);
179 JSParseNode
**pnup
= &pn2
->pn_lexdef
->dn_uses
;
181 pnup
= &(*pnup
)->pn_link
;
183 pn_link
= pn2
->pn_link
;
186 pn2
->pn_used
= false;
189 /* If this is a function node fix up the pn_funbox->node back-pointer. */
190 if (PN_TYPE(pn2
) == TOK_FUNCTION
&& pn2
->pn_arity
== PN_FUNC
)
191 pn2
->pn_funbox
->node
= this;
193 pn_type
= pn2
->pn_type
;
195 pn_arity
= pn2
->pn_arity
;
196 pn_parens
= pn2
->pn_parens
;
206 pn_used
= pn_defn
= false;
207 pn_arity
= PN_NULLARY
;
212 JSCompiler::init(const jschar
*base
, size_t length
,
213 FILE *fp
, const char *filename
, uintN lineno
)
215 JSContext
*cx
= context
;
217 tempPoolMark
= JS_ARENA_MARK(&cx
->tempPool
);
218 if (!tokenStream
.init(cx
, base
, length
, fp
, filename
, lineno
)) {
219 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
223 /* Root atoms and objects allocated for the parsed tree. */
224 JS_KEEP_ATOMS(cx
->runtime
);
225 JS_PUSH_TEMP_ROOT_COMPILER(cx
, this, &tempRoot
);
229 JSCompiler::~JSCompiler()
231 JSContext
*cx
= context
;
234 JSPRINCIPALS_DROP(cx
, principals
);
235 JS_ASSERT(tempRoot
.u
.compiler
== this);
236 JS_POP_TEMP_ROOT(cx
, &tempRoot
);
237 JS_UNKEEP_ATOMS(cx
->runtime
);
238 tokenStream
.close(cx
);
239 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
243 JSCompiler::setPrincipals(JSPrincipals
*prin
)
245 JS_ASSERT(!principals
);
247 JSPRINCIPALS_HOLD(context
, prin
);
252 JSCompiler::newObjectBox(JSObject
*obj
)
257 * We use JSContext.tempPool to allocate parsed objects and place them on
258 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
259 * containing the entries must be alive until we are done with scanning,
260 * parsing and code generation for the whole script or top-level function.
263 JS_ARENA_ALLOCATE_TYPE(objbox
, JSObjectBox
, &context
->tempPool
);
265 js_ReportOutOfScriptQuota(context
);
268 objbox
->traceLink
= traceListHead
;
269 traceListHead
= objbox
;
270 objbox
->emitLink
= NULL
;
271 objbox
->object
= obj
;
276 JSCompiler::newFunctionBox(JSObject
*obj
, JSParseNode
*fn
, JSTreeContext
*tc
)
279 JS_ASSERT(HAS_FUNCTION_CLASS(obj
));
282 * We use JSContext.tempPool to allocate parsed objects and place them on
283 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
284 * containing the entries must be alive until we are done with scanning,
285 * parsing and code generation for the whole script or top-level function.
287 JSFunctionBox
*funbox
;
288 JS_ARENA_ALLOCATE_TYPE(funbox
, JSFunctionBox
, &context
->tempPool
);
290 js_ReportOutOfScriptQuota(context
);
293 funbox
->traceLink
= traceListHead
;
294 traceListHead
= funbox
;
295 funbox
->emitLink
= NULL
;
296 funbox
->object
= obj
;
298 funbox
->siblings
= tc
->functionList
;
299 tc
->functionList
= funbox
;
300 ++tc
->compiler
->functionCount
;
302 funbox
->parent
= tc
->funbox
;
303 funbox
->queued
= false;
304 funbox
->inLoop
= false;
305 for (JSStmtInfo
*stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
306 if (STMT_IS_LOOP(stmt
)) {
307 funbox
->inLoop
= true;
311 funbox
->level
= tc
->staticLevel
;
312 funbox
->tcflags
= (TCF_IN_FUNCTION
| (tc
->flags
& (TCF_COMPILE_N_GO
| TCF_STRICT_MODE_CODE
)));
317 JSCompiler::trace(JSTracer
*trc
)
321 JS_ASSERT(tempRoot
.u
.compiler
== this);
322 objbox
= traceListHead
;
324 JS_CALL_OBJECT_TRACER(trc
, objbox
->object
, "parser.object");
325 objbox
= objbox
->traceLink
;
330 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
);
333 UnlinkFunctionBox(JSParseNode
*pn
, JSTreeContext
*tc
)
335 JSFunctionBox
*funbox
= pn
->pn_funbox
;
337 JS_ASSERT(funbox
->node
== pn
);
340 JSFunctionBox
**funboxp
= &tc
->functionList
;
342 if (*funboxp
== funbox
) {
343 *funboxp
= funbox
->siblings
;
346 funboxp
= &(*funboxp
)->siblings
;
349 uint32 oldflags
= tc
->flags
;
350 JSFunctionBox
*oldlist
= tc
->functionList
;
352 tc
->flags
= funbox
->tcflags
;
353 tc
->functionList
= funbox
->kids
;
354 UnlinkFunctionBoxes(pn
->pn_body
, tc
);
355 funbox
->kids
= tc
->functionList
;
356 tc
->flags
= oldflags
;
357 tc
->functionList
= oldlist
;
359 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
360 pn
->pn_funbox
= NULL
;
365 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
)
368 switch (pn
->pn_arity
) {
372 UnlinkFunctionBoxes(pn
->pn_kid
, tc
);
375 UnlinkFunctionBoxes(pn
->pn_left
, tc
);
376 UnlinkFunctionBoxes(pn
->pn_right
, tc
);
379 UnlinkFunctionBoxes(pn
->pn_kid1
, tc
);
380 UnlinkFunctionBoxes(pn
->pn_kid2
, tc
);
381 UnlinkFunctionBoxes(pn
->pn_kid3
, tc
);
384 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
385 UnlinkFunctionBoxes(pn2
, tc
);
388 UnlinkFunctionBox(pn
, tc
);
391 UnlinkFunctionBoxes(pn
->maybeExpr(), tc
);
394 UnlinkFunctionBoxes(pn
->pn_tree
, tc
);
400 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
);
403 RecycleTree(JSParseNode
*pn
, JSTreeContext
*tc
)
405 JSParseNode
*next
, **head
;
410 /* Catch back-to-back dup recycles. */
411 JS_ASSERT(pn
!= tc
->compiler
->nodeList
);
413 if (pn
->pn_used
|| pn
->pn_defn
) {
415 * JSAtomLists own definition nodes along with their used-node chains.
416 * Defer recycling such nodes until we unwind to top level to avoid
417 * linkage overhead or (alternatively) unlinking runtime complexity.
418 * Yes, this means dead code can contribute to static analysis results!
420 * Do recycle kids here, since they are no longer needed.
423 RecycleFuncNameKids(pn
, tc
);
425 UnlinkFunctionBoxes(pn
, tc
);
426 head
= &tc
->compiler
->nodeList
;
429 #ifdef METER_PARSENODES
437 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
)
439 switch (pn
->pn_arity
) {
441 UnlinkFunctionBox(pn
, tc
);
446 * Only a definition node might have a non-null strong pn_expr link
447 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
448 * Every node with the pn_used flag set has a non-null pn_lexdef
449 * weak reference to its definition node.
451 if (!pn
->pn_used
&& pn
->pn_expr
) {
452 RecycleTree(pn
->pn_expr
, tc
);
458 JS_ASSERT(PN_TYPE(pn
) == TOK_FUNCTION
);
463 NewOrRecycledNode(JSTreeContext
*tc
)
465 JSParseNode
*pn
, *pn2
;
467 pn
= tc
->compiler
->nodeList
;
469 JSContext
*cx
= tc
->compiler
->context
;
471 JS_ARENA_ALLOCATE_TYPE(pn
, JSParseNode
, &cx
->tempPool
);
473 js_ReportOutOfScriptQuota(cx
);
475 tc
->compiler
->nodeList
= pn
->pn_next
;
477 /* Recycle immediate descendents only, to save work and working set. */
478 switch (pn
->pn_arity
) {
480 RecycleTree(pn
->pn_body
, tc
);
485 while (pn2
&& !pn2
->pn_used
&& !pn2
->pn_defn
)
490 pn2
= RecycleTree(pn2
, tc
);
493 *pn
->pn_tail
= tc
->compiler
->nodeList
;
494 tc
->compiler
->nodeList
= pn
->pn_head
;
495 #ifdef METER_PARSENODES
496 recyclednodes
+= pn
->pn_count
;
503 RecycleTree(pn
->pn_kid1
, tc
);
504 RecycleTree(pn
->pn_kid2
, tc
);
505 RecycleTree(pn
->pn_kid3
, tc
);
508 if (pn
->pn_left
!= pn
->pn_right
)
509 RecycleTree(pn
->pn_left
, tc
);
510 RecycleTree(pn
->pn_right
, tc
);
513 RecycleTree(pn
->pn_kid
, tc
);
517 RecycleTree(pn
->pn_expr
, tc
);
524 #ifdef METER_PARSENODES
526 if (parsenodes
- recyclednodes
> maxparsenodes
)
527 maxparsenodes
= parsenodes
- recyclednodes
;
529 pn
->pn_used
= pn
->pn_defn
= false;
530 memset(&pn
->pn_u
, 0, sizeof pn
->pn_u
);
537 InitParseNode(JSParseNode
*pn
, JSTokenType type
, JSOp op
, JSParseNodeArity arity
)
541 pn
->pn_arity
= arity
;
542 pn
->pn_parens
= false;
543 JS_ASSERT(!pn
->pn_used
);
544 JS_ASSERT(!pn
->pn_defn
);
545 pn
->pn_next
= pn
->pn_link
= NULL
;
549 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
553 NewParseNode(JSParseNodeArity arity
, JSTreeContext
*tc
)
558 pn
= NewOrRecycledNode(tc
);
561 tp
= &CURRENT_TOKEN(&tc
->compiler
->tokenStream
);
562 InitParseNode(pn
, tp
->type
, JSOP_NOP
, arity
);
563 pn
->pn_pos
= tp
->pos
;
568 InitNameNodeCommon(JSParseNode
*pn
, JSTreeContext
*tc
)
571 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
572 pn
->pn_dflags
= tc
->atTopLevel() ? PND_TOPLEVEL
: 0;
573 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
574 pn
->pn_dflags
|= PND_BLOCKCHILD
;
575 pn
->pn_blockid
= tc
->blockid();
579 NewNameNode(JSContext
*cx
, JSAtom
*atom
, JSTreeContext
*tc
)
583 pn
= NewParseNode(PN_NAME
, tc
);
586 InitNameNodeCommon(pn
, tc
);
592 NewBinary(JSTokenType tt
, JSOp op
, JSParseNode
*left
, JSParseNode
*right
,
595 JSParseNode
*pn
, *pn1
, *pn2
;
601 * Flatten a left-associative (left-heavy) tree of a given operator into
602 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
604 if (PN_TYPE(left
) == tt
&&
606 (js_CodeSpec
[op
].format
& JOF_LEFTASSOC
)) {
607 if (left
->pn_arity
!= PN_LIST
) {
608 pn1
= left
->pn_left
, pn2
= left
->pn_right
;
609 left
->pn_arity
= PN_LIST
;
610 left
->pn_parens
= false;
613 if (tt
== TOK_PLUS
) {
614 if (pn1
->pn_type
== TOK_STRING
)
615 left
->pn_xflags
|= PNX_STRCAT
;
616 else if (pn1
->pn_type
!= TOK_NUMBER
)
617 left
->pn_xflags
|= PNX_CANTFOLD
;
618 if (pn2
->pn_type
== TOK_STRING
)
619 left
->pn_xflags
|= PNX_STRCAT
;
620 else if (pn2
->pn_type
!= TOK_NUMBER
)
621 left
->pn_xflags
|= PNX_CANTFOLD
;
625 left
->pn_pos
.end
= right
->pn_pos
.end
;
626 if (tt
== TOK_PLUS
) {
627 if (right
->pn_type
== TOK_STRING
)
628 left
->pn_xflags
|= PNX_STRCAT
;
629 else if (right
->pn_type
!= TOK_NUMBER
)
630 left
->pn_xflags
|= PNX_CANTFOLD
;
636 * Fold constant addition immediately, to conserve node space and, what's
637 * more, so js_FoldConstants never sees mixed addition and concatenation
638 * operations with more than one leading non-string operand in a PN_LIST
639 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
640 * to "3pt", not "12pt").
642 if (tt
== TOK_PLUS
&&
643 left
->pn_type
== TOK_NUMBER
&&
644 right
->pn_type
== TOK_NUMBER
) {
645 left
->pn_dval
+= right
->pn_dval
;
646 left
->pn_pos
.end
= right
->pn_pos
.end
;
647 RecycleTree(right
, tc
);
651 pn
= NewOrRecycledNode(tc
);
654 InitParseNode(pn
, tt
, op
, PN_BINARY
);
655 pn
->pn_pos
.begin
= left
->pn_pos
.begin
;
656 pn
->pn_pos
.end
= right
->pn_pos
.end
;
658 pn
->pn_right
= right
;
662 #if JS_HAS_GETTER_SETTER
664 CheckGetterOrSetter(JSContext
*cx
, JSTokenStream
*ts
, JSTokenType tt
)
671 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_NAME
);
672 atom
= CURRENT_TOKEN(ts
).t_atom
;
674 if (atom
== rt
->atomState
.getterAtom
)
676 else if (atom
== rt
->atomState
.setterAtom
)
680 if (js_PeekTokenSameLine(cx
, ts
) != tt
)
682 (void) js_GetToken(cx
, ts
);
683 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
684 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
685 JSMSG_BAD_GETTER_OR_SETTER
,
691 CURRENT_TOKEN(ts
).t_op
= op
;
692 if (JS_HAS_STRICT_OPTION(cx
)) {
693 name
= js_AtomToPrintableString(cx
, atom
);
695 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
696 JSREPORT_WARNING
| JSREPORT_STRICT
,
697 JSMSG_DEPRECATED_USAGE
,
707 GenerateBlockId(JSTreeContext
*tc
, uint32
& blockid
)
709 if (tc
->blockidGen
== JS_BIT(20)) {
710 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
711 JSMSG_NEED_DIET
, "program");
714 blockid
= tc
->blockidGen
++;
719 GenerateBlockIdForStmtNode(JSParseNode
*pn
, JSTreeContext
*tc
)
721 JS_ASSERT(tc
->topStmt
);
722 JS_ASSERT(STMT_MAYBE_SCOPE(tc
->topStmt
));
723 JS_ASSERT(pn
->pn_type
== TOK_LC
|| pn
->pn_type
== TOK_LEXICALSCOPE
);
724 if (!GenerateBlockId(tc
, tc
->topStmt
->blockid
))
726 pn
->pn_blockid
= tc
->topStmt
->blockid
;
731 * Parse a top-level JS script.
734 JSCompiler::parse(JSObject
*chain
)
737 * Protect atoms from being collected by a GC activation, which might
738 * - nest on this thread due to out of memory (the so-called "last ditch"
739 * GC attempted within js_NewGCThing), or
740 * - run for any reason on another thread if this thread is suspended on
741 * an object lock before it finishes generating bytecode into a script
742 * protected from the GC by a root or a stack frame reference.
744 JSTreeContext
tc(this);
745 tc
.scopeChain
= chain
;
746 if (!GenerateBlockId(&tc
, tc
.bodyid
))
749 JSParseNode
*pn
= Statements(context
, TS(this), &tc
);
751 if (!js_MatchToken(context
, TS(this), TOK_EOF
)) {
752 js_ReportCompileErrorNumber(context
, TS(this), NULL
, JSREPORT_ERROR
,
756 if (!js_FoldConstants(context
, pn
, &tc
))
763 JS_STATIC_ASSERT(FREE_STATIC_LEVEL
== JS_BITMASK(JSFB_LEVEL_BITS
));
766 SetStaticLevel(JSTreeContext
*tc
, uintN staticLevel
)
769 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
770 * (0xffffffff) and other cookies with that level.
772 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
773 * practically speaking it leaves more than enough room for upvars. In fact
774 * we might want to split cookie fields giving fewer bits for skip and more
775 * for slot, but only based on evidence.
777 if (staticLevel
>= FREE_STATIC_LEVEL
) {
778 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
779 JSMSG_TOO_DEEP
, js_function_str
);
782 tc
->staticLevel
= staticLevel
;
787 * Compile a top-level script.
790 JSCompiler::compileScript(JSContext
*cx
, JSObject
*scopeChain
, JSStackFrame
*callerFrame
,
791 JSPrincipals
*principals
, uint32 tcflags
,
792 const jschar
*chars
, size_t length
,
793 FILE *file
, const char *filename
, uintN lineno
,
794 JSString
*source
/* = NULL */,
795 unsigned staticLevel
/* = 0 */)
797 JSCompiler
jsc(cx
, principals
, callerFrame
);
798 JSArenaPool codePool
, notePool
;
801 uint32 scriptGlobals
;
803 bool inDirectivePrologue
;
804 #ifdef METER_PARSENODES
805 void *sbrk(ptrdiff_t), *before
= sbrk(0);
808 JS_ASSERT(!(tcflags
& ~(TCF_COMPILE_N_GO
| TCF_NO_SCRIPT_RVAL
| TCF_NEED_MUTABLE_SCRIPT
)));
811 * The scripted callerFrame can only be given for compile-and-go scripts
812 * and non-zero static level requires callerFrame.
814 JS_ASSERT_IF(callerFrame
, tcflags
& TCF_COMPILE_N_GO
);
815 JS_ASSERT_IF(staticLevel
!= 0, callerFrame
);
817 if (!jsc
.init(chars
, length
, file
, filename
, lineno
))
820 JS_InitArenaPool(&codePool
, "code", 1024, sizeof(jsbytecode
),
821 &cx
->scriptStackQuota
);
822 JS_InitArenaPool(¬ePool
, "note", 1024, sizeof(jssrcnote
),
823 &cx
->scriptStackQuota
);
825 JSCodeGenerator
cg(&jsc
, &codePool
, ¬ePool
, jsc
.tokenStream
.lineno
);
827 MUST_FLOW_THROUGH("out");
829 /* Null script early in case of error, to reduce our code footprint. */
833 cg
.scopeChain
= scopeChain
;
834 if (!SetStaticLevel(&cg
, staticLevel
))
837 /* If this is a direct call to eval, inherit the caller's strictness. */
839 callerFrame
->script
&&
840 callerFrame
->script
->strictModeCode
) {
841 cg
.flags
|= TCF_STRICT_MODE_CODE
;
842 jsc
.tokenStream
.flags
|= TSF_STRICT_MODE_CODE
;
846 * If funbox is non-null after we create the new script, callerFrame->fun
847 * was saved in the 0th object table entry.
852 if (tcflags
& TCF_COMPILE_N_GO
) {
855 * Save eval program source in script->atomMap.vector[0] for the
856 * eval cache (see obj_eval in jsobj.cpp).
858 JSAtom
*atom
= js_AtomizeString(cx
, source
, 0);
859 if (!atom
|| !cg
.atomList
.add(&jsc
, atom
))
863 if (callerFrame
&& callerFrame
->fun
) {
865 * An eval script in a caller frame needs to have its enclosing
866 * function captured in case it refers to an upvar, and someone
867 * wishes to decompile it while it's running.
869 funbox
= jsc
.newObjectBox(FUN_OBJECT(callerFrame
->fun
));
872 funbox
->emitLink
= cg
.objectList
.lastbox
;
873 cg
.objectList
.lastbox
= funbox
;
874 cg
.objectList
.length
++;
879 * Inline Statements to emit as we go to save AST space. We must generate
880 * our script-body blockid since we aren't calling Statements.
883 if (!GenerateBlockId(&cg
, bodyid
))
887 #if JS_HAS_XML_SUPPORT
893 CG_SWITCH_TO_PROLOG(&cg
);
894 if (js_Emit1(cx
, &cg
, JSOP_TRACE
) < 0)
896 CG_SWITCH_TO_MAIN(&cg
);
898 inDirectivePrologue
= true;
900 jsc
.tokenStream
.flags
|= TSF_OPERAND
;
901 tt
= js_PeekToken(cx
, &jsc
.tokenStream
);
902 jsc
.tokenStream
.flags
&= ~TSF_OPERAND
;
906 JS_ASSERT(tt
== TOK_ERROR
);
910 pn
= Statement(cx
, &jsc
.tokenStream
, &cg
);
913 JS_ASSERT(!cg
.blockNode
);
915 if (inDirectivePrologue
)
916 inDirectivePrologue
= RecognizeDirectivePrologue(cx
, &jsc
.tokenStream
, &cg
, pn
);
918 if (!js_FoldConstants(cx
, pn
, &cg
))
921 if (cg
.functionList
) {
922 if (!jsc
.analyzeFunctions(cg
.functionList
, cg
.flags
))
924 cg
.functionList
= NULL
;
927 if (!js_EmitTree(cx
, &cg
, pn
))
929 #if JS_HAS_XML_SUPPORT
930 if (PN_TYPE(pn
) != TOK_SEMI
||
932 !TREE_TYPE_IS_XML(PN_TYPE(pn
->pn_kid
))) {
936 RecycleTree(pn
, &cg
);
939 #if JS_HAS_XML_SUPPORT
941 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
942 * For background, see:
944 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
946 if (pn
&& onlyXML
&& (tcflags
& TCF_NO_SCRIPT_RVAL
)) {
947 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
, JSREPORT_ERROR
,
948 JSMSG_XML_WHOLE_PROGRAM
);
954 * Global variables and regexps share the index space with locals. Due to
955 * incremental code generation we need to patch the bytecode to adjust the
956 * local references to skip the globals.
958 scriptGlobals
= cg
.ngvars
+ cg
.regexpList
.length
;
959 if (scriptGlobals
!= 0 || cg
.hasSharps()) {
960 jsbytecode
*code
, *end
;
962 const JSCodeSpec
*cs
;
965 if (scriptGlobals
>= SLOTNO_LIMIT
)
968 for (end
= code
+ CG_OFFSET(&cg
); code
!= end
; code
+= len
) {
969 JS_ASSERT(code
< end
);
971 cs
= &js_CodeSpec
[op
];
972 len
= (cs
->length
> 0)
974 : js_GetVariableBytecodeLength(code
);
975 if ((cs
->format
& JOF_SHARPSLOT
) ||
976 JOF_TYPE(cs
->format
) == JOF_LOCAL
||
977 (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
)) {
979 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
980 * emitted only for a function.
982 JS_ASSERT_IF(!(cs
->format
& JOF_SHARPSLOT
),
983 (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
) ==
984 (op
== JSOP_GETLOCALPROP
));
985 slot
= GET_SLOTNO(code
);
986 slot
+= scriptGlobals
;
987 if (!(cs
->format
& JOF_SHARPSLOT
))
988 slot
+= cg
.sharpSlots();
989 if (slot
>= SLOTNO_LIMIT
)
991 SET_SLOTNO(code
, slot
);
996 #ifdef METER_PARSENODES
997 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
998 (char *)sbrk(0) - (char *)before
,
1001 parsenodes
- recyclednodes
);
1006 * Nowadays the threaded interpreter needs a stop instruction, so we
1007 * do have to emit that here.
1009 if (js_Emit1(cx
, &cg
, JSOP_STOP
) < 0)
1011 #ifdef METER_PARSENODES
1012 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
1013 (char *)sbrk(0) - (char *)before
, CG_OFFSET(&cg
), cg
.noteCount
);
1015 #ifdef JS_ARENAMETER
1016 JS_DumpArenaStats(stdout
);
1018 script
= js_NewScriptFromCG(cx
, &cg
);
1019 if (script
&& funbox
)
1020 script
->savedCallerFun
= true;
1022 #ifdef JS_SCOPE_DEPTH_METER
1024 JSObject
*obj
= scopeChain
;
1026 while ((obj
= OBJ_GET_PARENT(cx
, obj
)) != NULL
)
1028 JS_BASIC_STATS_ACCUM(&cx
->runtime
->hostenvScopeDepthStats
, depth
);
1033 JS_FinishArenaPool(&codePool
);
1034 JS_FinishArenaPool(¬ePool
);
1038 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
,
1039 JSREPORT_ERROR
, JSMSG_TOO_MANY_LOCALS
);
1045 * Insist on a final return before control flows out of pn. Try to be a bit
1046 * smart about loops: do {...; return e2;} while(0) at the end of a function
1047 * that contains an early return e1 will get a strict warning. Similarly for
1048 * iloops: while (true){...} is treated as though ... returns.
1050 #define ENDS_IN_OTHER 0
1051 #define ENDS_IN_RETURN 1
1052 #define ENDS_IN_BREAK 2
1055 HasFinalReturn(JSParseNode
*pn
)
1057 JSParseNode
*pn2
, *pn3
;
1058 uintN rv
, rv2
, hasDefault
;
1060 switch (pn
->pn_type
) {
1063 return ENDS_IN_OTHER
;
1064 return HasFinalReturn(pn
->last());
1068 return ENDS_IN_OTHER
;
1069 return HasFinalReturn(pn
->pn_kid2
) & HasFinalReturn(pn
->pn_kid3
);
1073 if (pn2
->pn_type
== TOK_PRIMARY
&& pn2
->pn_op
== JSOP_TRUE
)
1074 return ENDS_IN_RETURN
;
1075 if (pn2
->pn_type
== TOK_NUMBER
&& pn2
->pn_dval
)
1076 return ENDS_IN_RETURN
;
1077 return ENDS_IN_OTHER
;
1081 if (pn2
->pn_type
== TOK_PRIMARY
) {
1082 if (pn2
->pn_op
== JSOP_FALSE
)
1083 return HasFinalReturn(pn
->pn_left
);
1084 if (pn2
->pn_op
== JSOP_TRUE
)
1085 return ENDS_IN_RETURN
;
1087 if (pn2
->pn_type
== TOK_NUMBER
) {
1088 if (pn2
->pn_dval
== 0)
1089 return HasFinalReturn(pn
->pn_left
);
1090 return ENDS_IN_RETURN
;
1092 return ENDS_IN_OTHER
;
1096 if (pn2
->pn_arity
== PN_TERNARY
&& !pn2
->pn_kid2
)
1097 return ENDS_IN_RETURN
;
1098 return ENDS_IN_OTHER
;
1101 rv
= ENDS_IN_RETURN
;
1102 hasDefault
= ENDS_IN_OTHER
;
1104 if (pn2
->pn_type
== TOK_LEXICALSCOPE
)
1106 for (pn2
= pn2
->pn_head
; rv
&& pn2
; pn2
= pn2
->pn_next
) {
1107 if (pn2
->pn_type
== TOK_DEFAULT
)
1108 hasDefault
= ENDS_IN_RETURN
;
1109 pn3
= pn2
->pn_right
;
1110 JS_ASSERT(pn3
->pn_type
== TOK_LC
);
1112 rv2
= HasFinalReturn(pn3
->last());
1113 if (rv2
== ENDS_IN_OTHER
&& pn2
->pn_next
)
1114 /* Falling through to next case or default. */;
1119 /* If a final switch has no default case, we judge it harshly. */
1124 return ENDS_IN_BREAK
;
1127 return HasFinalReturn(pn
->pn_right
);
1130 return ENDS_IN_RETURN
;
1133 case TOK_LEXICALSCOPE
:
1134 return HasFinalReturn(pn
->expr());
1137 return ENDS_IN_RETURN
;
1140 /* If we have a finally block that returns, we are done. */
1142 rv
= HasFinalReturn(pn
->pn_kid3
);
1143 if (rv
== ENDS_IN_RETURN
)
1147 /* Else check the try block and any and all catch statements. */
1148 rv
= HasFinalReturn(pn
->pn_kid1
);
1150 JS_ASSERT(pn
->pn_kid2
->pn_arity
== PN_LIST
);
1151 for (pn2
= pn
->pn_kid2
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
1152 rv
&= HasFinalReturn(pn2
);
1157 /* Check this catch block's body. */
1158 return HasFinalReturn(pn
->pn_kid3
);
1161 /* Non-binary let statements are let declarations. */
1162 if (pn
->pn_arity
!= PN_BINARY
)
1163 return ENDS_IN_OTHER
;
1164 return HasFinalReturn(pn
->pn_right
);
1167 return ENDS_IN_OTHER
;
1172 ReportBadReturn(JSContext
*cx
, JSTreeContext
*tc
, uintN flags
, uintN errnum
,
1177 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1178 if (tc
->fun
->atom
) {
1179 name
= js_AtomToPrintableString(cx
, tc
->fun
->atom
);
1181 errnum
= anonerrnum
;
1184 return js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
, flags
,
1189 CheckFinalReturn(JSContext
*cx
, JSTreeContext
*tc
, JSParseNode
*pn
)
1191 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1192 return HasFinalReturn(pn
) == ENDS_IN_RETURN
||
1193 ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
1194 JSMSG_NO_RETURN_VALUE
, JSMSG_ANON_NO_RETURN_VALUE
);
1198 * Check that it is permitted to assign to lhs. Strict mode code may not
1199 * assign to 'eval' or 'arguments'.
1202 CheckStrictAssignment(JSContext
*cx
, JSTreeContext
*tc
, JSParseNode
*lhs
)
1204 if (tc
->needStrictChecks() &&
1205 lhs
->pn_type
== TOK_NAME
) {
1206 JSAtom
*atom
= lhs
->pn_atom
;
1207 JSAtomState
*atomState
= &cx
->runtime
->atomState
;
1208 if (atom
== atomState
->evalAtom
|| atom
== atomState
->argumentsAtom
) {
1209 const char *name
= js_AtomToPrintableString(cx
, atom
);
1211 !js_ReportStrictModeError(cx
, TS(tc
->compiler
), tc
, lhs
,
1212 JSMSG_DEPRECATED_ASSIGN
, name
)) {
1221 * Check that it is permitted to introduce a binding for atom. Strict
1222 * mode forbids introducing new definitions for 'eval' or 'arguments'.
1223 * Use pn for reporting error locations, or use tc's token stream if
1227 CheckStrictBinding(JSContext
*cx
, JSTreeContext
*tc
, JSAtom
*atom
,
1230 if (!tc
->needStrictChecks())
1233 JSAtomState
*atomState
= &cx
->runtime
->atomState
;
1234 if (atom
== atomState
->evalAtom
|| atom
== atomState
->argumentsAtom
) {
1235 const char *name
= js_AtomToPrintableString(cx
, atom
);
1237 js_ReportStrictModeError(cx
, TS(tc
->compiler
), tc
, pn
,
1238 JSMSG_BAD_BINDING
, name
);
1245 * In strict mode code, all formal parameter names must be distinct. If fun's
1246 * formals are legit given fun's strictness level, return true. Otherwise,
1247 * report an error and return false. Use pn for error position reporting,
1248 * unless we can find something more accurate in tc's decls.
1250 * In some cases the code to parse the argument list will already have noticed
1251 * the duplication; we could try to use that knowledge instead of re-checking
1252 * here. But since the strictness of the function's body determines what
1253 * constraints to apply to the argument list, we can't report the error until
1254 * after we've parsed the body. And as it turns out, the function's local name
1255 * list makes it reasonably cheap to find duplicates after the fact.
1258 CheckStrictFormals(JSContext
*cx
, JSTreeContext
*tc
, JSFunction
*fun
,
1263 if (!tc
->needStrictChecks())
1266 atom
= fun
->findDuplicateFormal();
1269 * We have found a duplicate parameter name. If we can find the
1270 * JSDefinition for the argument, that will have a more accurate source
1273 JSDefinition
*dn
= ALE_DEFN(tc
->decls
.lookup(atom
));
1274 if (dn
->pn_op
== JSOP_GETARG
)
1276 const char *name
= js_AtomToPrintableString(cx
, atom
);
1278 !js_ReportStrictModeError(cx
, TS(tc
->compiler
), tc
, pn
,
1279 JSMSG_DUPLICATE_FORMAL
, name
)) {
1284 if (tc
->flags
& (TCF_FUN_PARAM_ARGUMENTS
| TCF_FUN_PARAM_EVAL
)) {
1285 JSAtomState
*atoms
= &cx
->runtime
->atomState
;
1286 atom
= (tc
->flags
& TCF_FUN_PARAM_ARGUMENTS
1287 ? atoms
->argumentsAtom
: atoms
->evalAtom
);
1288 /* The definition's source position will be more precise. */
1289 JSDefinition
*dn
= ALE_DEFN(tc
->decls
.lookup(atom
));
1290 JS_ASSERT(dn
->pn_atom
== atom
);
1291 const char *name
= js_AtomToPrintableString(cx
, atom
);
1293 !js_ReportStrictModeError(cx
, TS(tc
->compiler
), tc
, dn
,
1294 JSMSG_BAD_BINDING
, name
)) {
1302 static JSParseNode
*
1303 FunctionBody(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1305 JSStmtInfo stmtInfo
;
1306 uintN oldflags
, firstLine
;
1309 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1310 js_PushStatement(tc
, &stmtInfo
, STMT_BLOCK
, -1);
1311 stmtInfo
.flags
= SIF_BODY_BLOCK
;
1313 oldflags
= tc
->flags
;
1314 tc
->flags
&= ~(TCF_RETURN_EXPR
| TCF_RETURN_VOID
);
1317 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1318 * later, because we may have not peeked in ts yet, so Statements won't
1319 * acquire a valid pn->pn_pos.begin from the current token.
1321 firstLine
= ts
->lineno
;
1322 #if JS_HAS_EXPR_CLOSURES
1323 if (CURRENT_TOKEN(ts
).type
== TOK_LC
) {
1324 pn
= Statements(cx
, ts
, tc
);
1326 pn
= NewParseNode(PN_UNARY
, tc
);
1328 pn
->pn_kid
= AssignExpr(cx
, ts
, tc
);
1332 if (tc
->flags
& TCF_FUN_IS_GENERATOR
) {
1333 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
1334 JSMSG_BAD_GENERATOR_RETURN
,
1335 JSMSG_BAD_ANON_GENERATOR_RETURN
);
1338 pn
->pn_type
= TOK_RETURN
;
1339 pn
->pn_op
= JSOP_RETURN
;
1340 pn
->pn_pos
.end
= pn
->pn_kid
->pn_pos
.end
;
1346 pn
= Statements(cx
, ts
, tc
);
1350 JS_ASSERT(!(tc
->topStmt
->flags
& SIF_SCOPE
));
1351 js_PopStatement(tc
);
1352 pn
->pn_pos
.begin
.lineno
= firstLine
;
1354 /* Check for falling off the end of a function that returns a value. */
1355 if (JS_HAS_STRICT_OPTION(cx
) && (tc
->flags
& TCF_RETURN_EXPR
) &&
1356 !CheckFinalReturn(cx
, tc
, pn
)) {
1361 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
1365 static JSAtomListElement
*
1366 MakePlaceholder(JSParseNode
*pn
, JSTreeContext
*tc
)
1368 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->compiler
, pn
->pn_atom
);
1372 JSDefinition
*dn
= (JSDefinition
*)
1373 NewNameNode(tc
->compiler
->context
, pn
->pn_atom
, tc
);
1377 ALE_SET_DEFN(ale
, dn
);
1379 dn
->pn_dflags
|= PND_PLACEHOLDER
;
1384 Define(JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
1386 JS_ASSERT(!pn
->pn_used
);
1387 JS_ASSERT_IF(pn
->pn_defn
, pn
->isPlaceholder());
1390 JSAtomListElement
*ale
= NULL
;
1391 JSAtomList
*list
= NULL
;
1394 ale
= (list
= &tc
->decls
)->rawLookup(atom
, hep
);
1396 ale
= (list
= &tc
->lexdeps
)->rawLookup(atom
, hep
);
1399 JSDefinition
*dn
= ALE_DEFN(ale
);
1401 JSParseNode
**pnup
= &dn
->dn_uses
;
1403 uintN start
= let
? pn
->pn_blockid
: tc
->bodyid
;
1405 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_blockid
>= start
) {
1406 JS_ASSERT(pnu
->pn_used
);
1407 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1408 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1409 pnup
= &pnu
->pn_link
;
1412 if (pnu
!= dn
->dn_uses
) {
1413 *pnup
= pn
->dn_uses
;
1414 pn
->dn_uses
= dn
->dn_uses
;
1417 if ((!pnu
|| pnu
->pn_blockid
< tc
->bodyid
) && list
!= &tc
->decls
)
1418 list
->rawRemove(tc
->compiler
, ale
, hep
);
1423 ale
= tc
->decls
.add(tc
->compiler
, atom
, let
? JSAtomList::SHADOW
: JSAtomList::UNIQUE
);
1426 ALE_SET_DEFN(ale
, pn
);
1428 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
1433 LinkUseToDef(JSParseNode
*pn
, JSDefinition
*dn
, JSTreeContext
*tc
)
1435 JS_ASSERT(!pn
->pn_used
);
1436 JS_ASSERT(!pn
->pn_defn
);
1437 JS_ASSERT(pn
!= dn
->dn_uses
);
1438 pn
->pn_link
= dn
->dn_uses
;
1440 dn
->pn_dflags
|= pn
->pn_dflags
& PND_USE2DEF_FLAGS
;
1446 ForgetUse(JSParseNode
*pn
)
1449 JS_ASSERT(!pn
->pn_defn
);
1453 JSParseNode
**pnup
= &pn
->lexdef()->dn_uses
;
1455 while ((pnu
= *pnup
) != pn
)
1456 pnup
= &pnu
->pn_link
;
1457 *pnup
= pn
->pn_link
;
1458 pn
->pn_used
= false;
1461 static JSParseNode
*
1462 MakeAssignment(JSParseNode
*pn
, JSParseNode
*rhs
, JSTreeContext
*tc
)
1464 JSParseNode
*lhs
= NewOrRecycledNode(tc
);
1470 JSDefinition
*dn
= pn
->pn_lexdef
;
1471 JSParseNode
**pnup
= &dn
->dn_uses
;
1474 pnup
= &(*pnup
)->pn_link
;
1476 lhs
->pn_link
= pn
->pn_link
;
1480 pn
->pn_type
= TOK_ASSIGN
;
1481 pn
->pn_op
= JSOP_NOP
;
1482 pn
->pn_arity
= PN_BINARY
;
1483 pn
->pn_parens
= false;
1484 pn
->pn_used
= pn
->pn_defn
= false;
1490 static JSParseNode
*
1491 MakeDefIntoUse(JSDefinition
*dn
, JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
)
1494 * If dn is var, const, or let, and it has an initializer, then we must
1495 * rewrite it to be an assignment node, whose freshly allocated left-hand
1496 * side becomes a use of pn.
1498 if (dn
->isBindingForm()) {
1499 JSParseNode
*rhs
= dn
->expr();
1501 JSParseNode
*lhs
= MakeAssignment(dn
, rhs
, tc
);
1504 //pn->dn_uses = lhs;
1505 dn
= (JSDefinition
*) lhs
;
1508 dn
->pn_op
= (js_CodeSpec
[dn
->pn_op
].format
& JOF_SET
) ? JSOP_SETNAME
: JSOP_NAME
;
1509 } else if (dn
->kind() == JSDefinition::FUNCTION
) {
1510 JS_ASSERT(dn
->isTopLevel());
1511 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
1512 dn
->pn_type
= TOK_NAME
;
1513 dn
->pn_arity
= PN_NAME
;
1517 /* Now make dn no longer a definition, rather a use of pn. */
1518 JS_ASSERT(dn
->pn_type
== TOK_NAME
);
1519 JS_ASSERT(dn
->pn_arity
== PN_NAME
);
1520 JS_ASSERT(dn
->pn_atom
== atom
);
1522 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
1523 JS_ASSERT(pnu
->pn_used
);
1524 JS_ASSERT(!pnu
->pn_defn
);
1525 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1526 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1528 pn
->pn_dflags
|= dn
->pn_dflags
& PND_USE2DEF_FLAGS
;
1531 dn
->pn_defn
= false;
1533 dn
->pn_lexdef
= (JSDefinition
*) pn
;
1534 dn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1535 dn
->pn_dflags
&= ~PND_BOUND
;
1540 DefineArg(JSParseNode
*pn
, JSAtom
*atom
, uintN i
, JSTreeContext
*tc
)
1542 JSParseNode
*argpn
, *argsbody
;
1544 /* Flag tc so we don't have to lookup arguments on every use. */
1545 if (atom
== tc
->compiler
->context
->runtime
->atomState
.argumentsAtom
)
1546 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1547 if (atom
== tc
->compiler
->context
->runtime
->atomState
.evalAtom
)
1548 tc
->flags
|= TCF_FUN_PARAM_EVAL
;
1551 * Make an argument definition node, distinguished by being in tc->decls
1552 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1553 * list node returned via pn->pn_body.
1555 argpn
= NewNameNode(tc
->compiler
->context
, atom
, tc
);
1558 JS_ASSERT(PN_TYPE(argpn
) == TOK_NAME
&& PN_OP(argpn
) == JSOP_NOP
);
1560 /* Arguments are initialized by definition. */
1561 argpn
->pn_dflags
|= PND_INITIALIZED
;
1562 if (!Define(argpn
, atom
, tc
))
1565 argsbody
= pn
->pn_body
;
1567 argsbody
= NewParseNode(PN_LIST
, tc
);
1570 argsbody
->pn_type
= TOK_ARGSBODY
;
1571 argsbody
->pn_op
= JSOP_NOP
;
1572 argsbody
->makeEmpty();
1573 pn
->pn_body
= argsbody
;
1575 argsbody
->append(argpn
);
1577 argpn
->pn_op
= JSOP_GETARG
;
1578 argpn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, i
);
1579 argpn
->pn_dflags
|= PND_BOUND
;
1584 * Compile a JS function body, which might appear as the value of an event
1585 * handler attribute in an HTML <INPUT> tag.
1588 JSCompiler::compileFunctionBody(JSContext
*cx
, JSFunction
*fun
, JSPrincipals
*principals
,
1589 const jschar
*chars
, size_t length
,
1590 const char *filename
, uintN lineno
)
1592 JSCompiler
jsc(cx
, principals
);
1594 if (!jsc
.init(chars
, length
, NULL
, filename
, lineno
))
1597 /* No early return from after here until the js_FinishArenaPool calls. */
1598 JSArenaPool codePool
, notePool
;
1599 JS_InitArenaPool(&codePool
, "code", 1024, sizeof(jsbytecode
),
1600 &cx
->scriptStackQuota
);
1601 JS_InitArenaPool(¬ePool
, "note", 1024, sizeof(jssrcnote
),
1602 &cx
->scriptStackQuota
);
1604 JSCodeGenerator
funcg(&jsc
, &codePool
, ¬ePool
, jsc
.tokenStream
.lineno
);
1605 funcg
.flags
|= TCF_IN_FUNCTION
;
1607 if (!GenerateBlockId(&funcg
, funcg
.bodyid
))
1610 /* FIXME: make Function format the source for a function definition. */
1611 jsc
.tokenStream
.tokens
[0].type
= TOK_NAME
;
1612 JSParseNode
*fn
= NewParseNode(PN_FUNC
, &funcg
);
1615 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1617 uintN nargs
= fun
->nargs
;
1619 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
1623 for (uintN i
= 0; i
< nargs
; i
++) {
1624 JSAtom
*name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1625 if (!DefineArg(fn
, name
, i
, &funcg
)) {
1635 * Farble the body so that it looks like a block statement to js_EmitTree,
1636 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1637 * done parsing, we must fold constants, analyze any nested functions, and
1638 * generate code for this function, including a stop opcode at the end.
1640 CURRENT_TOKEN(&jsc
.tokenStream
).type
= TOK_LC
;
1641 JSParseNode
*pn
= fn
? FunctionBody(cx
, &jsc
.tokenStream
, &funcg
) : NULL
;
1643 if (!CheckStrictFormals(cx
, &funcg
, fun
, pn
)) {
1645 } else if (!js_MatchToken(cx
, &jsc
.tokenStream
, TOK_EOF
)) {
1646 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
,
1647 JSREPORT_ERROR
, JSMSG_SYNTAX_ERROR
);
1649 } else if (!js_FoldConstants(cx
, pn
, &funcg
)) {
1650 /* js_FoldConstants reported the error already. */
1652 } else if (funcg
.functionList
&&
1653 !jsc
.analyzeFunctions(funcg
.functionList
, funcg
.flags
)) {
1657 JS_ASSERT(PN_TYPE(fn
->pn_body
) == TOK_ARGSBODY
);
1658 fn
->pn_body
->append(pn
);
1659 fn
->pn_body
->pn_pos
= pn
->pn_pos
;
1663 if (!js_EmitFunctionScript(cx
, &funcg
, pn
))
1668 /* Restore saved state and release code generation arenas. */
1669 JS_FinishArenaPool(&codePool
);
1670 JS_FinishArenaPool(¬ePool
);
1675 * Parameter block types for the several Binder functions. We use a common
1676 * helper function signature in order to share code among destructuring and
1677 * simple variable declaration parsers. In the destructuring case, the binder
1678 * function is called indirectly from the variable declaration parser by way
1679 * of CheckDestructuring and its friends.
1681 typedef struct BindData BindData
;
1684 (*Binder
)(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
);
1687 BindData() : fresh(true) {}
1689 JSParseNode
*pn
; /* name node for definition processing and
1690 error source coordinates */
1691 JSOp op
; /* prolog bytecode or nop */
1692 Binder binder
; /* binder, discriminates u */
1702 BindLocalVariable(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
,
1703 JSLocalKind localKind
, bool isArg
)
1705 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1708 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1709 * Instead 'var arguments' always restates the predefined property of the
1710 * activation objects whose name is 'arguments'. Assignment to such a
1711 * variable must be handled specially.
1713 * Special case: an argument named 'arguments' *does* shadow the predefined
1714 * arguments property.
1716 if (atom
== cx
->runtime
->atomState
.argumentsAtom
&& !isArg
)
1719 return js_AddLocal(cx
, fun
, atom
, localKind
);
1722 #if JS_HAS_DESTRUCTURING
1724 * Forward declaration to maintain top-down presentation.
1726 static JSParseNode
*
1727 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
1731 BindDestructuringArg(JSContext
*cx
, BindData
*data
, JSAtom
*atom
,
1736 /* Flag tc so we don't have to lookup arguments on every use. */
1737 if (atom
== tc
->compiler
->context
->runtime
->atomState
.argumentsAtom
)
1738 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1739 if (atom
== tc
->compiler
->context
->runtime
->atomState
.evalAtom
)
1740 tc
->flags
|= TCF_FUN_PARAM_EVAL
;
1742 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1744 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
1745 if (localKind
!= JSLOCAL_NONE
) {
1746 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
,
1747 JSREPORT_ERROR
, JSMSG_DESTRUCT_DUP_ARG
);
1750 JS_ASSERT(!tc
->decls
.lookup(atom
));
1753 if (!Define(pn
, atom
, tc
))
1756 uintN index
= tc
->fun
->u
.i
.nvars
;
1757 if (!BindLocalVariable(cx
, tc
->fun
, atom
, JSLOCAL_VAR
, true))
1759 pn
->pn_op
= JSOP_SETLOCAL
;
1760 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
1761 pn
->pn_dflags
|= PND_BOUND
;
1764 #endif /* JS_HAS_DESTRUCTURING */
1767 JSCompiler::newFunction(JSTreeContext
*tc
, JSAtom
*atom
, uintN lambda
)
1772 JS_ASSERT((lambda
& ~JSFUN_LAMBDA
) == 0);
1775 * Find the global compilation context in order to pre-set the newborn
1776 * function's parent slot to tc->scopeChain. If the global context is a
1777 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1778 * clear parent and proto.
1782 parent
= (tc
->flags
& TCF_IN_FUNCTION
) ? NULL
: tc
->scopeChain
;
1784 fun
= js_NewFunction(context
, NULL
, NULL
, 0, JSFUN_INTERPRETED
| lambda
,
1787 if (fun
&& !(tc
->flags
& TCF_COMPILE_N_GO
)) {
1788 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun
));
1789 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun
));
1795 MatchOrInsertSemicolon(JSContext
*cx
, JSTokenStream
*ts
)
1799 ts
->flags
|= TSF_OPERAND
;
1800 tt
= js_PeekTokenSameLine(cx
, ts
);
1801 ts
->flags
&= ~TSF_OPERAND
;
1802 if (tt
== TOK_ERROR
)
1804 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
1805 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1806 JSMSG_SEMI_BEFORE_STMNT
);
1809 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
1814 JSCompiler::analyzeFunctions(JSFunctionBox
*funbox
, uint32
& tcflags
)
1816 if (!markFunArgs(funbox
, tcflags
))
1818 setFunctionKinds(funbox
, tcflags
);
1823 * Mark as funargs any functions that reach up to one or more upvars across an
1824 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1826 * function f(o, p) {
1827 * o.m = function o_m(a) {
1828 * function g() { return p; }
1829 * function h() { return a; }
1834 * but without this extra marking phase, function g will not be marked as a
1835 * funarg since it is called from within its parent scope. But g reaches up to
1836 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1837 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1838 * nor uses an upvar "above" o_m's level.
1840 * If function g itself contained lambdas that contained non-lambdas that reach
1841 * up above its level, then those non-lambdas would have to be marked too. This
1842 * process is potentially exponential in the number of functions, but generally
1843 * not so complex. But it can't be done during a single recursive traversal of
1844 * the funbox tree, so we must use a work queue.
1846 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1847 * between the static level of the bodies of funbox and its peers (which must
1848 * be funbox->level + 1), and the static level of the nearest upvar among all
1849 * the upvars contained by funbox and its peers. If there are no upvars, return
1850 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1853 FindFunArgs(JSFunctionBox
*funbox
, int level
, JSFunctionBoxQueue
*queue
)
1855 uintN allskipmin
= FREE_STATIC_LEVEL
;
1858 JSParseNode
*fn
= funbox
->node
;
1859 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
1860 int fnlevel
= level
;
1863 * An eval can leak funbox, functions along its ancestor line, and its
1864 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1865 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1866 * already been marked as funargs by this point. Therefore we have to
1867 * flag only funbox->node and funbox->kids' nodes here.
1869 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
1871 for (JSFunctionBox
*kid
= funbox
->kids
; kid
; kid
= kid
->siblings
)
1872 kid
->node
->setFunArg();
1876 * Compute in skipmin the least distance from fun's static level up to
1877 * an upvar, whether used directly by fun, or indirectly by a function
1880 uintN skipmin
= FREE_STATIC_LEVEL
;
1881 JSParseNode
*pn
= fn
->pn_body
;
1883 if (pn
->pn_type
== TOK_UPVARS
) {
1884 JSAtomList
upvars(pn
->pn_names
);
1885 JS_ASSERT(upvars
.count
!= 0);
1887 JSAtomListIterator
iter(&upvars
);
1888 JSAtomListElement
*ale
;
1890 while ((ale
= iter()) != NULL
) {
1891 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1893 if (!lexdep
->isFreeVar()) {
1894 uintN upvarLevel
= lexdep
->frameLevel();
1896 if (int(upvarLevel
) <= fnlevel
)
1899 uintN skip
= (funbox
->level
+ 1) - upvarLevel
;
1907 * If this function escapes, whether directly (the parser detects such
1908 * escapes) or indirectly (because this non-escaping function uses an
1909 * upvar that reaches across an outer function boundary where the outer
1910 * function escapes), enqueue it for further analysis, and bump fnlevel
1911 * to trap any non-escaping children.
1913 if (fn
->isFunArg()) {
1914 queue
->push(funbox
);
1915 fnlevel
= int(funbox
->level
);
1919 * Now process the current function's children, and recalibrate their
1920 * cumulative skipmin to be relative to the current static level.
1923 uintN kidskipmin
= FindFunArgs(funbox
->kids
, fnlevel
, queue
);
1925 JS_ASSERT(kidskipmin
!= 0);
1926 if (kidskipmin
!= FREE_STATIC_LEVEL
) {
1928 if (kidskipmin
!= 0 && kidskipmin
< skipmin
)
1929 skipmin
= kidskipmin
;
1934 * Finally, after we've traversed all of the current function's kids,
1935 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1936 * with allskipmin, but minimize across funbox and all of its siblings,
1937 * to compute our return value.
1939 if (skipmin
!= FREE_STATIC_LEVEL
) {
1940 fun
->u
.i
.skipmin
= skipmin
;
1941 if (skipmin
< allskipmin
)
1942 allskipmin
= skipmin
;
1944 } while ((funbox
= funbox
->siblings
) != NULL
);
1950 JSCompiler::markFunArgs(JSFunctionBox
*funbox
, uintN tcflags
)
1952 JSFunctionBoxQueue queue
;
1953 if (!queue
.init(functionCount
))
1956 FindFunArgs(funbox
, -1, &queue
);
1957 while ((funbox
= queue
.pull()) != NULL
) {
1958 JSParseNode
*fn
= funbox
->node
;
1959 JS_ASSERT(fn
->isFunArg());
1961 JSParseNode
*pn
= fn
->pn_body
;
1962 if (pn
->pn_type
== TOK_UPVARS
) {
1963 JSAtomList
upvars(pn
->pn_names
);
1964 JS_ASSERT(upvars
.count
!= 0);
1966 JSAtomListIterator
iter(&upvars
);
1967 JSAtomListElement
*ale
;
1969 while ((ale
= iter()) != NULL
) {
1970 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1972 if (!lexdep
->isFreeVar() &&
1973 !lexdep
->isFunArg() &&
1974 lexdep
->kind() == JSDefinition::FUNCTION
) {
1976 * Mark this formerly-Algol-like function as an escaping
1977 * function (i.e., as a funarg), because it is used from a
1978 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1981 * Progress is guaranteed because we set the funarg flag
1982 * here, which suppresses revisiting this function (thanks
1983 * to the !lexdep->isFunArg() test just above).
1985 lexdep
->setFunArg();
1987 JSFunctionBox
*afunbox
= lexdep
->pn_funbox
;
1988 queue
.push(afunbox
);
1991 * Walk over nested functions again, now that we have
1992 * changed the level across which it is unsafe to access
1993 * upvars using the runtime dynamic link (frame chain).
1996 FindFunArgs(afunbox
->kids
, afunbox
->level
, &queue
);
2005 MinBlockId(JSParseNode
*fn
, uint32 id
)
2007 if (fn
->pn_blockid
< id
)
2010 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
2011 if (pn
->pn_blockid
< id
)
2019 OneBlockId(JSParseNode
*fn
, uint32 id
)
2021 if (fn
->pn_blockid
!= id
)
2024 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
2025 if (pn
->pn_blockid
!= id
)
2033 JSCompiler::setFunctionKinds(JSFunctionBox
*funbox
, uint32
& tcflags
)
2035 #ifdef JS_FUNCTION_METERING
2036 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
2038 # define FUN_METER(x) ((void)0)
2040 JSFunctionBox
*parent
= funbox
->parent
;
2043 JSParseNode
*fn
= funbox
->node
;
2046 setFunctionKinds(funbox
->kids
, tcflags
);
2048 JSParseNode
*pn
= fn
->pn_body
;
2049 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
2052 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
2054 JS_ASSERT(FUN_KIND(fun
) == JSFUN_INTERPRETED
);
2055 } else if (pn
->pn_type
!= TOK_UPVARS
) {
2057 * No lexical dependencies => null closure, for best performance.
2058 * A null closure needs no scope chain, but alas we've coupled
2059 * principals-finding to scope (for good fundamental reasons, but
2060 * the implementation overloads the parent slot and we should fix
2061 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
2063 * In more detail: the ES3 spec allows the implementation to create
2064 * "joined function objects", or not, at its discretion. But real-
2065 * world implementations always create unique function objects for
2066 * closures, and this can be detected via mutation. Open question:
2067 * do popular implementations create unique function objects for
2070 * FIXME: bug 476950.
2072 FUN_METER(nofreeupvar
);
2073 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2075 JSAtomList
upvars(pn
->pn_names
);
2076 JS_ASSERT(upvars
.count
!= 0);
2078 JSAtomListIterator
iter(&upvars
);
2079 JSAtomListElement
*ale
;
2081 if (!fn
->isFunArg()) {
2083 * This function is Algol-like, it never escapes. So long as it
2084 * does not assign to outer variables, it needs only an upvars
2085 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
2086 * bytecode to reach up the frame stack at runtime based on
2087 * those upvars' cookies.
2089 * Any assignments to upvars from functions called by this one
2090 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
2091 * which load from stack homes when interpreting or from native
2092 * stack slots when executing a trace.
2094 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
2095 * nested function to assign to an outer lexical variable, so
2096 * we defer adding yet more code footprint in the absence of
2097 * evidence motivating these opcodes.
2099 bool mutation
= !!(funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
);
2103 * Check that at least one outer lexical binding was assigned
2104 * to (global variables don't count). This is conservative: we
2105 * could limit assignments to those in the current function,
2106 * but that's too much work. As with flat closures (handled
2107 * below), we optimize for the case where outer bindings are
2108 * not reassigned anywhere.
2110 while ((ale
= iter()) != NULL
) {
2111 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2113 if (!lexdep
->isFreeVar()) {
2114 JS_ASSERT(lexdep
->frameLevel() <= funbox
->level
);
2116 if (lexdep
->isAssigned())
2124 FUN_METER(onlyfreevar
);
2125 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2126 } else if (!mutation
&& !(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
)) {
2128 * Algol-like functions can read upvars using the dynamic
2129 * link (cx->fp/fp->down). They do not need to entrain and
2130 * search their environment.
2133 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2135 if (!(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
))
2136 FUN_METER(setupvar
);
2142 * For each lexical dependency from this closure to an outer
2143 * binding, analyze whether it is safe to copy the binding's
2144 * value into a flat closure slot when the closure is formed.
2146 while ((ale
= iter()) != NULL
) {
2147 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2149 if (!lexdep
->isFreeVar()) {
2153 * Consider the current function (the lambda, innermost
2154 * below) using a var x defined two static levels up:
2160 * return function () { return x; };
2165 * So long as (1) the initialization in 'var x = 42'
2166 * dominates all uses of g and (2) x is not reassigned,
2167 * it is safe to optimize the lambda to a flat closure.
2168 * Uncommenting the early call to g makes it unsafe to
2169 * so optimize (z could name a global setter that calls
2172 JSFunctionBox
*afunbox
= funbox
;
2173 uintN lexdepLevel
= lexdep
->frameLevel();
2175 JS_ASSERT(lexdepLevel
<= funbox
->level
);
2176 while (afunbox
->level
!= lexdepLevel
) {
2177 afunbox
= afunbox
->parent
;
2180 * afunbox can't be null because we are sure
2181 * to find a function box whose level == lexdepLevel
2182 * before walking off the top of the funbox tree.
2183 * See bug 493260 comments 16-18.
2185 * Assert but check anyway, to check future changes
2186 * that bind eval upvars in the parser.
2191 * If this function is reaching up across an
2192 * enclosing funarg, we cannot make a flat
2193 * closure. The display stops working once the
2196 if (!afunbox
|| afunbox
->node
->isFunArg())
2201 * If afunbox's function (which is at the same level as
2202 * lexdep) is in a loop, pessimistically assume the
2203 * variable initializer may be in the same loop. A flat
2204 * closure would then be unsafe, as the captured
2205 * variable could be assigned after the closure is
2206 * created. See bug 493232.
2208 if (afunbox
->inLoop
)
2212 * with and eval defeat lexical scoping; eval anywhere
2213 * in a variable's scope can assign to it. Both defeat
2214 * the flat closure optimization. The parser detects
2215 * these cases and flags the function heavyweight.
2217 if ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
)
2218 & TCF_FUN_HEAVYWEIGHT
) {
2223 * If afunbox's function is not a lambda, it will be
2224 * hoisted, so it could capture the undefined value
2225 * that by default initializes var/let/const
2226 * bindings. And if lexdep is a function that comes at
2227 * (meaning a function refers to its own name) or
2228 * strictly after afunbox, we also break to defeat the
2229 * flat closure optimization.
2231 JSFunction
*afun
= (JSFunction
*) afunbox
->object
;
2232 if (!(afun
->flags
& JSFUN_LAMBDA
)) {
2233 if (lexdep
->isBindingForm())
2235 if (lexdep
->pn_pos
>= afunbox
->node
->pn_pos
)
2239 if (!lexdep
->isInitialized())
2242 JSDefinition::Kind lexdepKind
= lexdep
->kind();
2243 if (lexdepKind
!= JSDefinition::CONST
) {
2244 if (lexdep
->isAssigned())
2248 * Any formal could be mutated behind our back via
2249 * the arguments object, so deoptimize if the outer
2250 * function uses arguments.
2252 * In a Function constructor call where the final
2253 * argument -- the body source for the function to
2254 * create -- contains a nested function definition
2255 * or expression, afunbox->parent will be null. The
2256 * body source might use |arguments| outside of any
2257 * nested functions it may contain, so we have to
2258 * check the tcflags parameter that was passed in
2259 * from JSCompiler::compileFunctionBody.
2261 if (lexdepKind
== JSDefinition::ARG
&&
2262 ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
) &
2263 TCF_FUN_USES_ARGUMENTS
)) {
2269 * Check quick-and-dirty dominance relation. Function
2270 * definitions dominate their uses thanks to hoisting.
2271 * Other binding forms hoist as undefined, of course,
2272 * so check forward-reference and blockid relations.
2274 if (lexdepKind
!= JSDefinition::FUNCTION
) {
2276 * Watch out for code such as
2280 * var jQuery = ... = function (...) {
2281 * return new jQuery.foo.bar(baz);
2286 * where the jQuery var is not reassigned, but of
2287 * course is not initialized at the time that the
2288 * would-be-flat closure containing the jQuery
2291 if (lexdep
->pn_pos
.end
>= afunbox
->node
->pn_pos
.end
)
2294 if (lexdep
->isTopLevel()
2295 ? !MinBlockId(afunbox
->node
, lexdep
->pn_blockid
)
2296 : !lexdep
->isBlockChild() ||
2297 !afunbox
->node
->isBlockChild() ||
2298 !OneBlockId(afunbox
->node
, lexdep
->pn_blockid
)) {
2307 FUN_METER(onlyfreevar
);
2308 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2311 * We made it all the way through the upvar loop, so it's
2312 * safe to optimize to a flat closure.
2315 FUN_SET_KIND(fun
, JSFUN_FLAT_CLOSURE
);
2316 switch (PN_OP(fn
)) {
2318 fn
->pn_op
= JSOP_DEFFUN_FC
;
2320 case JSOP_DEFLOCALFUN
:
2321 fn
->pn_op
= JSOP_DEFLOCALFUN_FC
;
2324 fn
->pn_op
= JSOP_LAMBDA_FC
;
2327 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2328 JS_ASSERT(PN_OP(fn
) == JSOP_NOP
);
2331 FUN_METER(badfunarg
);
2336 if (FUN_KIND(fun
) == JSFUN_INTERPRETED
) {
2337 if (pn
->pn_type
!= TOK_UPVARS
) {
2339 parent
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2341 JSAtomList
upvars(pn
->pn_names
);
2342 JS_ASSERT(upvars
.count
!= 0);
2344 JSAtomListIterator
iter(&upvars
);
2345 JSAtomListElement
*ale
;
2348 * One or more upvars cannot be safely snapshot into a flat
2349 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2350 * all upvars, and for each non-free upvar, ensure that its
2351 * containing function has been flagged as heavyweight.
2353 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2354 * generating any code for a tree of nested functions.
2356 while ((ale
= iter()) != NULL
) {
2357 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2359 if (!lexdep
->isFreeVar()) {
2360 JSFunctionBox
*afunbox
= funbox
->parent
;
2361 uintN lexdepLevel
= lexdep
->frameLevel();
2365 * NB: afunbox->level is the static level of
2366 * the definition or expression of the function
2367 * parsed into afunbox, not the static level of
2368 * its body. Therefore we must add 1 to match
2369 * lexdep's level to find the afunbox whose
2370 * body contains the lexdep definition.
2372 if (afunbox
->level
+ 1U == lexdepLevel
||
2373 (lexdepLevel
== 0 && lexdep
->isLet())) {
2374 afunbox
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2377 afunbox
= afunbox
->parent
;
2379 if (!afunbox
&& (tcflags
& TCF_IN_FUNCTION
))
2380 tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2386 funbox
= funbox
->siblings
;
2389 JS_ASSERT(funbox
->parent
== parent
);
2394 const char js_argument_str
[] = "argument";
2395 const char js_variable_str
[] = "variable";
2396 const char js_unknown_str
[] = "unknown";
2399 JSDefinition::kindString(Kind kind
)
2401 static const char *table
[] = {
2402 js_var_str
, js_const_str
, js_let_str
,
2403 js_function_str
, js_argument_str
, js_unknown_str
2406 JS_ASSERT(unsigned(kind
) <= unsigned(ARG
));
2410 static JSFunctionBox
*
2411 EnterFunction(JSParseNode
*fn
, JSTreeContext
*tc
, JSTreeContext
*funtc
,
2412 JSAtom
*funAtom
= NULL
, uintN lambda
= JSFUN_LAMBDA
)
2414 JSFunction
*fun
= tc
->compiler
->newFunction(tc
, funAtom
, lambda
);
2418 /* Create box for fun->object early to protect against last-ditch GC. */
2419 JSFunctionBox
*funbox
= tc
->compiler
->newFunctionBox(FUN_OBJECT(fun
), fn
, tc
);
2423 /* Initialize non-default members of funtc. */
2424 funtc
->flags
|= funbox
->tcflags
;
2425 funtc
->blockidGen
= tc
->blockidGen
;
2426 if (!GenerateBlockId(funtc
, funtc
->bodyid
))
2429 funtc
->funbox
= funbox
;
2431 if (!SetStaticLevel(funtc
, tc
->staticLevel
+ 1))
2438 LeaveFunction(JSParseNode
*fn
, JSTreeContext
*funtc
, JSTreeContext
*tc
,
2439 JSAtom
*funAtom
= NULL
, uintN lambda
= JSFUN_LAMBDA
)
2441 tc
->blockidGen
= funtc
->blockidGen
;
2443 fn
->pn_funbox
->tcflags
|= funtc
->flags
& (TCF_FUN_FLAGS
| TCF_COMPILE_N_GO
);
2445 fn
->pn_dflags
|= PND_INITIALIZED
;
2446 JS_ASSERT_IF(tc
->atTopLevel() && lambda
== 0 && funAtom
,
2447 fn
->pn_dflags
& PND_TOPLEVEL
);
2448 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
2449 fn
->pn_dflags
|= PND_BLOCKCHILD
;
2452 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2453 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2454 * params and body. We do this only if there are lexical dependencies not
2455 * satisfied by the function's declarations, to avoid penalizing functions
2456 * that use only their arguments and other local bindings.
2458 if (funtc
->lexdeps
.count
!= 0) {
2459 JSAtomListIterator
iter(&funtc
->lexdeps
);
2460 JSAtomListElement
*ale
;
2461 int foundCallee
= 0;
2463 while ((ale
= iter()) != NULL
) {
2464 JSAtom
*atom
= ALE_ATOM(ale
);
2465 JSDefinition
*dn
= ALE_DEFN(ale
);
2466 JS_ASSERT(dn
->isPlaceholder());
2468 if (atom
== funAtom
&& lambda
!= 0) {
2469 dn
->pn_op
= JSOP_CALLEE
;
2470 dn
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
->staticLevel
, CALLEE_UPVAR_SLOT
);
2471 dn
->pn_dflags
|= PND_BOUND
;
2474 * If this named function expression uses its own name other
2475 * than to call itself, flag this function specially.
2478 fn
->pn_funbox
->tcflags
|= TCF_FUN_USES_OWN_NAME
;
2483 if (!(fn
->pn_funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
) &&
2486 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2487 * any use of dn in funtc assigns. See NoteLValue for the easy
2488 * backward-reference case; this is the hard forward-reference
2489 * case where we pay a higher price.
2491 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
2492 if (pnu
->isAssigned() && pnu
->pn_blockid
>= funtc
->bodyid
) {
2493 fn
->pn_funbox
->tcflags
|= TCF_FUN_SETS_OUTER_NAME
;
2499 JSAtomListElement
*outer_ale
= tc
->decls
.lookup(atom
);
2501 outer_ale
= tc
->lexdeps
.lookup(atom
);
2504 * Insert dn's uses list at the front of outer_dn's list.
2506 * Without loss of generality or correctness, we allow a dn to
2507 * be in inner and outer lexdeps, since the purpose of lexdeps
2508 * is one-pass coordination of name use and definition across
2509 * functions, and if different dn's are used we'll merge lists
2510 * when leaving the inner function.
2512 * The dn == outer_dn case arises with generator expressions
2513 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2514 * case), and nowhere else, currently.
2516 JSDefinition
*outer_dn
= ALE_DEFN(outer_ale
);
2518 if (dn
!= outer_dn
) {
2519 JSParseNode
**pnup
= &dn
->dn_uses
;
2522 while ((pnu
= *pnup
) != NULL
) {
2523 pnu
->pn_lexdef
= outer_dn
;
2524 pnup
= &pnu
->pn_link
;
2528 * Make dn be a use that redirects to outer_dn, because we
2529 * can't replace dn with outer_dn in all the pn_namesets in
2530 * the AST where it may be. Instead we make it forward to
2531 * outer_dn. See JSDefinition::resolve.
2533 *pnup
= outer_dn
->dn_uses
;
2534 outer_dn
->dn_uses
= dn
;
2535 outer_dn
->pn_dflags
|= dn
->pn_dflags
& ~PND_PLACEHOLDER
;
2536 dn
->pn_defn
= false;
2538 dn
->pn_lexdef
= outer_dn
;
2541 /* Add an outer lexical dependency for ale's definition. */
2542 outer_ale
= tc
->lexdeps
.add(tc
->compiler
, atom
);
2545 ALE_SET_DEFN(outer_ale
, ALE_DEFN(ale
));
2549 if (funtc
->lexdeps
.count
- foundCallee
!= 0) {
2550 JSParseNode
*body
= fn
->pn_body
;
2552 fn
->pn_body
= NewParseNode(PN_NAMESET
, tc
);
2556 fn
->pn_body
->pn_type
= TOK_UPVARS
;
2557 fn
->pn_body
->pn_pos
= body
->pn_pos
;
2559 funtc
->lexdeps
.remove(tc
->compiler
, funAtom
);
2560 fn
->pn_body
->pn_names
= funtc
->lexdeps
;
2561 fn
->pn_body
->pn_tree
= body
;
2564 funtc
->lexdeps
.clear();
2570 static JSParseNode
*
2571 FunctionDef(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2575 JSParseNode
*pn
, *body
, *result
;
2578 JSAtomListElement
*ale
;
2579 #if JS_HAS_DESTRUCTURING
2580 JSParseNode
*item
, *list
= NULL
;
2581 bool destructuringArg
= false;
2582 JSAtom
*duplicatedArg
= NULL
;
2585 /* Make a TOK_FUNCTION node. */
2586 #if JS_HAS_GETTER_SETTER
2587 op
= CURRENT_TOKEN(ts
).t_op
;
2589 pn
= NewParseNode(PN_FUNC
, tc
);
2593 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2596 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2597 * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2599 * Also treat function sub-statements (non-lambda, non-top-level functions)
2600 * as escaping funargs, since we can't statically analyze their definitions
2603 bool topLevel
= tc
->atTopLevel();
2604 pn
->pn_dflags
= (lambda
|| !topLevel
) ? PND_FUNARG
: 0;
2606 /* Scan the optional function name into funAtom. */
2607 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
2608 tt
= js_GetToken(cx
, ts
);
2609 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
2610 if (tt
== TOK_NAME
) {
2611 funAtom
= CURRENT_TOKEN(ts
).t_atom
;
2613 if (lambda
== 0 && (cx
->options
& JSOPTION_ANONFUNFIX
)) {
2614 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2615 JSMSG_SYNTAX_ERROR
);
2623 * Record names for function statements in tc->decls so we know when to
2624 * avoid optimizing variable references that might name a function.
2626 if (lambda
== 0 && funAtom
) {
2627 ale
= tc
->decls
.lookup(funAtom
);
2629 JSDefinition
*dn
= ALE_DEFN(ale
);
2630 JSDefinition::Kind dn_kind
= dn
->kind();
2632 JS_ASSERT(!dn
->pn_used
);
2633 JS_ASSERT(dn
->pn_defn
);
2635 if (JS_HAS_STRICT_OPTION(cx
) || dn_kind
== JSDefinition::CONST
) {
2636 const char *name
= js_AtomToPrintableString(cx
, funAtom
);
2638 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2639 (dn_kind
!= JSDefinition::CONST
)
2640 ? JSREPORT_WARNING
| JSREPORT_STRICT
2642 JSMSG_REDECLARED_VAR
,
2643 JSDefinition::kindString(dn_kind
),
2650 ALE_SET_DEFN(ale
, pn
);
2652 pn
->dn_uses
= dn
; /* dn->dn_uses is now pn_link */
2654 if (!MakeDefIntoUse(dn
, pn
, funAtom
, tc
))
2657 } else if (topLevel
) {
2659 * If this function was used before it was defined, claim the
2660 * pre-created definition node for this function that PrimaryExpr
2661 * put in tc->lexdeps on first forward reference, and recycle pn.
2665 ale
= tc
->lexdeps
.rawLookup(funAtom
, hep
);
2667 JSDefinition
*fn
= ALE_DEFN(ale
);
2669 JS_ASSERT(fn
->pn_defn
);
2670 fn
->pn_type
= TOK_FUNCTION
;
2671 fn
->pn_arity
= PN_FUNC
;
2672 fn
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2674 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2676 tc
->lexdeps
.rawRemove(tc
->compiler
, ale
, hep
);
2677 RecycleTree(pn
, tc
);
2681 if (!Define(pn
, funAtom
, tc
))
2686 * A function nested at top level inside another's body needs only a
2687 * local variable to bind its name to its value, and not an activation
2688 * object property (it might also need the activation property, if the
2689 * outer function contains with statements, e.g., but the stack slot
2690 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2691 * JSOP_GETLOCAL bytecode).
2694 pn
->pn_dflags
|= PND_TOPLEVEL
;
2696 if (tc
->flags
& TCF_IN_FUNCTION
) {
2697 JSLocalKind localKind
;
2701 * Define a local in the outer function so that BindNameToSlot
2702 * can properly optimize accesses. Note that we need a local
2703 * variable, not an argument, for the function statement. Thus
2704 * we add a variable even if a parameter with the given name
2707 localKind
= js_LookupLocal(cx
, tc
->fun
, funAtom
, &index
);
2708 switch (localKind
) {
2711 index
= tc
->fun
->u
.i
.nvars
;
2712 if (!js_AddLocal(cx
, tc
->fun
, funAtom
, JSLOCAL_VAR
))
2717 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
2718 pn
->pn_dflags
|= PND_BOUND
;
2727 /* Initialize early for possible flags mutation via DestructuringExpr. */
2728 JSTreeContext
funtc(tc
->compiler
);
2730 JSFunctionBox
*funbox
= EnterFunction(pn
, tc
, &funtc
, funAtom
, lambda
);
2734 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
2736 #if JS_HAS_GETTER_SETTER
2738 fun
->flags
|= (op
== JSOP_GETTER
) ? JSPROP_GETTER
: JSPROP_SETTER
;
2741 /* Now parse formal argument list and compute fun->nargs. */
2742 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_FORMAL
);
2743 if (!js_MatchToken(cx
, ts
, TOK_RP
)) {
2745 tt
= js_GetToken(cx
, ts
);
2747 #if JS_HAS_DESTRUCTURING
2752 JSParseNode
*lhs
, *rhs
;
2755 /* See comment below in the TOK_NAME case. */
2757 goto report_dup_and_destructuring
;
2758 destructuringArg
= true;
2761 * A destructuring formal parameter turns into one or more
2762 * local variables initialized from properties of a single
2763 * anonymous positional parameter, so here we must tweak our
2764 * binder and its data.
2767 data
.op
= JSOP_DEFVAR
;
2768 data
.binder
= BindDestructuringArg
;
2769 lhs
= DestructuringExpr(cx
, &data
, &funtc
, tt
);
2774 * Adjust fun->nargs to count the single anonymous positional
2775 * parameter that is to be destructured.
2778 if (!js_AddLocal(cx
, fun
, NULL
, JSLOCAL_ARG
))
2782 * Synthesize a destructuring assignment from the single
2783 * anonymous positional parameter into the destructuring
2784 * left-hand-side expression and accumulate it in list.
2786 rhs
= NewNameNode(cx
, cx
->runtime
->atomState
.emptyAtom
, &funtc
);
2789 rhs
->pn_type
= TOK_NAME
;
2790 rhs
->pn_op
= JSOP_GETARG
;
2791 rhs
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
.staticLevel
, slot
);
2792 rhs
->pn_dflags
|= PND_BOUND
;
2794 item
= NewBinary(TOK_ASSIGN
, JSOP_NOP
, lhs
, rhs
, &funtc
);
2798 list
= NewParseNode(PN_LIST
, &funtc
);
2801 list
->pn_type
= TOK_COMMA
;
2807 #endif /* JS_HAS_DESTRUCTURING */
2811 JSAtom
*atom
= CURRENT_TOKEN(ts
).t_atom
;
2812 if (!DefineArg(pn
, atom
, fun
->nargs
, &funtc
))
2814 #ifdef JS_HAS_DESTRUCTURING
2816 * ECMA-262 requires us to support duplicate parameter names, but if the
2817 * parameter list includes destructuring, we consider the code to have
2818 * opted in to higher standards, and forbid duplicates. We may see a
2819 * destructuring parameter later, so always note duplicates now.
2821 * Duplicates are warned about (strict option) or cause errors (strict
2822 * mode code), but we do those tests in one place below, after having
2825 if (js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2826 duplicatedArg
= atom
;
2827 if (destructuringArg
)
2828 goto report_dup_and_destructuring
;
2831 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
2837 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2838 JSMSG_MISSING_FORMAL
);
2843 #if JS_HAS_DESTRUCTURING
2844 report_dup_and_destructuring
:
2845 JSDefinition
*dn
= ALE_DEFN(funtc
.decls
.lookup(duplicatedArg
));
2846 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), dn
,
2848 JSMSG_DESTRUCT_DUP_ARG
);
2852 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
2854 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FORMAL
);
2857 #if JS_HAS_EXPR_CLOSURES
2858 ts
->flags
|= TSF_OPERAND
;
2859 tt
= js_GetToken(cx
, ts
);
2860 ts
->flags
&= ~TSF_OPERAND
;
2863 fun
->flags
|= JSFUN_EXPR_CLOSURE
;
2866 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_BODY
);
2869 body
= FunctionBody(cx
, ts
, &funtc
);
2873 if (!CheckStrictBinding(cx
, &funtc
, funAtom
, pn
))
2876 if (!CheckStrictFormals(cx
, &funtc
, fun
, pn
))
2879 #if JS_HAS_EXPR_CLOSURES
2881 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2882 else if (lambda
== 0 && !MatchOrInsertSemicolon(cx
, ts
))
2885 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2887 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2889 #if JS_HAS_DESTRUCTURING
2891 * If there were destructuring formal parameters, prepend the initializing
2892 * comma expression that we synthesized to body. If the body is a lexical
2893 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2894 * parameter destructuring code without bracing the decompilation of the
2895 * function body's lexical scope.
2898 if (body
->pn_arity
!= PN_LIST
) {
2901 block
= NewParseNode(PN_LIST
, tc
);
2904 block
->pn_type
= TOK_SEQ
;
2905 block
->pn_pos
= body
->pn_pos
;
2906 block
->initList(body
);
2911 item
= NewParseNode(PN_UNARY
, tc
);
2915 item
->pn_type
= TOK_SEMI
;
2916 item
->pn_pos
.begin
= item
->pn_pos
.end
= body
->pn_pos
.begin
;
2917 item
->pn_kid
= list
;
2918 item
->pn_next
= body
->pn_head
;
2919 body
->pn_head
= item
;
2920 if (body
->pn_tail
== &body
->pn_head
)
2921 body
->pn_tail
= &item
->pn_next
;
2923 body
->pn_xflags
|= PNX_DESTRUCT
;
2928 * If we collected flags that indicate nested heavyweight functions, or
2929 * this function contains heavyweight-making statements (with statement,
2930 * visible eval call, or assignment to 'arguments'), flag the function as
2931 * heavyweight (requiring a call object per invocation).
2933 if (funtc
.flags
& TCF_FUN_HEAVYWEIGHT
) {
2934 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
2935 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2938 * If this function is a named statement function not at top-level
2939 * (i.e. not a top-level function definiton or expression), then our
2940 * enclosing function, if any, must be heavyweight.
2942 if (!topLevel
&& lambda
== 0 && funAtom
)
2943 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2949 * ECMA ed. 3 standard: function expression, possibly anonymous.
2952 } else if (!funAtom
) {
2954 * If this anonymous function definition is *not* embedded within a
2955 * larger expression, we treat it as an expression statement, not as
2956 * a function declaration -- and not as a syntax error (as ECMA-262
2957 * Edition 3 would have it). Backward compatibility must trump all,
2958 * unless JSOPTION_ANONFUNFIX is set.
2960 result
= NewParseNode(PN_UNARY
, tc
);
2963 result
->pn_type
= TOK_SEMI
;
2964 result
->pn_pos
= pn
->pn_pos
;
2965 result
->pn_kid
= pn
;
2967 } else if (!topLevel
) {
2969 * ECMA ed. 3 extension: a function expression statement not at the
2970 * top level, e.g., in a compound statement such as the "then" part
2971 * of an "if" statement, binds a closure only if control reaches that
2979 funbox
->kids
= funtc
.functionList
;
2981 pn
->pn_funbox
= funbox
;
2984 pn
->pn_body
->append(body
);
2985 pn
->pn_body
->pn_pos
= body
->pn_pos
;
2990 pn
->pn_blockid
= tc
->blockid();
2992 if (!LeaveFunction(pn
, &funtc
, tc
, funAtom
, lambda
))
2995 /* If the surrounding function is not strict code, reset the lexer. */
2996 if (!(tc
->flags
& TCF_STRICT_MODE_CODE
))
2997 ts
->flags
&= ~TSF_STRICT_MODE_CODE
;
3002 static JSParseNode
*
3003 FunctionStmt(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3005 return FunctionDef(cx
, ts
, tc
, 0);
3008 static JSParseNode
*
3009 FunctionExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3011 return FunctionDef(cx
, ts
, tc
, JSFUN_LAMBDA
);
3015 * Recognize Directive Prologue members and directives. Assuming pn
3016 * is a candidate for membership in a directive prologue, return
3017 * true if it is in fact a member. Recognize directives and set
3018 * tc's flags accordingly.
3020 * Note that the following is a strict mode function:
3023 * "blah" // inserted semi colon
3029 * That is, a statement can be a Directive Prologue member, even
3030 * if it can't possibly be a directive, now or in the future.
3033 RecognizeDirectivePrologue(JSContext
*cx
, JSTokenStream
*ts
,
3034 JSTreeContext
*tc
, JSParseNode
*pn
)
3036 if (!pn
->isDirectivePrologueMember())
3038 if (pn
->isDirective()) {
3039 JSAtom
*directive
= pn
->pn_kid
->pn_atom
;
3040 if (directive
== cx
->runtime
->atomState
.useStrictAtom
) {
3041 tc
->flags
|= TCF_STRICT_MODE_CODE
;
3042 ts
->flags
|= TSF_STRICT_MODE_CODE
;
3049 * Parse the statements in a block, creating a TOK_LC node that lists the
3050 * statements' trees. If called from block-parsing code, the caller must
3051 * match { before and } after.
3053 static JSParseNode
*
3054 Statements(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3056 JSParseNode
*pn
, *pn2
, *saveBlock
;
3058 bool inDirectivePrologue
= tc
->atTopLevel();
3060 JS_CHECK_RECURSION(cx
, return NULL
);
3062 pn
= NewParseNode(PN_LIST
, tc
);
3065 pn
->pn_type
= TOK_LC
;
3067 pn
->pn_blockid
= tc
->blockid();
3068 saveBlock
= tc
->blockNode
;
3072 ts
->flags
|= TSF_OPERAND
;
3073 tt
= js_PeekToken(cx
, ts
);
3074 ts
->flags
&= ~TSF_OPERAND
;
3075 if (tt
<= TOK_EOF
|| tt
== TOK_RC
) {
3076 if (tt
== TOK_ERROR
) {
3077 if (ts
->flags
& TSF_EOF
)
3078 ts
->flags
|= TSF_UNEXPECTED_EOF
;
3083 pn2
= Statement(cx
, ts
, tc
);
3085 if (ts
->flags
& TSF_EOF
)
3086 ts
->flags
|= TSF_UNEXPECTED_EOF
;
3090 if (inDirectivePrologue
) {
3091 if (RecognizeDirectivePrologue(cx
, ts
, tc
, pn2
)) {
3092 /* A Directive Prologue member is dead code. Omit it from the statement list. */
3093 RecycleTree(pn2
, tc
);
3096 inDirectivePrologue
= false;
3100 if (pn2
->pn_type
== TOK_FUNCTION
) {
3102 * PNX_FUNCDEFS notifies the emitter that the block contains top-
3103 * level function definitions that should be processed before the
3106 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
3107 * is relevant only for function definitions not at top-level,
3108 * which we call function statements.
3110 if (tc
->atTopLevel())
3111 pn
->pn_xflags
|= PNX_FUNCDEFS
;
3113 tc
->flags
|= TCF_HAS_FUNCTION_STMT
;
3119 * Handle the case where there was a let declaration under this block. If
3120 * it replaced tc->blockNode with a new block node then we must refresh pn
3121 * and then restore tc->blockNode.
3123 if (tc
->blockNode
!= pn
)
3125 tc
->blockNode
= saveBlock
;
3127 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
3131 static JSParseNode
*
3132 Condition(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3136 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
3137 pn
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
3140 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
3142 /* Check for (a = b) and warn about possible (a == b) mistype. */
3143 if (pn
->pn_type
== TOK_ASSIGN
&&
3144 pn
->pn_op
== JSOP_NOP
&&
3146 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
3147 JSREPORT_WARNING
| JSREPORT_STRICT
,
3148 JSMSG_EQUAL_AS_ASSIGN
,
3156 MatchLabel(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
)
3161 tt
= js_PeekTokenSameLine(cx
, ts
);
3162 if (tt
== TOK_ERROR
)
3164 if (tt
== TOK_NAME
) {
3165 (void) js_GetToken(cx
, ts
);
3166 label
= CURRENT_TOKEN(ts
).t_atom
;
3170 pn
->pn_atom
= label
;
3175 BindLet(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3179 JSAtomListElement
*ale
;
3183 * Top-level 'let' is the same as 'var' currently -- this may change in a
3184 * successor standard to ES3.1 that specifies 'let'.
3186 JS_ASSERT(!tc
->atTopLevel());
3189 if (!CheckStrictBinding(cx
, tc
, atom
, pn
))
3192 blockObj
= tc
->blockChain
;
3193 ale
= tc
->decls
.lookup(atom
);
3194 if (ale
&& ALE_DEFN(ale
)->pn_blockid
== tc
->blockid()) {
3195 const char *name
= js_AtomToPrintableString(cx
, atom
);
3197 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3198 JSREPORT_ERROR
, JSMSG_REDECLARED_VAR
,
3199 (ale
&& ALE_DEFN(ale
)->isConst())
3207 n
= OBJ_BLOCK_COUNT(cx
, blockObj
);
3208 if (n
== JS_BIT(16)) {
3209 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3210 JSREPORT_ERROR
, data
->let
.overflow
);
3215 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3216 * This is balanced by PopStatement, defined immediately below.
3218 if (!Define(pn
, atom
, tc
, true))
3222 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3223 * upvar cookie whose skip tells the current static level. The emitter will
3224 * adjust the node's slot based on its stack depth model -- and, for global
3225 * and eval code, JSCompiler::compileScript will adjust the slot again to
3226 * include script->nfixed.
3228 pn
->pn_op
= JSOP_GETLOCAL
;
3229 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, n
);
3230 pn
->pn_dflags
|= PND_LET
| PND_BOUND
;
3233 * Define the let binding's property before storing pn in a reserved slot,
3234 * since block_reserveSlots depends on OBJ_SCOPE(blockObj)->entryCount.
3236 if (!js_DefineBlockVariable(cx
, blockObj
, ATOM_TO_JSID(atom
), n
))
3240 * Store pn temporarily in what would be reserved slots in a cloned block
3241 * object (once the prototype's final population is known, after all 'let'
3242 * bindings for this block have been parsed). We will free these reserved
3243 * slots in jsemit.cpp:EmitEnterBlock.
3245 uintN slot
= JSSLOT_FREE(&js_BlockClass
) + n
;
3246 if (slot
>= STOBJ_NSLOTS(blockObj
) &&
3247 !js_GrowSlots(cx
, blockObj
, slot
+ 1)) {
3250 OBJ_SCOPE(blockObj
)->freeslot
= slot
+ 1;
3251 STOBJ_SET_SLOT(blockObj
, slot
, PRIVATE_TO_JSVAL(pn
));
3256 PopStatement(JSTreeContext
*tc
)
3258 JSStmtInfo
*stmt
= tc
->topStmt
;
3260 if (stmt
->flags
& SIF_SCOPE
) {
3261 JSObject
*obj
= stmt
->blockObj
;
3262 JSScope
*scope
= OBJ_SCOPE(obj
);
3263 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj
));
3265 for (JSScopeProperty
*sprop
= scope
->lastProperty(); sprop
; sprop
= sprop
->parent
) {
3266 JSAtom
*atom
= JSID_TO_ATOM(sprop
->id
);
3268 /* Beware the empty destructuring dummy. */
3269 if (atom
== tc
->compiler
->context
->runtime
->atomState
.emptyAtom
)
3271 tc
->decls
.remove(tc
->compiler
, atom
);
3275 * The block scope will not be modified again. It may be shared. Clear
3276 * scope->object to make scope->owned() false.
3278 scope
->object
= NULL
;
3280 js_PopStatement(tc
);
3284 OuterLet(JSTreeContext
*tc
, JSStmtInfo
*stmt
, JSAtom
*atom
)
3286 while (stmt
->downScope
) {
3287 stmt
= js_LexicalLookup(tc
, atom
, NULL
, stmt
->downScope
);
3290 if (stmt
->type
== STMT_BLOCK
)
3297 BindVarOrConst(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3299 JSParseNode
*pn
= data
->pn
;
3301 if (!CheckStrictBinding(cx
, tc
, atom
, pn
))
3304 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
3306 if (stmt
&& stmt
->type
== STMT_WITH
) {
3307 pn
->pn_op
= JSOP_NAME
;
3308 data
->fresh
= false;
3312 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
3316 JSDefinition
*dn
= ale
? ALE_DEFN(ale
) : NULL
;
3317 JSDefinition::Kind dn_kind
= dn
? dn
->kind() : JSDefinition::VAR
;
3320 if (dn_kind
== JSDefinition::ARG
) {
3321 name
= js_AtomToPrintableString(cx
, atom
);
3325 if (op
== JSOP_DEFCONST
) {
3326 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3327 JSREPORT_ERROR
, JSMSG_REDECLARED_PARAM
,
3331 if (!js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3332 JSREPORT_WARNING
| JSREPORT_STRICT
,
3333 JSMSG_VAR_HIDES_ARG
, name
)) {
3337 bool error
= (op
== JSOP_DEFCONST
||
3338 dn_kind
== JSDefinition::CONST
||
3339 (dn_kind
== JSDefinition::LET
&&
3340 (stmt
->type
!= STMT_CATCH
|| OuterLet(tc
, stmt
, atom
))));
3342 if (JS_HAS_STRICT_OPTION(cx
)
3343 ? op
!= JSOP_DEFVAR
|| dn_kind
!= JSDefinition::VAR
3345 name
= js_AtomToPrintableString(cx
, atom
);
3347 !js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3349 ? JSREPORT_WARNING
| JSREPORT_STRICT
3351 JSMSG_REDECLARED_VAR
,
3352 JSDefinition::kindString(dn_kind
),
3361 if (!Define(pn
, atom
, tc
))
3365 * A var declaration never recreates an existing binding, it restates
3366 * it and possibly reinitializes its value. Beware that if pn becomes a
3367 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3368 * const (typically a const would ;-), then pn must be rewritten into a
3369 * TOK_ASSIGN node. See Variables, further below.
3371 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3372 * There the x definition is hoisted but the x = 2 assignment mutates
3373 * the block-local binding of x.
3375 JSDefinition
*dn
= ALE_DEFN(ale
);
3377 data
->fresh
= false;
3380 /* Make pnu be a fresh name node that uses dn. */
3381 JSParseNode
*pnu
= pn
;
3384 pnu
= NewNameNode(cx
, atom
, tc
);
3389 LinkUseToDef(pnu
, dn
, tc
);
3390 pnu
->pn_op
= JSOP_NAME
;
3393 while (dn
->kind() == JSDefinition::LET
) {
3395 ale
= ALE_NEXT(ale
);
3396 } while (ale
&& ALE_ATOM(ale
) != atom
);
3403 JS_ASSERT_IF(data
->op
== JSOP_DEFCONST
,
3404 dn
->kind() == JSDefinition::CONST
);
3409 * A var or const that is shadowed by one or more let bindings of the
3410 * same name, but that has not been declared until this point, must be
3411 * hoisted above the let bindings.
3416 ale
= tc
->lexdeps
.rawLookup(atom
, hep
);
3419 tc
->lexdeps
.rawRemove(tc
->compiler
, ale
, hep
);
3421 JSParseNode
*pn2
= NewNameNode(cx
, atom
, tc
);
3425 /* The token stream may be past the location for pn. */
3426 pn2
->pn_type
= TOK_NAME
;
3427 pn2
->pn_pos
= pn
->pn_pos
;
3430 pn
->pn_op
= JSOP_NAME
;
3433 ale
= tc
->decls
.add(tc
->compiler
, atom
, JSAtomList::HOIST
);
3436 ALE_SET_DEFN(ale
, pn
);
3438 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
3441 if (data
->op
== JSOP_DEFCONST
)
3442 pn
->pn_dflags
|= PND_CONST
;
3444 if (!(tc
->flags
& TCF_IN_FUNCTION
)) {
3446 * If we are generating global or eval-called-from-global code, bind a
3447 * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3448 * up global variable access by memoizing name-to-slot mappings in the
3449 * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3450 * can't be done due to a pre-existing property of the same name as the
3451 * var or const but incompatible attributes/getter/setter/etc, these
3452 * ops devolve to JSOP_NAME, etc.
3454 * For now, don't try to lookup eval frame variables at compile time.
3455 * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3456 * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3458 pn
->pn_op
= JSOP_NAME
;
3459 if ((tc
->flags
& TCF_COMPILING
) && !tc
->compiler
->callerFrame
) {
3460 JSCodeGenerator
*cg
= (JSCodeGenerator
*) tc
;
3462 /* Index atom so we can map fast global number to name. */
3463 ale
= cg
->atomList
.add(tc
->compiler
, atom
);
3467 /* Defend against cg->ngvars 16-bit overflow. */
3468 uintN slot
= ALE_INDEX(ale
);
3469 if ((slot
+ 1) >> 16)
3472 if ((uint16
)(slot
+ 1) > cg
->ngvars
)
3473 cg
->ngvars
= (uint16
)(slot
+ 1);
3475 pn
->pn_op
= JSOP_GETGVAR
;
3476 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, slot
);
3477 pn
->pn_dflags
|= PND_BOUND
| PND_GVAR
;
3482 if (atom
== cx
->runtime
->atomState
.argumentsAtom
) {
3483 pn
->pn_op
= JSOP_ARGUMENTS
;
3484 pn
->pn_dflags
|= PND_BOUND
;
3488 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
3489 if (localKind
== JSLOCAL_NONE
) {
3491 * Property not found in current variable scope: we have not seen this
3492 * variable before. Define a new local variable by adding a property to
3493 * the function's scope and allocating one slot in the function's vars
3494 * frame. Any locals declared in a with statement body are handled at
3495 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3496 * and heavyweight-function-local vars.
3498 localKind
= (data
->op
== JSOP_DEFCONST
) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
3500 uintN index
= tc
->fun
->u
.i
.nvars
;
3501 if (!BindLocalVariable(cx
, tc
->fun
, atom
, localKind
, false))
3503 pn
->pn_op
= JSOP_GETLOCAL
;
3504 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
3505 pn
->pn_dflags
|= PND_BOUND
;
3509 if (localKind
== JSLOCAL_ARG
) {
3510 /* We checked errors and strict warnings earlier -- see above. */
3511 JS_ASSERT(ale
&& ALE_DEFN(ale
)->kind() == JSDefinition::ARG
);
3513 /* Not an argument, must be a redeclared local var. */
3514 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
3516 pn
->pn_op
= JSOP_NAME
;
3521 MakeSetCall(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN msg
)
3525 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
3526 JS_ASSERT(pn
->pn_op
== JSOP_CALL
|| pn
->pn_op
== JSOP_EVAL
|| pn
->pn_op
== JSOP_APPLY
);
3528 if (pn2
->pn_type
== TOK_FUNCTION
&& (pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
)) {
3529 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
, JSREPORT_ERROR
, msg
);
3532 pn
->pn_op
= JSOP_SETCALL
;
3537 NoteLValue(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN dflag
= PND_ASSIGNED
)
3540 JSDefinition
*dn
= pn
->pn_lexdef
;
3543 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3544 * occur as direct kids of the same block with no forward refs to x.
3546 if (!(dn
->pn_dflags
& (PND_INITIALIZED
| PND_CONST
| PND_PLACEHOLDER
)) &&
3547 dn
->isBlockChild() &&
3548 pn
->isBlockChild() &&
3549 dn
->pn_blockid
== pn
->pn_blockid
&&
3550 dn
->pn_pos
.end
<= pn
->pn_pos
.begin
&&
3551 dn
->dn_uses
== pn
) {
3552 dflag
= PND_INITIALIZED
;
3555 dn
->pn_dflags
|= dflag
;
3557 if (dn
->frameLevel() != tc
->staticLevel
) {
3559 * The above condition takes advantage of the all-ones nature of
3560 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3561 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3563 JS_ASSERT_IF(dn
->pn_cookie
!= FREE_UPVAR_COOKIE
,
3564 dn
->frameLevel() < tc
->staticLevel
);
3565 tc
->flags
|= TCF_FUN_SETS_OUTER_NAME
;
3569 pn
->pn_dflags
|= dflag
;
3571 if (pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
3572 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3575 #if JS_HAS_DESTRUCTURING
3578 BindDestructuringVar(JSContext
*cx
, BindData
*data
, JSParseNode
*pn
,
3584 * Destructuring is a form of assignment, so just as for an initialized
3585 * simple variable, we must check for assignment to 'arguments' and flag
3586 * the enclosing function (if any) as heavyweight.
3588 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
3590 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
3591 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3594 if (!data
->binder(cx
, data
, atom
, tc
))
3598 * Select the appropriate name-setting opcode, respecting eager selection
3599 * done by the data->binder function.
3601 if (pn
->pn_dflags
& PND_BOUND
) {
3602 pn
->pn_op
= (pn
->pn_op
== JSOP_ARGUMENTS
)
3604 : (pn
->pn_dflags
& PND_GVAR
)
3608 pn
->pn_op
= (data
->op
== JSOP_DEFCONST
)
3613 if (data
->op
== JSOP_DEFCONST
)
3614 pn
->pn_dflags
|= PND_CONST
;
3616 NoteLValue(cx
, pn
, tc
, PND_INITIALIZED
);
3621 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3622 * LHS expression except a destructuring initialiser, and R is on the stack.
3623 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3624 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3625 * then push its property name QN. At this point the stack looks like
3627 * [... R, R[P], QB, QN]
3629 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3630 * its operands with left-hand side above right-hand side:
3632 * [rval, lval, xval]
3634 * and pops all three values, setting lval[xval] = rval. But we cannot select
3635 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3636 * which can be optimized further. So we select JSOP_SETNAME.
3639 BindDestructuringLHS(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
3641 switch (pn
->pn_type
) {
3643 NoteLValue(cx
, pn
, tc
);
3648 pn
->pn_op
= JSOP_SETNAME
;
3652 if (!MakeSetCall(cx
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
3656 #if JS_HAS_XML_SUPPORT
3658 if (pn
->pn_op
== JSOP_XMLNAME
) {
3659 pn
->pn_op
= JSOP_BINDXMLNAME
;
3666 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3667 JSREPORT_ERROR
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
3674 typedef struct FindPropValData
{
3675 uint32 numvars
; /* # of destructuring vars in left side */
3676 uint32 maxstep
; /* max # of steps searching right side */
3677 JSDHashTable table
; /* hash table for O(1) right side search */
3680 typedef struct FindPropValEntry
{
3681 JSDHashEntryHdr hdr
;
3686 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3687 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3688 ((pnkey)->pn_type == TOK_NUMBER || \
3689 (pnkey)->pn_type == TOK_STRING || \
3690 (pnkey)->pn_type == TOK_NAME)) || \
3691 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3693 static JSDHashNumber
3694 HashFindPropValKey(JSDHashTable
*table
, const void *key
)
3696 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3698 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3699 return (pnkey
->pn_type
== TOK_NUMBER
)
3700 ? (JSDHashNumber
) JS_HASH_DOUBLE(pnkey
->pn_dval
)
3701 : ATOM_HASH(pnkey
->pn_atom
);
3705 MatchFindPropValEntry(JSDHashTable
*table
,
3706 const JSDHashEntryHdr
*entry
,
3709 const FindPropValEntry
*fpve
= (const FindPropValEntry
*)entry
;
3710 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3712 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3713 return pnkey
->pn_type
== fpve
->pnkey
->pn_type
&&
3714 ((pnkey
->pn_type
== TOK_NUMBER
)
3715 ? pnkey
->pn_dval
== fpve
->pnkey
->pn_dval
3716 : pnkey
->pn_atom
== fpve
->pnkey
->pn_atom
);
3719 static const JSDHashTableOps FindPropValOps
= {
3723 MatchFindPropValEntry
,
3724 JS_DHashMoveEntryStub
,
3725 JS_DHashClearEntryStub
,
3726 JS_DHashFinalizeStub
,
3730 #define STEP_HASH_THRESHOLD 10
3731 #define BIG_DESTRUCTURING 5
3732 #define BIG_OBJECT_INIT 20
3734 static JSParseNode
*
3735 FindPropertyValue(JSParseNode
*pn
, JSParseNode
*pnid
, FindPropValData
*data
)
3737 FindPropValEntry
*entry
;
3738 JSParseNode
*pnhit
, *pnhead
, *pnprop
, *pnkey
;
3741 /* If we have a hash table, use it as the sole source of truth. */
3742 if (data
->table
.ops
) {
3743 entry
= (FindPropValEntry
*)
3744 JS_DHashTableOperate(&data
->table
, pnid
, JS_DHASH_LOOKUP
);
3745 return JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
) ? entry
->pnval
: NULL
;
3748 /* If pn is not an object initialiser node, we can't do anything here. */
3749 if (pn
->pn_type
!= TOK_RC
)
3753 * We must search all the way through pn's list, to handle the case of an
3754 * id duplicated for two or more property initialisers.
3758 ASSERT_VALID_PROPERTY_KEY(pnid
);
3759 pnhead
= pn
->pn_head
;
3760 if (pnid
->pn_type
== TOK_NUMBER
) {
3761 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3762 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3763 if (pnprop
->pn_op
== JSOP_NOP
) {
3764 pnkey
= pnprop
->pn_left
;
3765 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3766 if (pnkey
->pn_type
== TOK_NUMBER
&&
3767 pnkey
->pn_dval
== pnid
->pn_dval
) {
3774 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3775 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3776 if (pnprop
->pn_op
== JSOP_NOP
) {
3777 pnkey
= pnprop
->pn_left
;
3778 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3779 if (pnkey
->pn_type
== pnid
->pn_type
&&
3780 pnkey
->pn_atom
== pnid
->pn_atom
) {
3790 /* Hit via full search -- see whether it's time to create the hash table. */
3791 JS_ASSERT(!data
->table
.ops
);
3792 if (step
> data
->maxstep
) {
3793 data
->maxstep
= step
;
3794 if (step
>= STEP_HASH_THRESHOLD
&&
3795 data
->numvars
>= BIG_DESTRUCTURING
&&
3796 pn
->pn_count
>= BIG_OBJECT_INIT
&&
3797 JS_DHashTableInit(&data
->table
, &FindPropValOps
, pn
,
3798 sizeof(FindPropValEntry
),
3799 JS_DHASH_DEFAULT_CAPACITY(pn
->pn_count
)))
3801 for (pn
= pnhead
; pn
; pn
= pn
->pn_next
) {
3802 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3803 ASSERT_VALID_PROPERTY_KEY(pn
->pn_left
);
3804 entry
= (FindPropValEntry
*)
3805 JS_DHashTableOperate(&data
->table
, pn
->pn_left
,
3807 entry
->pnval
= pn
->pn_right
;
3811 return pnhit
->pn_right
;
3815 * Destructuring patterns can appear in two kinds of contexts:
3817 * - assignment-like: assignment expressions and |for| loop heads. In
3818 * these cases, the patterns' property value positions can be
3819 * arbitrary lvalue expressions; the destructuring is just a fancy
3822 * - declaration-like: |var| and |let| declarations, functions' formal
3823 * parameter lists, |catch| clauses, and comprehension tails. In
3824 * these cases, the patterns' property value positions must be
3825 * simple names; the destructuring defines them as new variables.
3827 * In both cases, other code parses the pattern as an arbitrary
3828 * PrimaryExpr, and then, here in CheckDestructuring, verify that the
3829 * tree is a valid destructuring expression.
3831 * In assignment-like contexts, we parse the pattern with the
3832 * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
3833 * pattern are parsed normally. PrimaryExpr links variable references
3834 * into the appropriate use chains; creates placeholder definitions;
3835 * and so on. CheckDestructuring is called with |data| NULL (since we
3836 * won't be binding any new names), and we specialize lvalues as
3837 * appropriate. If right is NULL, we just check for well-formed lvalues.
3839 * In declaration-like contexts, the normal variable reference
3840 * processing would just be an obstruction, because we're going to
3841 * define the names that appear in the property value positions as new
3842 * variables anyway. In this case, we parse the pattern with
3843 * TCF_DECL_DESTRUCTURING set, which directs PrimaryExpr to leave
3844 * whatever name nodes it creates unconnected. Then, here in
3845 * CheckDestructuring, we require the pattern's property value
3846 * positions to be simple names, and define them as appropriate to the
3847 * context. For these calls, |data| points to the right sort of
3850 * See also UndominateInitializers, immediately below. If you change
3851 * either of these functions, you might have to change the other to
3855 CheckDestructuring(JSContext
*cx
, BindData
*data
,
3856 JSParseNode
*left
, JSParseNode
*right
,
3860 FindPropValData fpvd
;
3861 JSParseNode
*lhs
, *rhs
, *pn
, *pn2
;
3863 if (left
->pn_type
== TOK_ARRAYCOMP
) {
3864 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), left
,
3865 JSREPORT_ERROR
, JSMSG_ARRAY_COMP_LEFTSIDE
);
3869 #if JS_HAS_DESTRUCTURING_SHORTHAND
3870 if (right
&& right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
3871 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), right
,
3872 JSREPORT_ERROR
, JSMSG_BAD_OBJECT_INIT
);
3877 fpvd
.table
.ops
= NULL
;
3878 lhs
= left
->pn_head
;
3879 if (left
->pn_type
== TOK_RB
) {
3880 rhs
= (right
&& right
->pn_type
== left
->pn_type
)
3885 pn
= lhs
, pn2
= rhs
;
3887 /* Nullary comma is an elision; binary comma is an expression.*/
3888 if (pn
->pn_type
!= TOK_COMMA
|| pn
->pn_arity
!= PN_NULLARY
) {
3889 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3890 ok
= CheckDestructuring(cx
, data
, pn
, pn2
, tc
);
3893 if (pn
->pn_type
!= TOK_NAME
)
3896 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3898 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3910 JS_ASSERT(left
->pn_type
== TOK_RC
);
3911 fpvd
.numvars
= left
->pn_count
;
3916 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
3919 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3921 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
3922 ok
= CheckDestructuring(cx
, data
, pn
, rhs
, tc
);
3924 if (pn
->pn_type
!= TOK_NAME
)
3927 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3929 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3939 * The catch/finally handler implementation in the interpreter assumes
3940 * that any operation that introduces a new scope (like a "let" or "with"
3941 * block) increases the stack depth. This way, it is possible to restore
3942 * the scope chain based on stack depth of the handler alone. "let" with
3943 * an empty destructuring pattern like in
3947 * would violate this assumption as the there would be no let locals to
3948 * store on the stack. To satisfy it we add an empty property to such
3949 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3950 * slots, would be always positive.
3952 * Note that we add such a property even if the block has locals due to
3953 * later let declarations in it. We optimize for code simplicity here,
3954 * not the fastest runtime performance with empty [] or {}.
3957 data
->binder
== BindLet
&&
3958 OBJ_BLOCK_COUNT(cx
, tc
->blockChain
) == 0) {
3959 ok
= !!js_DefineNativeProperty(cx
, tc
->blockChain
,
3960 ATOM_TO_JSID(cx
->runtime
->
3961 atomState
.emptyAtom
),
3962 JSVAL_VOID
, NULL
, NULL
,
3966 SPROP_HAS_SHORTID
, 0, NULL
);
3975 JS_DHashTableFinish(&fpvd
.table
);
3979 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
, JSREPORT_ERROR
,
3980 JSMSG_NO_VARIABLE_NAME
);
3986 * This is a greatly pared down version of CheckDestructuring that extends the
3987 * pn_pos.end source coordinate of each name in a destructuring binding such as
3989 * var [x, y] = [function () y, 42];
3991 * to cover its corresponding initializer, so that the initialized binding does
3992 * not appear to dominate any closures in its initializer. See bug 496134.
3994 * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
3995 * not very precise. With one-pass SSA construction from structured source code
3996 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3997 * Languages", Brandis and Mössenböck), we could do much better.
3999 * See CheckDestructuring, immediately above. If you change either of these
4000 * functions, you might have to change the other to match.
4003 UndominateInitializers(JSParseNode
*left
, JSParseNode
*right
, JSTreeContext
*tc
)
4005 FindPropValData fpvd
;
4006 JSParseNode
*lhs
, *rhs
;
4008 JS_ASSERT(left
->pn_type
!= TOK_ARRAYCOMP
);
4011 #if JS_HAS_DESTRUCTURING_SHORTHAND
4012 if (right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
4013 js_ReportCompileErrorNumber(tc
->compiler
->context
, TS(tc
->compiler
), right
,
4014 JSREPORT_ERROR
, JSMSG_BAD_OBJECT_INIT
);
4019 if (right
->pn_type
!= left
->pn_type
)
4022 fpvd
.table
.ops
= NULL
;
4023 lhs
= left
->pn_head
;
4024 if (left
->pn_type
== TOK_RB
) {
4025 rhs
= right
->pn_head
;
4027 while (lhs
&& rhs
) {
4028 /* Nullary comma is an elision; binary comma is an expression.*/
4029 if (lhs
->pn_type
!= TOK_COMMA
|| lhs
->pn_arity
!= PN_NULLARY
) {
4030 if (lhs
->pn_type
== TOK_RB
|| lhs
->pn_type
== TOK_RC
) {
4031 if (!UndominateInitializers(lhs
, rhs
, tc
))
4034 lhs
->pn_pos
.end
= rhs
->pn_pos
.end
;
4042 JS_ASSERT(left
->pn_type
== TOK_RC
);
4043 fpvd
.numvars
= left
->pn_count
;
4047 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
4048 JSParseNode
*pn
= lhs
->pn_right
;
4050 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
4051 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
4052 if (rhs
&& !UndominateInitializers(pn
, rhs
, tc
))
4056 pn
->pn_pos
.end
= rhs
->pn_pos
.end
;
4065 static JSParseNode
*
4066 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
4072 ts
= TS(tc
->compiler
);
4073 tc
->flags
|= TCF_DECL_DESTRUCTURING
;
4074 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
4075 tc
->flags
&= ~TCF_DECL_DESTRUCTURING
;
4078 if (!CheckDestructuring(cx
, data
, pn
, NULL
, tc
))
4084 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
4085 * This function assumes the cloned tree is for use in the same statement and
4086 * binding context as the original tree.
4088 static JSParseNode
*
4089 CloneParseTree(JSParseNode
*opn
, JSTreeContext
*tc
)
4091 JSParseNode
*pn
, *pn2
, *opn2
;
4093 pn
= NewOrRecycledNode(tc
);
4096 pn
->pn_type
= opn
->pn_type
;
4097 pn
->pn_pos
= opn
->pn_pos
;
4098 pn
->pn_op
= opn
->pn_op
;
4099 pn
->pn_used
= opn
->pn_used
;
4100 pn
->pn_defn
= opn
->pn_defn
;
4101 pn
->pn_arity
= opn
->pn_arity
;
4102 pn
->pn_parens
= opn
->pn_parens
;
4104 switch (pn
->pn_arity
) {
4105 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
4108 NULLCHECK(pn
->pn_funbox
=
4109 tc
->compiler
->newFunctionBox(opn
->pn_funbox
->object
, pn
, tc
));
4110 NULLCHECK(pn
->pn_body
= CloneParseTree(opn
->pn_body
, tc
));
4111 pn
->pn_cookie
= opn
->pn_cookie
;
4112 pn
->pn_dflags
= opn
->pn_dflags
;
4113 pn
->pn_blockid
= opn
->pn_blockid
;
4118 for (opn2
= opn
->pn_head
; opn2
; opn2
= opn2
->pn_next
) {
4119 NULLCHECK(pn2
= CloneParseTree(opn2
, tc
));
4122 pn
->pn_xflags
= opn
->pn_xflags
;
4126 NULLCHECK(pn
->pn_kid1
= CloneParseTree(opn
->pn_kid1
, tc
));
4127 NULLCHECK(pn
->pn_kid2
= CloneParseTree(opn
->pn_kid2
, tc
));
4128 NULLCHECK(pn
->pn_kid3
= CloneParseTree(opn
->pn_kid3
, tc
));
4132 NULLCHECK(pn
->pn_left
= CloneParseTree(opn
->pn_left
, tc
));
4133 if (opn
->pn_right
!= opn
->pn_left
)
4134 NULLCHECK(pn
->pn_right
= CloneParseTree(opn
->pn_right
, tc
));
4136 pn
->pn_right
= pn
->pn_left
;
4137 pn
->pn_val
= opn
->pn_val
;
4138 pn
->pn_iflags
= opn
->pn_iflags
;
4142 NULLCHECK(pn
->pn_kid
= CloneParseTree(opn
->pn_kid
, tc
));
4143 pn
->pn_num
= opn
->pn_num
;
4144 pn
->pn_hidden
= opn
->pn_hidden
;
4148 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
4149 pn
->pn_u
= opn
->pn_u
;
4152 * The old name is a use of its pn_lexdef. Make the clone also be a
4153 * use of that definition.
4155 JSDefinition
*dn
= pn
->pn_lexdef
;
4157 pn
->pn_link
= dn
->dn_uses
;
4159 } else if (opn
->pn_expr
) {
4160 NULLCHECK(pn
->pn_expr
= CloneParseTree(opn
->pn_expr
, tc
));
4163 * If the old name is a definition, the new one has pn_defn set.
4164 * Make the old name a use of the new node.
4167 opn
->pn_defn
= false;
4168 LinkUseToDef(opn
, (JSDefinition
*) pn
, tc
);
4174 pn
->pn_names
= opn
->pn_names
;
4175 NULLCHECK(pn
->pn_tree
= CloneParseTree(opn
->pn_tree
, tc
));
4179 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
4180 pn
->pn_u
= opn
->pn_u
;
4188 #endif /* JS_HAS_DESTRUCTURING */
4190 extern const char js_with_statement_str
[];
4192 static JSParseNode
*
4193 ContainsStmt(JSParseNode
*pn
, JSTokenType tt
)
4195 JSParseNode
*pn2
, *pnt
;
4199 if (PN_TYPE(pn
) == tt
)
4201 switch (pn
->pn_arity
) {
4203 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
4204 pnt
= ContainsStmt(pn2
, tt
);
4210 pnt
= ContainsStmt(pn
->pn_kid1
, tt
);
4213 pnt
= ContainsStmt(pn
->pn_kid2
, tt
);
4216 return ContainsStmt(pn
->pn_kid3
, tt
);
4219 * Limit recursion if pn is a binary expression, which can't contain a
4222 if (pn
->pn_op
!= JSOP_NOP
)
4224 pnt
= ContainsStmt(pn
->pn_left
, tt
);
4227 return ContainsStmt(pn
->pn_right
, tt
);
4229 if (pn
->pn_op
!= JSOP_NOP
)
4231 return ContainsStmt(pn
->pn_kid
, tt
);
4233 return ContainsStmt(pn
->maybeExpr(), tt
);
4235 return ContainsStmt(pn
->pn_tree
, tt
);
4241 static JSParseNode
*
4242 ReturnOrYield(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4243 JSParser operandParser
)
4245 JSTokenType tt
, tt2
;
4246 JSParseNode
*pn
, *pn2
;
4248 tt
= CURRENT_TOKEN(ts
).type
;
4249 if (tt
== TOK_RETURN
&& !(tc
->flags
& TCF_IN_FUNCTION
)) {
4250 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4251 JSMSG_BAD_RETURN_OR_YIELD
, js_return_str
);
4255 pn
= NewParseNode(PN_UNARY
, tc
);
4259 #if JS_HAS_GENERATORS
4260 if (tt
== TOK_YIELD
)
4261 tc
->flags
|= TCF_FUN_IS_GENERATOR
;
4264 /* This is ugly, but we don't want to require a semicolon. */
4265 ts
->flags
|= TSF_OPERAND
;
4266 tt2
= js_PeekTokenSameLine(cx
, ts
);
4267 ts
->flags
&= ~TSF_OPERAND
;
4268 if (tt2
== TOK_ERROR
)
4271 if (tt2
!= TOK_EOF
&& tt2
!= TOK_EOL
&& tt2
!= TOK_SEMI
&& tt2
!= TOK_RC
4272 #if JS_HAS_GENERATORS
4273 && (tt
!= TOK_YIELD
||
4274 (tt2
!= tt
&& tt2
!= TOK_RB
&& tt2
!= TOK_RP
&&
4275 tt2
!= TOK_COLON
&& tt2
!= TOK_COMMA
))
4278 pn2
= operandParser(cx
, ts
, tc
);
4281 #if JS_HAS_GENERATORS
4282 if (tt
== TOK_RETURN
)
4284 tc
->flags
|= TCF_RETURN_EXPR
;
4285 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4288 #if JS_HAS_GENERATORS
4289 if (tt
== TOK_RETURN
)
4291 tc
->flags
|= TCF_RETURN_VOID
;
4294 if ((~tc
->flags
& (TCF_RETURN_EXPR
| TCF_FUN_IS_GENERATOR
)) == 0) {
4295 /* As in Python (see PEP-255), disallow return v; in generators. */
4296 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
4297 JSMSG_BAD_GENERATOR_RETURN
,
4298 JSMSG_BAD_ANON_GENERATOR_RETURN
);
4302 if (JS_HAS_STRICT_OPTION(cx
) &&
4303 (~tc
->flags
& (TCF_RETURN_EXPR
| TCF_RETURN_VOID
)) == 0 &&
4304 !ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
4305 JSMSG_NO_RETURN_VALUE
,
4306 JSMSG_ANON_NO_RETURN_VALUE
)) {
4313 static JSParseNode
*
4314 PushLexicalScope(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4319 JSObjectBox
*blockbox
;
4321 pn
= NewParseNode(PN_NAME
, tc
);
4325 obj
= js_NewBlockObject(cx
);
4329 blockbox
= tc
->compiler
->newObjectBox(obj
);
4333 js_PushBlockScope(tc
, stmt
, obj
, -1);
4334 pn
->pn_type
= TOK_LEXICALSCOPE
;
4335 pn
->pn_op
= JSOP_LEAVEBLOCK
;
4336 pn
->pn_objbox
= blockbox
;
4337 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
4339 if (!GenerateBlockId(tc
, stmt
->blockid
))
4341 pn
->pn_blockid
= stmt
->blockid
;
4345 #if JS_HAS_BLOCK_SCOPE
4347 static JSParseNode
*
4348 LetBlock(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, JSBool statement
)
4350 JSParseNode
*pn
, *pnblock
, *pnlet
;
4351 JSStmtInfo stmtInfo
;
4353 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LET
);
4355 /* Create the let binary node. */
4356 pnlet
= NewParseNode(PN_BINARY
, tc
);
4360 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_LET
);
4362 /* This is a let block or expression of the form: let (a, b, c) .... */
4363 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
4367 pn
->pn_expr
= pnlet
;
4369 pnlet
->pn_left
= Variables(cx
, ts
, tc
, true);
4370 if (!pnlet
->pn_left
)
4372 pnlet
->pn_left
->pn_xflags
= PNX_POPVAR
;
4374 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_LET
);
4376 ts
->flags
|= TSF_OPERAND
;
4377 if (statement
&& !js_MatchToken(cx
, ts
, TOK_LC
)) {
4379 * If this is really an expression in let statement guise, then we
4380 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4381 * the return value of the expression.
4383 pn
= NewParseNode(PN_UNARY
, tc
);
4386 pn
->pn_type
= TOK_SEMI
;
4388 pn
->pn_kid
= pnblock
;
4390 statement
= JS_FALSE
;
4392 ts
->flags
&= ~TSF_OPERAND
;
4395 pnlet
->pn_right
= Statements(cx
, ts
, tc
);
4396 if (!pnlet
->pn_right
)
4398 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_LET
);
4401 * Change pnblock's opcode to the variant that propagates the last
4402 * result down after popping the block, and clear statement.
4404 pnblock
->pn_op
= JSOP_LEAVEBLOCKEXPR
;
4405 pnlet
->pn_right
= AssignExpr(cx
, ts
, tc
);
4406 if (!pnlet
->pn_right
)
4414 #endif /* JS_HAS_BLOCK_SCOPE */
4417 PushBlocklikeStatement(JSStmtInfo
*stmt
, JSStmtType type
, JSTreeContext
*tc
)
4419 js_PushStatement(tc
, stmt
, type
, -1);
4420 return GenerateBlockId(tc
, stmt
->blockid
);
4423 static JSParseNode
*
4424 NewBindingNode(JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
4426 JSParseNode
*pn
= NULL
;
4428 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
4431 JS_ASSERT(!pn
->isPlaceholder());
4433 ale
= tc
->lexdeps
.lookup(atom
);
4436 JS_ASSERT(pn
->isPlaceholder());
4441 JS_ASSERT(pn
->pn_defn
);
4444 * A let binding at top level becomes a var before we get here, so if
4445 * pn and tc have the same blockid then that id must not be the bodyid.
4446 * If pn is a forward placeholder definition from the same or a higher
4447 * block then we claim it.
4449 JS_ASSERT_IF(let
&& pn
->pn_blockid
== tc
->blockid(),
4450 pn
->pn_blockid
!= tc
->bodyid
);
4452 if (pn
->isPlaceholder() && pn
->pn_blockid
>= (let
? tc
->blockid() : tc
->bodyid
)) {
4454 pn
->pn_blockid
= tc
->blockid();
4456 tc
->lexdeps
.remove(tc
->compiler
, atom
);
4461 /* Make a new node for this declarator name (or destructuring pattern). */
4462 pn
= NewNameNode(tc
->compiler
->context
, atom
, tc
);
4468 #if JS_HAS_BLOCK_SCOPE
4470 RebindLets(JSParseNode
*pn
, JSTreeContext
*tc
)
4475 switch (pn
->pn_arity
) {
4477 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
4478 RebindLets(pn2
, tc
);
4482 RebindLets(pn
->pn_kid1
, tc
);
4483 RebindLets(pn
->pn_kid2
, tc
);
4484 RebindLets(pn
->pn_kid3
, tc
);
4488 RebindLets(pn
->pn_left
, tc
);
4489 RebindLets(pn
->pn_right
, tc
);
4493 RebindLets(pn
->pn_kid
, tc
);
4497 RebindLets(pn
->pn_body
, tc
);
4501 RebindLets(pn
->maybeExpr(), tc
);
4504 JS_ASSERT(pn
->pn_blockid
> tc
->topStmt
->blockid
);
4505 } else if (pn
->pn_used
) {
4506 if (pn
->pn_lexdef
->pn_blockid
== tc
->topStmt
->blockid
) {
4509 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
4511 while ((ale
= ALE_NEXT(ale
)) != NULL
) {
4512 if (ALE_ATOM(ale
) == pn
->pn_atom
) {
4513 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4519 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
4521 ale
= MakePlaceholder(pn
, tc
);
4525 JSDefinition
*dn
= ALE_DEFN(ale
);
4526 dn
->pn_type
= TOK_NAME
;
4527 dn
->pn_op
= JSOP_NOP
;
4529 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4535 RebindLets(pn
->pn_tree
, tc
);
4541 #endif /* JS_HAS_BLOCK_SCOPE */
4543 static JSParseNode
*
4544 Statement(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4547 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
, *pn4
;
4548 JSStmtInfo stmtInfo
, *stmt
, *stmt2
;
4551 JS_CHECK_RECURSION(cx
, return NULL
);
4553 ts
->flags
|= TSF_OPERAND
;
4554 tt
= js_GetToken(cx
, ts
);
4555 ts
->flags
&= ~TSF_OPERAND
;
4557 #if JS_HAS_GETTER_SETTER
4558 if (tt
== TOK_NAME
) {
4559 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
4560 if (tt
== TOK_ERROR
)
4567 #if JS_HAS_XML_SUPPORT
4568 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
4569 tt
= js_PeekToken(cx
, ts
);
4570 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
4571 if (tt
== TOK_DBLCOLON
)
4574 return FunctionStmt(cx
, ts
, tc
);
4577 /* An IF node has three kids: condition, then, and optional else. */
4578 pn
= NewParseNode(PN_TERNARY
, tc
);
4581 pn1
= Condition(cx
, ts
, tc
);
4584 js_PushStatement(tc
, &stmtInfo
, STMT_IF
, -1);
4585 pn2
= Statement(cx
, ts
, tc
);
4588 ts
->flags
|= TSF_OPERAND
;
4589 if (js_MatchToken(cx
, ts
, TOK_ELSE
)) {
4590 ts
->flags
&= ~TSF_OPERAND
;
4591 stmtInfo
.type
= STMT_ELSE
;
4592 pn3
= Statement(cx
, ts
, tc
);
4595 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
4597 ts
->flags
&= ~TSF_OPERAND
;
4599 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4609 JSParseNode
*pn5
, *saveBlock
;
4610 JSBool seenDefault
= JS_FALSE
;
4612 pn
= NewParseNode(PN_BINARY
, tc
);
4615 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_SWITCH
);
4617 /* pn1 points to the switch's discriminant. */
4618 pn1
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
4622 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_SWITCH
);
4623 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_SWITCH
);
4626 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4627 * because that function states tc->topStmt->blockid.
4629 js_PushStatement(tc
, &stmtInfo
, STMT_SWITCH
, -1);
4631 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4632 pn2
= NewParseNode(PN_LIST
, tc
);
4636 if (!GenerateBlockIdForStmtNode(pn2
, tc
))
4638 saveBlock
= tc
->blockNode
;
4639 tc
->blockNode
= pn2
;
4641 while ((tt
= js_GetToken(cx
, ts
)) != TOK_RC
) {
4645 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4646 JSMSG_TOO_MANY_DEFAULTS
);
4649 seenDefault
= JS_TRUE
;
4653 pn3
= NewParseNode(PN_BINARY
, tc
);
4656 if (tt
== TOK_CASE
) {
4657 pn3
->pn_left
= Expr(cx
, ts
, tc
);
4662 if (pn2
->pn_count
== JS_BIT(16)) {
4663 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4664 JSMSG_TOO_MANY_CASES
);
4673 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4677 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_AFTER_CASE
);
4679 pn4
= NewParseNode(PN_LIST
, tc
);
4682 pn4
->pn_type
= TOK_LC
;
4684 ts
->flags
|= TSF_OPERAND
;
4685 while ((tt
= js_PeekToken(cx
, ts
)) != TOK_RC
&&
4686 tt
!= TOK_CASE
&& tt
!= TOK_DEFAULT
) {
4687 ts
->flags
&= ~TSF_OPERAND
;
4688 if (tt
== TOK_ERROR
)
4690 pn5
= Statement(cx
, ts
, tc
);
4693 pn4
->pn_pos
.end
= pn5
->pn_pos
.end
;
4695 ts
->flags
|= TSF_OPERAND
;
4697 ts
->flags
&= ~TSF_OPERAND
;
4699 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4701 pn4
->pn_pos
.begin
= pn4
->pn_head
->pn_pos
.begin
;
4702 pn3
->pn_pos
.end
= pn4
->pn_pos
.end
;
4703 pn3
->pn_right
= pn4
;
4707 * Handle the case where there was a let declaration in any case in
4708 * the switch body, but not within an inner block. If it replaced
4709 * tc->blockNode with a new block node then we must refresh pn2 and
4710 * then restore tc->blockNode.
4712 if (tc
->blockNode
!= pn2
)
4713 pn2
= tc
->blockNode
;
4714 tc
->blockNode
= saveBlock
;
4717 pn
->pn_pos
.end
= pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4724 pn
= NewParseNode(PN_BINARY
, tc
);
4727 js_PushStatement(tc
, &stmtInfo
, STMT_WHILE_LOOP
, -1);
4728 pn2
= Condition(cx
, ts
, tc
);
4732 pn2
= Statement(cx
, ts
, tc
);
4736 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4741 pn
= NewParseNode(PN_BINARY
, tc
);
4744 js_PushStatement(tc
, &stmtInfo
, STMT_DO_LOOP
, -1);
4745 pn2
= Statement(cx
, ts
, tc
);
4749 MUST_MATCH_TOKEN(TOK_WHILE
, JSMSG_WHILE_AFTER_DO
);
4750 pn2
= Condition(cx
, ts
, tc
);
4754 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4756 if (JSVERSION_NUMBER(cx
) != JSVERSION_ECMA_3
) {
4758 * All legacy and extended versions must do automatic semicolon
4759 * insertion after do-while. See the testcase and discussion in
4760 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4762 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
4769 JSParseNode
*pnseq
= NULL
;
4770 #if JS_HAS_BLOCK_SCOPE
4771 JSParseNode
*pnlet
= NULL
;
4772 JSStmtInfo blockInfo
;
4775 /* A FOR node is binary, left is loop control and right is the body. */
4776 pn
= NewParseNode(PN_BINARY
, tc
);
4779 js_PushStatement(tc
, &stmtInfo
, STMT_FOR_LOOP
, -1);
4781 pn
->pn_op
= JSOP_ITER
;
4783 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
4784 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
4785 pn
->pn_iflags
= JSITER_FOREACH
;
4790 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
4791 ts
->flags
|= TSF_OPERAND
;
4792 tt
= js_PeekToken(cx
, ts
);
4793 ts
->flags
&= ~TSF_OPERAND
;
4795 #if JS_HAS_BLOCK_SCOPE
4799 if (tt
== TOK_SEMI
) {
4800 if (pn
->pn_iflags
& JSITER_FOREACH
)
4803 /* No initializer -- set first kid of left sub-node to null. */
4807 * Set pn1 to a var list or an initializing expression.
4809 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4810 * of the for statement. This flag will be used by the RelExpr
4811 * production; if it is set, then the 'in' keyword will not be
4812 * recognized as an operator, leaving it available to be parsed as
4813 * part of a for/in loop.
4815 * A side effect of this restriction is that (unparenthesized)
4816 * expressions involving an 'in' operator are illegal in the init
4817 * clause of an ordinary for loop.
4819 tc
->flags
|= TCF_IN_FOR_INIT
;
4820 if (tt
== TOK_VAR
) {
4821 (void) js_GetToken(cx
, ts
);
4822 pn1
= Variables(cx
, ts
, tc
, false);
4823 #if JS_HAS_BLOCK_SCOPE
4824 } else if (tt
== TOK_LET
) {
4826 (void) js_GetToken(cx
, ts
);
4827 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
4828 pn1
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
4829 tt
= TOK_LEXICALSCOPE
;
4831 pnlet
= PushLexicalScope(cx
, ts
, tc
, &blockInfo
);
4834 blockInfo
.flags
|= SIF_FOR_BLOCK
;
4835 pn1
= Variables(cx
, ts
, tc
, false);
4839 pn1
= Expr(cx
, ts
, tc
);
4841 tc
->flags
&= ~TCF_IN_FOR_INIT
;
4847 * We can be sure that it's a for/in loop if there's still an 'in'
4848 * keyword here, even if JavaScript recognizes 'in' as an operator,
4849 * as we've excluded 'in' from being parsed in RelExpr by setting
4850 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4852 if (pn1
&& js_MatchToken(cx
, ts
, TOK_IN
)) {
4853 pn
->pn_iflags
|= JSITER_ENUMERATE
;
4854 stmtInfo
.type
= STMT_FOR_IN_LOOP
;
4856 /* Check that the left side of the 'in' is valid. */
4857 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt
) || PN_TYPE(pn1
) == tt
);
4858 if (TOKEN_TYPE_IS_DECL(tt
)
4859 ? (pn1
->pn_count
> 1 || pn1
->pn_op
== JSOP_DEFCONST
4860 #if JS_HAS_DESTRUCTURING
4861 || (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
4862 pn
->pn_op
== JSOP_ITER
&&
4863 !(pn
->pn_iflags
& JSITER_FOREACH
) &&
4864 (pn1
->pn_head
->pn_type
== TOK_RC
||
4865 (pn1
->pn_head
->pn_type
== TOK_RB
&&
4866 pn1
->pn_head
->pn_count
!= 2) ||
4867 (pn1
->pn_head
->pn_type
== TOK_ASSIGN
&&
4868 (pn1
->pn_head
->pn_left
->pn_type
!= TOK_RB
||
4869 pn1
->pn_head
->pn_left
->pn_count
!= 2))))
4872 : (pn1
->pn_type
!= TOK_NAME
&&
4873 pn1
->pn_type
!= TOK_DOT
&&
4874 #if JS_HAS_DESTRUCTURING
4875 ((JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
4876 pn
->pn_op
== JSOP_ITER
&&
4877 !(pn
->pn_iflags
& JSITER_FOREACH
))
4878 ? (pn1
->pn_type
!= TOK_RB
|| pn1
->pn_count
!= 2)
4879 : (pn1
->pn_type
!= TOK_RB
&& pn1
->pn_type
!= TOK_RC
)) &&
4881 pn1
->pn_type
!= TOK_LP
&&
4882 #if JS_HAS_XML_SUPPORT
4883 (pn1
->pn_type
!= TOK_UNARYOP
||
4884 pn1
->pn_op
!= JSOP_XMLNAME
) &&
4886 pn1
->pn_type
!= TOK_LB
)) {
4887 js_ReportCompileErrorNumber(cx
, ts
, pn1
, JSREPORT_ERROR
,
4888 JSMSG_BAD_FOR_LEFTSIDE
);
4892 /* pn2 points to the name or destructuring pattern on in's left. */
4894 uintN dflag
= PND_ASSIGNED
;
4896 if (TOKEN_TYPE_IS_DECL(tt
)) {
4897 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4898 pn1
->pn_xflags
|= PNX_FORINVAR
;
4901 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4902 * 'var', or 'const' to hoist the initializer or the entire
4903 * decl out of the loop head. TOK_VAR is the type for both
4904 * 'var' and 'const'.
4907 if ((pn2
->pn_type
== TOK_NAME
&& pn2
->maybeExpr())
4908 #if JS_HAS_DESTRUCTURING
4909 || pn2
->pn_type
== TOK_ASSIGN
4912 pnseq
= NewParseNode(PN_LIST
, tc
);
4915 pnseq
->pn_type
= TOK_SEQ
;
4916 pnseq
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4918 #if JS_HAS_BLOCK_SCOPE
4919 if (tt
== TOK_LET
) {
4921 * Hoist just the 'i' from 'for (let x = i in o)' to
4922 * before the loop, glued together via pnseq.
4924 pn3
= NewParseNode(PN_UNARY
, tc
);
4927 pn3
->pn_type
= TOK_SEMI
;
4928 pn3
->pn_op
= JSOP_NOP
;
4929 #if JS_HAS_DESTRUCTURING
4930 if (pn2
->pn_type
== TOK_ASSIGN
) {
4931 pn4
= pn2
->pn_right
;
4932 pn2
= pn1
->pn_head
= pn2
->pn_left
;
4937 pn2
->pn_expr
= NULL
;
4939 if (!RebindLets(pn4
, tc
))
4941 pn3
->pn_pos
= pn4
->pn_pos
;
4943 pnseq
->initList(pn3
);
4945 #endif /* JS_HAS_BLOCK_SCOPE */
4947 dflag
= PND_INITIALIZED
;
4950 * All of 'var x = i' is hoisted above 'for (x in o)',
4951 * so clear PNX_FORINVAR.
4953 * Request JSOP_POP here since the var is for a simple
4954 * name (it is not a destructuring binding's left-hand
4955 * side) and it has an initializer.
4957 pn1
->pn_xflags
&= ~PNX_FORINVAR
;
4958 pn1
->pn_xflags
|= PNX_POPVAR
;
4959 pnseq
->initList(pn1
);
4961 #if JS_HAS_DESTRUCTURING
4962 if (pn2
->pn_type
== TOK_ASSIGN
) {
4963 pn1
= CloneParseTree(pn2
->pn_left
, tc
);
4969 JS_ASSERT(pn2
->pn_type
== TOK_NAME
);
4970 pn1
= NewNameNode(cx
, pn2
->pn_atom
, tc
);
4973 pn1
->pn_type
= TOK_NAME
;
4974 pn1
->pn_op
= JSOP_NAME
;
4975 pn1
->pn_pos
= pn2
->pn_pos
;
4977 LinkUseToDef(pn1
, (JSDefinition
*) pn2
, tc
);
4986 if (pn2
->pn_type
== TOK_LP
&&
4987 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
)) {
4990 #if JS_HAS_XML_SUPPORT
4991 if (pn2
->pn_type
== TOK_UNARYOP
)
4992 pn2
->pn_op
= JSOP_BINDXMLNAME
;
4996 switch (pn2
->pn_type
) {
4998 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4999 NoteLValue(cx
, pn2
, tc
, dflag
);
5002 #if JS_HAS_DESTRUCTURING
5005 JS_ASSERT(pn2
->pn_type
== TOK_RB
|| pn2
->pn_type
== TOK_RC
);
5009 /* Check for valid lvalues in var-less destructuring for-in. */
5010 if (pn1
== pn2
&& !CheckDestructuring(cx
, NULL
, pn2
, NULL
, tc
))
5013 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
5015 * Destructuring for-in requires [key, value] enumeration
5018 JS_ASSERT(pn
->pn_op
== JSOP_ITER
);
5019 if (!(pn
->pn_iflags
& JSITER_FOREACH
))
5020 pn
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
5029 * Parse the object expression as the right operand of 'in', first
5030 * removing the top statement from the statement-stack if this is a
5031 * 'for (let x in y)' loop.
5033 #if JS_HAS_BLOCK_SCOPE
5034 JSStmtInfo
*save
= tc
->topStmt
;
5036 tc
->topStmt
= save
->down
;
5038 pn2
= Expr(cx
, ts
, tc
);
5039 #if JS_HAS_BLOCK_SCOPE
5044 pn2
= NewBinary(TOK_IN
, JSOP_NOP
, pn1
, pn2
, tc
);
5049 if (pn
->pn_iflags
& JSITER_FOREACH
)
5051 pn
->pn_op
= JSOP_NOP
;
5053 /* Parse the loop condition or null into pn2. */
5054 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
5055 ts
->flags
|= TSF_OPERAND
;
5056 tt
= js_PeekToken(cx
, ts
);
5057 ts
->flags
&= ~TSF_OPERAND
;
5058 if (tt
== TOK_SEMI
) {
5061 pn2
= Expr(cx
, ts
, tc
);
5066 /* Parse the update expression or null into pn3. */
5067 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
5068 ts
->flags
|= TSF_OPERAND
;
5069 tt
= js_PeekToken(cx
, ts
);
5070 ts
->flags
&= ~TSF_OPERAND
;
5074 pn3
= Expr(cx
, ts
, tc
);
5079 /* Build the FORHEAD node to use as the left kid of pn. */
5080 pn4
= NewParseNode(PN_TERNARY
, tc
);
5083 pn4
->pn_type
= TOK_FORHEAD
;
5084 pn4
->pn_op
= JSOP_NOP
;
5091 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
5093 /* Parse the loop body into pn->pn_right. */
5094 pn2
= Statement(cx
, ts
, tc
);
5099 /* Record the absolute line number for source note emission. */
5100 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5102 #if JS_HAS_BLOCK_SCOPE
5105 pnlet
->pn_expr
= pn
;
5110 pnseq
->pn_pos
.end
= pn
->pn_pos
.end
;
5118 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
5119 JSMSG_BAD_FOR_EACH_LOOP
);
5124 JSParseNode
*catchList
, *lastCatch
;
5127 * try nodes are ternary.
5128 * kid1 is the try Statement
5129 * kid2 is the catch node list or null
5130 * kid3 is the finally Statement
5132 * catch nodes are ternary.
5133 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5134 * kid2 is the catch guard or null if no guard
5135 * kid3 is the catch block
5137 * catch lvalue nodes are either:
5138 * TOK_NAME for a single identifier
5139 * TOK_RB or TOK_RC for a destructuring left-hand side
5141 * finally nodes are TOK_LC Statement lists.
5143 pn
= NewParseNode(PN_TERNARY
, tc
);
5146 pn
->pn_op
= JSOP_NOP
;
5148 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_TRY
);
5149 if (!PushBlocklikeStatement(&stmtInfo
, STMT_TRY
, tc
))
5151 pn
->pn_kid1
= Statements(cx
, ts
, tc
);
5154 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_TRY
);
5158 tt
= js_GetToken(cx
, ts
);
5159 if (tt
== TOK_CATCH
) {
5160 catchList
= NewParseNode(PN_LIST
, tc
);
5163 catchList
->pn_type
= TOK_RESERVED
;
5164 catchList
->makeEmpty();
5168 JSParseNode
*pnblock
;
5171 /* Check for another catch after unconditional catch. */
5172 if (lastCatch
&& !lastCatch
->pn_kid2
) {
5173 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5174 JSMSG_CATCH_AFTER_GENERAL
);
5179 * Create a lexical scope node around the whole catch clause,
5180 * including the head.
5182 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
5185 stmtInfo
.type
= STMT_CATCH
;
5188 * Legal catch forms are:
5190 * catch (lhs if <boolean_expression>)
5191 * where lhs is a name or a destructuring left-hand side.
5192 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5194 pn2
= NewParseNode(PN_TERNARY
, tc
);
5197 pnblock
->pn_expr
= pn2
;
5198 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_CATCH
);
5201 * Contrary to ECMA Ed. 3, the catch variable is lexically
5202 * scoped, not a property of a new Object instance. This is
5203 * an intentional change that anticipates ECMA Ed. 4.
5207 data
.binder
= BindLet
;
5208 data
.let
.overflow
= JSMSG_TOO_MANY_CATCH_VARS
;
5210 tt
= js_GetToken(cx
, ts
);
5212 #if JS_HAS_DESTRUCTURING
5215 pn3
= DestructuringExpr(cx
, &data
, tc
, tt
);
5222 label
= CURRENT_TOKEN(ts
).t_atom
;
5223 pn3
= NewBindingNode(label
, tc
, true);
5227 if (!data
.binder(cx
, &data
, label
, tc
))
5232 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5233 JSMSG_CATCH_IDENTIFIER
);
5238 #if JS_HAS_CATCH_GUARD
5240 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5241 * to avoid conflicting with the JS2/ECMAv4 type annotation
5242 * catchguard syntax.
5244 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
5245 pn2
->pn_kid2
= Expr(cx
, ts
, tc
);
5250 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_CATCH
);
5252 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_CATCH
);
5253 pn2
->pn_kid3
= Statements(cx
, ts
, tc
);
5256 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_CATCH
);
5259 catchList
->append(pnblock
);
5261 ts
->flags
|= TSF_OPERAND
;
5262 tt
= js_GetToken(cx
, ts
);
5263 ts
->flags
&= ~TSF_OPERAND
;
5264 } while (tt
== TOK_CATCH
);
5266 pn
->pn_kid2
= catchList
;
5268 if (tt
== TOK_FINALLY
) {
5269 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_FINALLY
);
5270 if (!PushBlocklikeStatement(&stmtInfo
, STMT_FINALLY
, tc
))
5272 pn
->pn_kid3
= Statements(cx
, ts
, tc
);
5275 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_FINALLY
);
5280 if (!catchList
&& !pn
->pn_kid3
) {
5281 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5282 JSMSG_CATCH_OR_FINALLY
);
5289 pn
= NewParseNode(PN_UNARY
, tc
);
5293 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5294 ts
->flags
|= TSF_OPERAND
;
5295 tt
= js_PeekTokenSameLine(cx
, ts
);
5296 ts
->flags
&= ~TSF_OPERAND
;
5297 if (tt
== TOK_ERROR
)
5299 if (tt
== TOK_EOF
|| tt
== TOK_EOL
|| tt
== TOK_SEMI
|| tt
== TOK_RC
) {
5300 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5301 JSMSG_SYNTAX_ERROR
);
5305 pn2
= Expr(cx
, ts
, tc
);
5308 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5309 pn
->pn_op
= JSOP_THROW
;
5313 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5315 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5316 JSMSG_CATCH_WITHOUT_TRY
);
5320 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5321 JSMSG_FINALLY_WITHOUT_TRY
);
5325 pn
= NewParseNode(PN_NULLARY
, tc
);
5328 if (!MatchLabel(cx
, ts
, pn
))
5331 label
= pn
->pn_atom
;
5333 for (; ; stmt
= stmt
->down
) {
5335 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5336 JSMSG_LABEL_NOT_FOUND
);
5339 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
)
5343 for (; ; stmt
= stmt
->down
) {
5345 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5349 if (STMT_IS_LOOP(stmt
) || stmt
->type
== STMT_SWITCH
)
5354 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5358 pn
= NewParseNode(PN_NULLARY
, tc
);
5361 if (!MatchLabel(cx
, ts
, pn
))
5364 label
= pn
->pn_atom
;
5366 for (stmt2
= NULL
; ; stmt
= stmt
->down
) {
5368 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5369 JSMSG_LABEL_NOT_FOUND
);
5372 if (stmt
->type
== STMT_LABEL
) {
5373 if (stmt
->label
== label
) {
5374 if (!stmt2
|| !STMT_IS_LOOP(stmt2
)) {
5375 js_ReportCompileErrorNumber(cx
, ts
, NULL
,
5377 JSMSG_BAD_CONTINUE
);
5387 for (; ; stmt
= stmt
->down
) {
5389 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5390 JSMSG_BAD_CONTINUE
);
5393 if (STMT_IS_LOOP(stmt
))
5398 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5403 * In most cases, we want the constructs forbidden in strict mode
5404 * code to be a subset of those that JSOPTION_STRICT warns about, and
5405 * we should use js_ReportStrictModeError. However, 'with' is the sole
5406 * instance of a construct that is forbidden in strict mode code, but
5407 * doesn't even merit a warning under JSOPTION_STRICT. See
5408 * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5410 if (tc
->flags
& TCF_STRICT_MODE_CODE
) {
5411 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5412 JSMSG_STRICT_CODE_WITH
);
5416 pn
= NewParseNode(PN_BINARY
, tc
);
5419 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_WITH
);
5420 pn2
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
5423 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_WITH
);
5426 js_PushStatement(tc
, &stmtInfo
, STMT_WITH
, -1);
5427 pn2
= Statement(cx
, ts
, tc
);
5432 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5434 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5438 pn
= Variables(cx
, ts
, tc
, false);
5442 /* Tell js_EmitTree to generate a final POP. */
5443 pn
->pn_xflags
|= PNX_POPVAR
;
5446 #if JS_HAS_BLOCK_SCOPE
5450 JSObjectBox
*blockbox
;
5452 /* Check for a let statement or let expression. */
5453 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
5454 pn
= LetBlock(cx
, ts
, tc
, JS_TRUE
);
5455 if (!pn
|| pn
->pn_op
== JSOP_LEAVEBLOCK
)
5458 /* Let expressions require automatic semicolon insertion. */
5459 JS_ASSERT(pn
->pn_type
== TOK_SEMI
||
5460 pn
->pn_op
== JSOP_LEAVEBLOCKEXPR
);
5465 * This is a let declaration. We must be directly under a block per
5466 * the proposed ES4 specs, but not an implicit block created due to
5467 * 'for (let ...)'. If we pass this error test, make the enclosing
5468 * JSStmtInfo be our scope. Further let declarations in this block
5469 * will find this scope statement and use the same block object.
5471 * If we are the first let declaration in this block (i.e., when the
5472 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5473 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5477 (!STMT_MAYBE_SCOPE(stmt
) || (stmt
->flags
& SIF_FOR_BLOCK
))) {
5478 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5479 JSMSG_LET_DECL_NOT_IN_BLOCK
);
5483 if (stmt
&& (stmt
->flags
& SIF_SCOPE
)) {
5484 JS_ASSERT(tc
->blockChain
== stmt
->blockObj
);
5485 obj
= tc
->blockChain
;
5487 if (!stmt
|| (stmt
->flags
& SIF_BODY_BLOCK
)) {
5489 * ES4 specifies that let at top level and at body-block scope
5490 * does not shadow var, so convert back to var.
5492 CURRENT_TOKEN(ts
).type
= TOK_VAR
;
5493 CURRENT_TOKEN(ts
).t_op
= JSOP_DEFVAR
;
5495 pn
= Variables(cx
, ts
, tc
, false);
5498 pn
->pn_xflags
|= PNX_POPVAR
;
5503 * Some obvious assertions here, but they may help clarify the
5504 * situation. This stmt is not yet a scope, so it must not be a
5505 * catch block (catch is a lexical scope by definition).
5507 JS_ASSERT(!(stmt
->flags
& SIF_SCOPE
));
5508 JS_ASSERT(stmt
!= tc
->topScopeStmt
);
5509 JS_ASSERT(stmt
->type
== STMT_BLOCK
||
5510 stmt
->type
== STMT_SWITCH
||
5511 stmt
->type
== STMT_TRY
||
5512 stmt
->type
== STMT_FINALLY
);
5513 JS_ASSERT(!stmt
->downScope
);
5515 /* Convert the block statement into a scope statement. */
5516 JSObject
*obj
= js_NewBlockObject(tc
->compiler
->context
);
5520 blockbox
= tc
->compiler
->newObjectBox(obj
);
5525 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5526 * list stack, if it isn't already there. If it is there, but it
5527 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5530 stmt
->flags
|= SIF_SCOPE
;
5531 stmt
->downScope
= tc
->topScopeStmt
;
5532 tc
->topScopeStmt
= stmt
;
5533 JS_SCOPE_DEPTH_METERING(++tc
->scopeDepth
> tc
->maxScopeDepth
&&
5534 (tc
->maxScopeDepth
= tc
->scopeDepth
));
5536 STOBJ_SET_PARENT(obj
, tc
->blockChain
);
5537 tc
->blockChain
= obj
;
5538 stmt
->blockObj
= obj
;
5541 pn1
= tc
->blockNode
;
5542 JS_ASSERT(!pn1
|| pn1
->pn_type
!= TOK_LEXICALSCOPE
);
5545 /* Create a new lexical scope node for these statements. */
5546 pn1
= NewParseNode(PN_NAME
, tc
);
5550 pn1
->pn_type
= TOK_LEXICALSCOPE
;
5551 pn1
->pn_op
= JSOP_LEAVEBLOCK
;
5552 pn1
->pn_pos
= tc
->blockNode
->pn_pos
;
5553 pn1
->pn_objbox
= blockbox
;
5554 pn1
->pn_expr
= tc
->blockNode
;
5555 pn1
->pn_blockid
= tc
->blockNode
->pn_blockid
;
5556 tc
->blockNode
= pn1
;
5559 pn
= Variables(cx
, ts
, tc
, false);
5562 pn
->pn_xflags
= PNX_POPVAR
;
5565 #endif /* JS_HAS_BLOCK_SCOPE */
5568 pn
= ReturnOrYield(cx
, ts
, tc
, Expr
);
5577 oldflags
= tc
->flags
;
5578 tc
->flags
= oldflags
& ~TCF_HAS_FUNCTION_STMT
;
5579 if (!PushBlocklikeStatement(&stmtInfo
, STMT_BLOCK
, tc
))
5581 pn
= Statements(cx
, ts
, tc
);
5585 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_COMPOUND
);
5589 * If we contain a function statement and our container is top-level
5590 * or another block, flag pn to preserve braces when decompiling.
5592 if ((tc
->flags
& TCF_HAS_FUNCTION_STMT
) &&
5593 (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)) {
5594 pn
->pn_xflags
|= PNX_NEEDBRACES
;
5596 tc
->flags
= oldflags
| (tc
->flags
& (TCF_FUN_FLAGS
| TCF_RETURN_FLAGS
));
5602 pn
= NewParseNode(PN_UNARY
, tc
);
5605 pn
->pn_type
= TOK_SEMI
;
5608 #if JS_HAS_DEBUGGER_KEYWORD
5610 pn
= NewParseNode(PN_NULLARY
, tc
);
5613 pn
->pn_type
= TOK_DEBUGGER
;
5614 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5616 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5618 #if JS_HAS_XML_SUPPORT
5620 pn
= NewParseNode(PN_UNARY
, tc
);
5623 if (!js_MatchToken(cx
, ts
, TOK_NAME
) ||
5624 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.xmlAtom
||
5625 !js_MatchToken(cx
, ts
, TOK_NAME
) ||
5626 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.namespaceAtom
||
5627 !js_MatchToken(cx
, ts
, TOK_ASSIGN
) ||
5628 CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
5629 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5630 JSMSG_BAD_DEFAULT_XML_NAMESPACE
);
5634 /* Is this an E4X dagger I see before me? */
5635 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5636 pn2
= Expr(cx
, ts
, tc
);
5639 pn
->pn_op
= JSOP_DEFXMLNS
;
5640 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5649 #if JS_HAS_XML_SUPPORT
5653 pn2
= Expr(cx
, ts
, tc
);
5657 if (js_PeekToken(cx
, ts
) == TOK_COLON
) {
5658 if (pn2
->pn_type
!= TOK_NAME
) {
5659 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5663 label
= pn2
->pn_atom
;
5664 for (stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
5665 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
) {
5666 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5667 JSMSG_DUPLICATE_LABEL
);
5673 (void) js_GetToken(cx
, ts
);
5675 /* Push a label struct and parse the statement. */
5676 js_PushStatement(tc
, &stmtInfo
, STMT_LABEL
, -1);
5677 stmtInfo
.label
= label
;
5678 pn
= Statement(cx
, ts
, tc
);
5682 /* Normalize empty statement to empty block for the decompiler. */
5683 if (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
) {
5684 pn
->pn_type
= TOK_LC
;
5685 pn
->pn_arity
= PN_LIST
;
5689 /* Pop the label, set pn_expr, and return early. */
5691 pn2
->pn_type
= TOK_COLON
;
5692 pn2
->pn_pos
.end
= pn
->pn_pos
.end
;
5697 pn
= NewParseNode(PN_UNARY
, tc
);
5700 pn
->pn_type
= TOK_SEMI
;
5701 pn
->pn_pos
= pn2
->pn_pos
;
5705 * Specialize JSOP_SETPROP into JSOP_SETMETHOD to defer or avoid null
5706 * closure cloning. Do this here rather than in AssignExpr as only now
5707 * do we know that the uncloned (unjoined in ES3 terms) function object
5708 * result of the assignment expression can't escape.
5710 if (PN_TYPE(pn2
) == TOK_ASSIGN
&& PN_OP(pn2
) == JSOP_NOP
&&
5711 PN_OP(pn2
->pn_left
) == JSOP_SETPROP
&&
5712 PN_OP(pn2
->pn_right
) == JSOP_LAMBDA
&&
5713 !(pn2
->pn_right
->pn_funbox
->tcflags
5714 & (TCF_FUN_USES_ARGUMENTS
| TCF_FUN_USES_OWN_NAME
))) {
5715 pn2
->pn_left
->pn_op
= JSOP_SETMETHOD
;
5720 /* Check termination of this primitive statement. */
5721 return MatchOrInsertSemicolon(cx
, ts
) ? pn
: NULL
;
5725 NoteArgumentsUse(JSTreeContext
*tc
)
5727 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
5728 tc
->flags
|= TCF_FUN_USES_ARGUMENTS
;
5730 tc
->funbox
->node
->pn_dflags
|= PND_FUNARG
;
5733 static JSParseNode
*
5734 Variables(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, bool inLetHead
)
5738 JSStmtInfo
*scopeStmt
;
5740 JSParseNode
*pn
, *pn2
;
5744 * The three options here are:
5745 * - TOK_LET: We are parsing a let declaration.
5746 * - TOK_LP: We are parsing the head of a let block.
5747 * - Otherwise, we're parsing var declarations.
5749 tt
= CURRENT_TOKEN(ts
).type
;
5750 let
= (tt
== TOK_LET
|| tt
== TOK_LP
);
5751 JS_ASSERT(let
|| tt
== TOK_VAR
);
5753 #if JS_HAS_BLOCK_SCOPE
5754 bool popScope
= (inLetHead
|| (let
&& (tc
->flags
& TCF_IN_FOR_INIT
)));
5755 JSStmtInfo
*save
= tc
->topStmt
, *saveScope
= tc
->topScopeStmt
;
5758 /* Make sure that Statement set up the tree context correctly. */
5759 scopeStmt
= tc
->topScopeStmt
;
5761 while (scopeStmt
&& !(scopeStmt
->flags
& SIF_SCOPE
)) {
5762 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt
));
5763 scopeStmt
= scopeStmt
->downScope
;
5765 JS_ASSERT(scopeStmt
);
5768 data
.op
= let
? JSOP_NOP
: CURRENT_TOKEN(ts
).t_op
;
5769 pn
= NewParseNode(PN_LIST
, tc
);
5772 pn
->pn_op
= data
.op
;
5776 * SpiderMonkey const is really "write once per initialization evaluation"
5777 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5778 * this code will change soon.
5781 JS_ASSERT(tc
->blockChain
== scopeStmt
->blockObj
);
5782 data
.binder
= BindLet
;
5783 data
.let
.overflow
= JSMSG_TOO_MANY_LOCALS
;
5785 data
.binder
= BindVarOrConst
;
5789 tt
= js_GetToken(cx
, ts
);
5790 #if JS_HAS_DESTRUCTURING
5791 if (tt
== TOK_LB
|| tt
== TOK_LC
) {
5792 tc
->flags
|= TCF_DECL_DESTRUCTURING
;
5793 pn2
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
5794 tc
->flags
&= ~TCF_DECL_DESTRUCTURING
;
5798 if (!CheckDestructuring(cx
, &data
, pn2
, NULL
, tc
))
5800 if ((tc
->flags
& TCF_IN_FOR_INIT
) &&
5801 js_PeekToken(cx
, ts
) == TOK_IN
) {
5806 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_BAD_DESTRUCT_DECL
);
5807 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
5810 #if JS_HAS_BLOCK_SCOPE
5812 tc
->topStmt
= save
->down
;
5813 tc
->topScopeStmt
= saveScope
->downScope
;
5816 JSParseNode
*init
= AssignExpr(cx
, ts
, tc
);
5817 #if JS_HAS_BLOCK_SCOPE
5820 tc
->topScopeStmt
= saveScope
;
5824 if (!init
|| !UndominateInitializers(pn2
, init
, tc
))
5827 pn2
= NewBinary(TOK_ASSIGN
, JSOP_NOP
, pn2
, init
, tc
);
5833 #endif /* JS_HAS_DESTRUCTURING */
5835 if (tt
!= TOK_NAME
) {
5836 if (tt
!= TOK_ERROR
) {
5837 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5838 JSMSG_NO_VARIABLE_NAME
);
5843 atom
= CURRENT_TOKEN(ts
).t_atom
;
5844 pn2
= NewBindingNode(atom
, tc
, let
);
5847 if (data
.op
== JSOP_DEFCONST
)
5848 pn2
->pn_dflags
|= PND_CONST
;
5850 if (!data
.binder(cx
, &data
, atom
, tc
))
5854 if (js_MatchToken(cx
, ts
, TOK_ASSIGN
)) {
5855 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
5858 #if JS_HAS_BLOCK_SCOPE
5860 tc
->topStmt
= save
->down
;
5861 tc
->topScopeStmt
= saveScope
->downScope
;
5864 JSParseNode
*init
= AssignExpr(cx
, ts
, tc
);
5865 #if JS_HAS_BLOCK_SCOPE
5868 tc
->topScopeStmt
= saveScope
;
5875 pn2
= MakeAssignment(pn2
, init
, tc
);
5879 pn2
->pn_expr
= init
;
5882 pn2
->pn_op
= (PN_OP(pn2
) == JSOP_ARGUMENTS
)
5884 : (pn2
->pn_dflags
& PND_GVAR
)
5886 : (pn2
->pn_dflags
& PND_BOUND
)
5888 : (data
.op
== JSOP_DEFCONST
)
5892 NoteLValue(cx
, pn2
, tc
, data
.fresh
? PND_INITIALIZED
: PND_ASSIGNED
);
5894 /* The declarator's position must include the initializer. */
5895 pn2
->pn_pos
.end
= init
->pn_pos
.end
;
5897 if ((tc
->flags
& TCF_IN_FUNCTION
) &&
5898 atom
== cx
->runtime
->atomState
.argumentsAtom
) {
5899 NoteArgumentsUse(tc
);
5901 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5904 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
5906 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5910 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5911 JSMSG_BAD_VAR_INIT
);
5915 static JSParseNode
*
5916 Expr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5918 JSParseNode
*pn
, *pn2
;
5920 pn
= AssignExpr(cx
, ts
, tc
);
5921 if (pn
&& js_MatchToken(cx
, ts
, TOK_COMMA
)) {
5922 pn2
= NewParseNode(PN_LIST
, tc
);
5925 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
5929 #if JS_HAS_GENERATORS
5931 if (pn2
->pn_type
== TOK_YIELD
&& !pn2
->pn_parens
) {
5932 js_ReportCompileErrorNumber(cx
, ts
, pn2
, JSREPORT_ERROR
,
5933 JSMSG_BAD_GENERATOR_SYNTAX
,
5938 pn2
= AssignExpr(cx
, ts
, tc
);
5942 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
5943 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5948 static JSParseNode
*
5949 AssignExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5951 JSParseNode
*pn
, *rhs
;
5955 JS_CHECK_RECURSION(cx
, return NULL
);
5957 #if JS_HAS_GENERATORS
5958 ts
->flags
|= TSF_OPERAND
;
5959 if (js_MatchToken(cx
, ts
, TOK_YIELD
)) {
5960 ts
->flags
&= ~TSF_OPERAND
;
5961 return ReturnOrYield(cx
, ts
, tc
, AssignExpr
);
5963 ts
->flags
&= ~TSF_OPERAND
;
5966 pn
= CondExpr(cx
, ts
, tc
);
5970 tt
= js_GetToken(cx
, ts
);
5971 #if JS_HAS_GETTER_SETTER
5972 if (tt
== TOK_NAME
) {
5973 tt
= CheckGetterOrSetter(cx
, ts
, TOK_ASSIGN
);
5974 if (tt
== TOK_ERROR
)
5978 if (tt
!= TOK_ASSIGN
) {
5983 op
= CURRENT_TOKEN(ts
).t_op
;
5984 switch (pn
->pn_type
) {
5986 if (!CheckStrictAssignment(cx
, tc
, pn
))
5988 pn
->pn_op
= JSOP_SETNAME
;
5989 NoteLValue(cx
, pn
, tc
);
5992 pn
->pn_op
= JSOP_SETPROP
;
5995 pn
->pn_op
= JSOP_SETELEM
;
5997 #if JS_HAS_DESTRUCTURING
6000 if (op
!= JSOP_NOP
) {
6001 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6002 JSMSG_BAD_DESTRUCT_ASS
);
6005 rhs
= AssignExpr(cx
, ts
, tc
);
6006 if (!rhs
|| !CheckDestructuring(cx
, NULL
, pn
, rhs
, tc
))
6008 return NewBinary(TOK_ASSIGN
, op
, pn
, rhs
, tc
);
6011 if (!MakeSetCall(cx
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
6014 #if JS_HAS_XML_SUPPORT
6016 if (pn
->pn_op
== JSOP_XMLNAME
) {
6017 pn
->pn_op
= JSOP_SETXMLNAME
;
6023 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6024 JSMSG_BAD_LEFTSIDE_OF_ASS
);
6028 rhs
= AssignExpr(cx
, ts
, tc
);
6029 if (rhs
&& PN_TYPE(pn
) == TOK_NAME
&& pn
->pn_used
) {
6030 JSDefinition
*dn
= pn
->pn_lexdef
;
6033 * If the definition is not flagged as assigned, we must have imputed
6034 * the initialized flag to it, to optimize for flat closures. But that
6035 * optimization uses source coordinates to check dominance relations,
6036 * so we must extend the end of the definition to cover the right-hand
6037 * side of this assignment, i.e., the initializer.
6039 if (!dn
->isAssigned()) {
6040 JS_ASSERT(dn
->isInitialized());
6041 dn
->pn_pos
.end
= rhs
->pn_pos
.end
;
6045 return NewBinary(TOK_ASSIGN
, op
, pn
, rhs
, tc
);
6048 static JSParseNode
*
6049 CondExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6051 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
;
6054 pn
= OrExpr(cx
, ts
, tc
);
6055 if (pn
&& js_MatchToken(cx
, ts
, TOK_HOOK
)) {
6057 pn
= NewParseNode(PN_TERNARY
, tc
);
6061 * Always accept the 'in' operator in the middle clause of a ternary,
6062 * where it's unambiguous, even if we might be parsing the init of a
6065 oldflags
= tc
->flags
;
6066 tc
->flags
&= ~TCF_IN_FOR_INIT
;
6067 pn2
= AssignExpr(cx
, ts
, tc
);
6068 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
6072 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_IN_COND
);
6073 pn3
= AssignExpr(cx
, ts
, tc
);
6076 pn
->pn_pos
.begin
= pn1
->pn_pos
.begin
;
6077 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
6085 static JSParseNode
*
6086 OrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6090 pn
= AndExpr(cx
, ts
, tc
);
6091 while (pn
&& js_MatchToken(cx
, ts
, TOK_OR
))
6092 pn
= NewBinary(TOK_OR
, JSOP_OR
, pn
, AndExpr(cx
, ts
, tc
), tc
);
6096 static JSParseNode
*
6097 AndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6101 pn
= BitOrExpr(cx
, ts
, tc
);
6102 while (pn
&& js_MatchToken(cx
, ts
, TOK_AND
))
6103 pn
= NewBinary(TOK_AND
, JSOP_AND
, pn
, BitOrExpr(cx
, ts
, tc
), tc
);
6107 static JSParseNode
*
6108 BitOrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6112 pn
= BitXorExpr(cx
, ts
, tc
);
6113 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITOR
)) {
6114 pn
= NewBinary(TOK_BITOR
, JSOP_BITOR
, pn
, BitXorExpr(cx
, ts
, tc
),
6120 static JSParseNode
*
6121 BitXorExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6125 pn
= BitAndExpr(cx
, ts
, tc
);
6126 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITXOR
)) {
6127 pn
= NewBinary(TOK_BITXOR
, JSOP_BITXOR
, pn
, BitAndExpr(cx
, ts
, tc
),
6133 static JSParseNode
*
6134 BitAndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6138 pn
= EqExpr(cx
, ts
, tc
);
6139 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITAND
))
6140 pn
= NewBinary(TOK_BITAND
, JSOP_BITAND
, pn
, EqExpr(cx
, ts
, tc
), tc
);
6144 static JSParseNode
*
6145 EqExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6150 pn
= RelExpr(cx
, ts
, tc
);
6151 while (pn
&& js_MatchToken(cx
, ts
, TOK_EQOP
)) {
6152 op
= CURRENT_TOKEN(ts
).t_op
;
6153 pn
= NewBinary(TOK_EQOP
, op
, pn
, RelExpr(cx
, ts
, tc
), tc
);
6158 static JSParseNode
*
6159 RelExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6164 uintN inForInitFlag
= tc
->flags
& TCF_IN_FOR_INIT
;
6167 * Uses of the in operator in ShiftExprs are always unambiguous,
6168 * so unset the flag that prohibits recognizing it.
6170 tc
->flags
&= ~TCF_IN_FOR_INIT
;
6172 pn
= ShiftExpr(cx
, ts
, tc
);
6174 (js_MatchToken(cx
, ts
, TOK_RELOP
) ||
6176 * Recognize the 'in' token as an operator only if we're not
6177 * currently in the init expr of a for loop.
6179 (inForInitFlag
== 0 && js_MatchToken(cx
, ts
, TOK_IN
)) ||
6180 js_MatchToken(cx
, ts
, TOK_INSTANCEOF
))) {
6181 tt
= CURRENT_TOKEN(ts
).type
;
6182 op
= CURRENT_TOKEN(ts
).t_op
;
6183 pn
= NewBinary(tt
, op
, pn
, ShiftExpr(cx
, ts
, tc
), tc
);
6185 /* Restore previous state of inForInit flag. */
6186 tc
->flags
|= inForInitFlag
;
6191 static JSParseNode
*
6192 ShiftExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6197 pn
= AddExpr(cx
, ts
, tc
);
6198 while (pn
&& js_MatchToken(cx
, ts
, TOK_SHOP
)) {
6199 op
= CURRENT_TOKEN(ts
).t_op
;
6200 pn
= NewBinary(TOK_SHOP
, op
, pn
, AddExpr(cx
, ts
, tc
), tc
);
6205 static JSParseNode
*
6206 AddExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6212 pn
= MulExpr(cx
, ts
, tc
);
6214 (js_MatchToken(cx
, ts
, TOK_PLUS
) ||
6215 js_MatchToken(cx
, ts
, TOK_MINUS
))) {
6216 tt
= CURRENT_TOKEN(ts
).type
;
6217 op
= (tt
== TOK_PLUS
) ? JSOP_ADD
: JSOP_SUB
;
6218 pn
= NewBinary(tt
, op
, pn
, MulExpr(cx
, ts
, tc
), tc
);
6223 static JSParseNode
*
6224 MulExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6230 pn
= UnaryExpr(cx
, ts
, tc
);
6232 (js_MatchToken(cx
, ts
, TOK_STAR
) ||
6233 js_MatchToken(cx
, ts
, TOK_DIVOP
))) {
6234 tt
= CURRENT_TOKEN(ts
).type
;
6235 op
= CURRENT_TOKEN(ts
).t_op
;
6236 pn
= NewBinary(tt
, op
, pn
, UnaryExpr(cx
, ts
, tc
), tc
);
6241 static JSParseNode
*
6242 SetLvalKid(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6243 JSParseNode
*pn
, JSParseNode
*kid
, const char *name
)
6245 if (kid
->pn_type
!= TOK_NAME
&&
6246 kid
->pn_type
!= TOK_DOT
&&
6247 (kid
->pn_type
!= TOK_LP
||
6248 (kid
->pn_op
!= JSOP_CALL
&& kid
->pn_op
!= JSOP_EVAL
&& kid
->pn_op
!= JSOP_APPLY
)) &&
6249 #if JS_HAS_XML_SUPPORT
6250 (kid
->pn_type
!= TOK_UNARYOP
|| kid
->pn_op
!= JSOP_XMLNAME
) &&
6252 kid
->pn_type
!= TOK_LB
) {
6253 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6254 JSMSG_BAD_OPERAND
, name
);
6257 if (!CheckStrictAssignment(cx
, tc
, kid
))
6263 static const char incop_name_str
[][10] = {"increment", "decrement"};
6266 SetIncOpKid(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6267 JSParseNode
*pn
, JSParseNode
*kid
,
6268 JSTokenType tt
, JSBool preorder
)
6272 kid
= SetLvalKid(cx
, ts
, tc
, pn
, kid
, incop_name_str
[tt
== TOK_DEC
]);
6275 switch (kid
->pn_type
) {
6277 op
= (tt
== TOK_INC
)
6278 ? (preorder
? JSOP_INCNAME
: JSOP_NAMEINC
)
6279 : (preorder
? JSOP_DECNAME
: JSOP_NAMEDEC
);
6280 NoteLValue(cx
, kid
, tc
);
6284 op
= (tt
== TOK_INC
)
6285 ? (preorder
? JSOP_INCPROP
: JSOP_PROPINC
)
6286 : (preorder
? JSOP_DECPROP
: JSOP_PROPDEC
);
6290 if (!MakeSetCall(cx
, kid
, tc
, JSMSG_BAD_INCOP_OPERAND
))
6293 #if JS_HAS_XML_SUPPORT
6295 if (kid
->pn_op
== JSOP_XMLNAME
)
6296 kid
->pn_op
= JSOP_SETXMLNAME
;
6300 op
= (tt
== TOK_INC
)
6301 ? (preorder
? JSOP_INCELEM
: JSOP_ELEMINC
)
6302 : (preorder
? JSOP_DECELEM
: JSOP_ELEMDEC
);
6313 static JSParseNode
*
6314 UnaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6317 JSParseNode
*pn
, *pn2
;
6319 JS_CHECK_RECURSION(cx
, return NULL
);
6321 ts
->flags
|= TSF_OPERAND
;
6322 tt
= js_GetToken(cx
, ts
);
6323 ts
->flags
&= ~TSF_OPERAND
;
6329 pn
= NewParseNode(PN_UNARY
, tc
);
6332 pn
->pn_type
= TOK_UNARYOP
; /* PLUS and MINUS are binary */
6333 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
6334 pn2
= UnaryExpr(cx
, ts
, tc
);
6337 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6343 pn
= NewParseNode(PN_UNARY
, tc
);
6346 pn2
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
6349 if (!SetIncOpKid(cx
, ts
, tc
, pn
, pn2
, tt
, JS_TRUE
))
6351 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6355 pn
= NewParseNode(PN_UNARY
, tc
);
6358 pn2
= UnaryExpr(cx
, ts
, tc
);
6361 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6364 * Under ECMA3, deleting any unary expression is valid -- it simply
6365 * returns true. Here we fold constants before checking for a call
6366 * expression, in order to rule out delete of a generator expression.
6368 if (!js_FoldConstants(cx
, pn2
, tc
))
6370 switch (pn2
->pn_type
) {
6372 if (pn2
->pn_op
!= JSOP_SETCALL
&&
6373 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_DELETE_OPERAND
)) {
6378 if (!js_ReportStrictModeError(cx
, ts
, tc
, pn
, JSMSG_DEPRECATED_DELETE_OPERAND
))
6380 pn2
->pn_op
= JSOP_DELNAME
;
6392 pn
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
6396 /* Don't look across a newline boundary for a postfix incop. */
6397 if (ON_CURRENT_LINE(ts
, pn
->pn_pos
)) {
6398 ts
->flags
|= TSF_OPERAND
;
6399 tt
= js_PeekTokenSameLine(cx
, ts
);
6400 ts
->flags
&= ~TSF_OPERAND
;
6401 if (tt
== TOK_INC
|| tt
== TOK_DEC
) {
6402 (void) js_GetToken(cx
, ts
);
6403 pn2
= NewParseNode(PN_UNARY
, tc
);
6406 if (!SetIncOpKid(cx
, ts
, tc
, pn2
, pn
, tt
, JS_FALSE
))
6408 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6417 #if JS_HAS_GENERATORS
6420 * A dedicated helper for transplanting the comprehension expression E in
6422 * [E for (V in I)] // array comprehension
6423 * (E for (V in I)) // generator expression
6425 * from its initial location in the AST, on the left of the 'for', to its final
6426 * position on the right. To avoid a separate pass we do this by adjusting the
6427 * blockids and name binding links that were established when E was parsed.
6429 * A generator expression desugars like so:
6431 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6433 * so the transplanter must adjust static level as well as blockid. E's source
6434 * coordinates in root->pn_pos are critical to deciding which binding links to
6435 * preserve and which to cut.
6437 * NB: This is not a general tree transplanter -- it knows in particular that
6438 * the one or more bindings induced by V have not yet been created.
6440 class CompExprTransplanter
{
6448 CompExprTransplanter(JSParseNode
*pn
, JSTreeContext
*tc
, bool ge
, uintN adj
)
6449 : root(pn
), tc(tc
), genexp(ge
), adjust(adj
), funcLevel(0)
6453 bool transplant(JSParseNode
*pn
);
6457 * Any definitions nested within the comprehension expression of a generator
6458 * expression must move "down" one static level, which of course increases the
6459 * upvar-frame-skip count.
6462 BumpStaticLevel(JSParseNode
*pn
, JSTreeContext
*tc
)
6464 if (pn
->pn_cookie
!= FREE_UPVAR_COOKIE
) {
6465 uintN level
= UPVAR_FRAME_SKIP(pn
->pn_cookie
) + 1;
6467 JS_ASSERT(level
>= tc
->staticLevel
);
6468 if (level
>= FREE_STATIC_LEVEL
) {
6469 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
6470 JSMSG_TOO_DEEP
, js_function_str
);
6474 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(level
, UPVAR_FRAME_SLOT(pn
->pn_cookie
));
6480 AdjustBlockId(JSParseNode
*pn
, uintN adjust
, JSTreeContext
*tc
)
6482 JS_ASSERT(pn
->pn_arity
== PN_LIST
|| pn
->pn_arity
== PN_FUNC
|| pn
->pn_arity
== PN_NAME
);
6483 pn
->pn_blockid
+= adjust
;
6484 if (pn
->pn_blockid
>= tc
->blockidGen
)
6485 tc
->blockidGen
= pn
->pn_blockid
+ 1;
6489 CompExprTransplanter::transplant(JSParseNode
*pn
)
6494 switch (pn
->pn_arity
) {
6496 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
6498 if (pn
->pn_pos
>= root
->pn_pos
)
6499 AdjustBlockId(pn
, adjust
, tc
);
6503 transplant(pn
->pn_kid1
);
6504 transplant(pn
->pn_kid2
);
6505 transplant(pn
->pn_kid3
);
6509 transplant(pn
->pn_left
);
6511 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6512 if (pn
->pn_right
!= pn
->pn_left
)
6513 transplant(pn
->pn_right
);
6517 transplant(pn
->pn_kid
);
6523 * Only the first level of transplant recursion through functions needs
6524 * to reparent the funbox, since all descendant functions are correctly
6525 * linked under the top-most funbox. But every visit to this case needs
6526 * to update funbox->level.
6528 * Recall that funbox->level is the static level of the code containing
6529 * the definition or expression of the function and not the static level
6530 * of the function's body.
6532 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6534 funbox
->level
= tc
->staticLevel
+ funcLevel
;
6535 if (++funcLevel
== 1 && genexp
) {
6536 JSFunctionBox
*parent
= tc
->funbox
;
6538 JSFunctionBox
**funboxp
= &tc
->parent
->functionList
;
6539 while (*funboxp
!= funbox
)
6540 funboxp
= &(*funboxp
)->siblings
;
6541 *funboxp
= funbox
->siblings
;
6543 funbox
->parent
= parent
;
6544 funbox
->siblings
= parent
->kids
;
6545 parent
->kids
= funbox
;
6546 funbox
->level
= tc
->staticLevel
;
6552 transplant(pn
->maybeExpr());
6553 if (pn
->pn_arity
== PN_FUNC
)
6557 if (genexp
&& !BumpStaticLevel(pn
, tc
))
6559 } else if (pn
->pn_used
) {
6560 JS_ASSERT(pn
->pn_op
!= JSOP_NOP
);
6561 JS_ASSERT(pn
->pn_cookie
== FREE_UPVAR_COOKIE
);
6563 JSDefinition
*dn
= pn
->pn_lexdef
;
6564 JS_ASSERT(dn
->pn_defn
);
6567 * Adjust the definition's block id only if it is a placeholder not
6568 * to the left of the root node, and if pn is the last use visited
6569 * in the comprehension expression (to avoid adjusting the blockid
6572 * Non-placeholder definitions within the comprehension expression
6573 * will be visited further below.
6575 if (dn
->isPlaceholder() && dn
->pn_pos
>= root
->pn_pos
&& dn
->dn_uses
== pn
) {
6576 if (genexp
&& !BumpStaticLevel(dn
, tc
))
6578 AdjustBlockId(dn
, adjust
, tc
);
6581 JSAtom
*atom
= pn
->pn_atom
;
6583 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
6584 JS_ASSERT(!stmt
|| stmt
!= tc
->topStmt
);
6586 if (genexp
&& PN_OP(dn
) != JSOP_CALLEE
) {
6587 JS_ASSERT(!tc
->decls
.lookup(atom
));
6589 if (dn
->pn_pos
< root
->pn_pos
|| dn
->isPlaceholder()) {
6590 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->compiler
, dn
->pn_atom
);
6594 if (dn
->pn_pos
>= root
->pn_pos
) {
6595 tc
->parent
->lexdeps
.remove(tc
->compiler
, atom
);
6597 JSDefinition
*dn2
= (JSDefinition
*)
6598 NewNameNode(tc
->compiler
->context
, dn
->pn_atom
, tc
);
6602 dn2
->pn_type
= dn
->pn_type
;
6603 dn2
->pn_pos
= root
->pn_pos
;
6604 dn2
->pn_defn
= true;
6605 dn2
->pn_dflags
|= PND_PLACEHOLDER
;
6607 JSParseNode
**pnup
= &dn
->dn_uses
;
6609 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_pos
>= root
->pn_pos
) {
6610 pnu
->pn_lexdef
= dn2
;
6611 dn2
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
6612 pnup
= &pnu
->pn_link
;
6614 dn2
->dn_uses
= dn
->dn_uses
;
6615 dn
->dn_uses
= *pnup
;
6621 ALE_SET_DEFN(ale
, dn
);
6626 if (pn
->pn_pos
>= root
->pn_pos
)
6627 AdjustBlockId(pn
, adjust
, tc
);
6631 transplant(pn
->pn_tree
);
6638 * Starting from a |for| keyword after the first array initialiser element or
6639 * an expression in an open parenthesis, parse the tail of the comprehension
6640 * or generator expression signified by this |for| keyword in context.
6642 * Return null on failure, else return the top-most parse node for the array
6643 * comprehension or generator expression, with a unary node as the body of the
6644 * (possibly nested) for-loop, initialized by |type, op, kid|.
6646 static JSParseNode
*
6647 ComprehensionTail(JSParseNode
*kid
, uintN blockid
, JSTreeContext
*tc
,
6648 JSTokenType type
= TOK_SEMI
, JSOp op
= JSOP_NOP
)
6650 JSContext
*cx
= tc
->compiler
->context
;
6651 JSTokenStream
*ts
= TS(tc
->compiler
);
6654 JSParseNode
*pn
, *pn2
, *pn3
, **pnp
;
6655 JSStmtInfo stmtInfo
;
6660 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_FOR
);
6662 if (type
== TOK_SEMI
) {
6664 * Generator expression desugars to an immediately applied lambda that
6665 * yields the next value from a for-in loop (possibly nested, and with
6666 * optional if guard). Make pn be the TOK_LC body node.
6668 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
6671 adjust
= pn
->pn_blockid
- blockid
;
6673 JS_ASSERT(type
== TOK_ARRAYPUSH
);
6676 * Make a parse-node and literal object representing the block scope of
6677 * this array comprehension. Our caller in PrimaryExpr, the TOK_LB case
6678 * aka the array initialiser case, has passed the blockid to claim for
6679 * the comprehension's block scope. We allocate that id or one above it
6680 * here, by calling js_PushLexicalScope.
6682 * In the case of a comprehension expression that has nested blocks
6683 * (e.g., let expressions), we will allocate a higher blockid but then
6684 * slide all blocks "to the right" to make room for the comprehension's
6687 adjust
= tc
->blockid();
6688 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
6692 JS_ASSERT(blockid
<= pn
->pn_blockid
);
6693 JS_ASSERT(blockid
< tc
->blockidGen
);
6694 JS_ASSERT(tc
->bodyid
< blockid
);
6695 pn
->pn_blockid
= stmtInfo
.blockid
= blockid
;
6696 JS_ASSERT(adjust
< blockid
);
6697 adjust
= blockid
- adjust
;
6702 CompExprTransplanter
transplanter(kid
, tc
, type
== TOK_SEMI
, adjust
);
6703 transplanter
.transplant(kid
);
6707 data
.binder
= BindLet
;
6708 data
.let
.overflow
= JSMSG_ARRAY_INIT_TOO_BIG
;
6712 * FOR node is binary, left is loop control and right is body. Use
6713 * index to count each block-local let-variable on the left-hand side
6716 pn2
= NewParseNode(PN_BINARY
, tc
);
6720 pn2
->pn_op
= JSOP_ITER
;
6721 pn2
->pn_iflags
= JSITER_ENUMERATE
;
6722 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
6723 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
6724 pn2
->pn_iflags
|= JSITER_FOREACH
;
6728 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
6731 tt
= js_GetToken(cx
, ts
);
6733 #if JS_HAS_DESTRUCTURING
6736 tc
->flags
|= TCF_DECL_DESTRUCTURING
;
6737 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
6738 tc
->flags
&= ~TCF_DECL_DESTRUCTURING
;
6745 atom
= CURRENT_TOKEN(ts
).t_atom
;
6748 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6749 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6750 * in the operand stack frame. The code generator computes that,
6751 * and it tries to bind all names to slots, so we must let it do
6754 pn3
= NewBindingNode(atom
, tc
, true);
6760 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6761 JSMSG_NO_VARIABLE_NAME
);
6767 MUST_MATCH_TOKEN(TOK_IN
, JSMSG_IN_AFTER_FOR_NAME
);
6768 JSParseNode
*pn4
= Expr(cx
, ts
, tc
);
6771 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
6774 #if JS_HAS_DESTRUCTURING
6777 if (!CheckDestructuring(cx
, &data
, pn3
, NULL
, tc
))
6780 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
6781 /* Destructuring requires [key, value] enumeration in JS1.7. */
6782 if (pn3
->pn_type
!= TOK_RB
|| pn3
->pn_count
!= 2) {
6783 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6784 JSMSG_BAD_FOR_LEFTSIDE
);
6788 JS_ASSERT(pn2
->pn_op
== JSOP_ITER
);
6789 JS_ASSERT(pn2
->pn_iflags
& JSITER_ENUMERATE
);
6790 if (!(pn2
->pn_iflags
& JSITER_FOREACH
))
6791 pn2
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
6798 if (!data
.binder(cx
, &data
, atom
, tc
))
6805 pn2
->pn_left
= NewBinary(TOK_IN
, JSOP_NOP
, pn3
, pn4
, tc
);
6809 pnp
= &pn2
->pn_right
;
6810 } while (js_MatchToken(cx
, ts
, TOK_FOR
));
6812 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
6813 pn2
= NewParseNode(PN_TERNARY
, tc
);
6816 pn2
->pn_kid1
= Condition(cx
, ts
, tc
);
6820 pnp
= &pn2
->pn_kid2
;
6823 pn2
= NewParseNode(PN_UNARY
, tc
);
6826 pn2
->pn_type
= type
;
6835 #if JS_HAS_GENERATOR_EXPRS
6838 * Starting from a |for| keyword after an expression, parse the comprehension
6839 * tail completing this generator expression. Wrap the expression at kid in a
6840 * generator function that is immediately called to evaluate to the generator
6841 * iterator that is the value of this generator expression.
6843 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
6844 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6845 * expression-statement node that constitutes the body of the |for| loop(s) in
6846 * the generator function.
6848 * Note how unlike Python, we do not evaluate the expression to the right of
6849 * the first |in| in the chain of |for| heads. Instead, a generator expression
6850 * is merely sugar for a generator function expression and its application.
6852 static JSParseNode
*
6853 GeneratorExpr(JSParseNode
*pn
, JSParseNode
*kid
, JSTreeContext
*tc
)
6855 /* Initialize pn, connecting it to kid. */
6856 JS_ASSERT(pn
->pn_arity
== PN_UNARY
);
6857 pn
->pn_type
= TOK_YIELD
;
6858 pn
->pn_op
= JSOP_YIELD
;
6859 pn
->pn_parens
= true;
6860 pn
->pn_pos
= kid
->pn_pos
;
6862 pn
->pn_hidden
= true;
6864 /* Make a new node for the desugared generator function. */
6865 JSParseNode
*genfn
= NewParseNode(PN_FUNC
, tc
);
6868 genfn
->pn_type
= TOK_FUNCTION
;
6869 genfn
->pn_op
= JSOP_LAMBDA
;
6870 JS_ASSERT(!genfn
->pn_body
);
6871 genfn
->pn_dflags
= PND_FUNARG
;
6874 JSTreeContext
gentc(tc
->compiler
);
6876 JSFunctionBox
*funbox
= EnterFunction(genfn
, tc
, &gentc
);
6881 * We have to dance around a bit to propagate sharp variables from tc
6882 * to gentc before setting TCF_HAS_SHARPS implicitly by propagating all
6883 * of tc's TCF_FUN_FLAGS flags. As below, we have to be conservative by
6884 * leaving TCF_HAS_SHARPS set in tc if we do propagate to gentc.
6886 if (tc
->flags
& TCF_HAS_SHARPS
) {
6887 gentc
.flags
|= TCF_IN_FUNCTION
;
6888 if (!gentc
.ensureSharpSlots())
6893 * We assume conservatively that any deoptimization flag in tc->flags
6894 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6895 * propagate these flags into genfn. For code simplicity we also do
6896 * not detect if the flags were only set in the kid and could be
6897 * removed from tc->flags.
6899 gentc
.flags
|= TCF_FUN_IS_GENERATOR
| TCF_GENEXP_LAMBDA
|
6900 (tc
->flags
& (TCF_FUN_FLAGS
& ~TCF_FUN_PARAM_ARGUMENTS
));
6901 funbox
->tcflags
|= gentc
.flags
;
6902 genfn
->pn_funbox
= funbox
;
6903 genfn
->pn_blockid
= gentc
.bodyid
;
6905 JSParseNode
*body
= ComprehensionTail(pn
, tc
->blockid(), &gentc
);
6908 JS_ASSERT(!genfn
->pn_body
);
6909 genfn
->pn_body
= body
;
6910 genfn
->pn_pos
.begin
= body
->pn_pos
.begin
= kid
->pn_pos
.begin
;
6911 genfn
->pn_pos
.end
= body
->pn_pos
.end
= CURRENT_TOKEN(TS(tc
->compiler
)).pos
.end
;
6913 if (!LeaveFunction(genfn
, &gentc
, tc
))
6918 * Our result is a call expression that invokes the anonymous generator
6921 JSParseNode
*result
= NewParseNode(PN_LIST
, tc
);
6924 result
->pn_type
= TOK_LP
;
6925 result
->pn_op
= JSOP_CALL
;
6926 result
->pn_pos
.begin
= genfn
->pn_pos
.begin
;
6927 result
->initList(genfn
);
6931 static const char js_generator_str
[] = "generator";
6933 #endif /* JS_HAS_GENERATOR_EXPRS */
6934 #endif /* JS_HAS_GENERATORS */
6937 ArgumentList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6938 JSParseNode
*listNode
)
6942 ts
->flags
|= TSF_OPERAND
;
6943 matched
= js_MatchToken(cx
, ts
, TOK_RP
);
6944 ts
->flags
&= ~TSF_OPERAND
;
6947 JSParseNode
*argNode
= AssignExpr(cx
, ts
, tc
);
6950 #if JS_HAS_GENERATORS
6951 if (argNode
->pn_type
== TOK_YIELD
&&
6952 !argNode
->pn_parens
&&
6953 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
6954 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
6955 JSMSG_BAD_GENERATOR_SYNTAX
,
6960 #if JS_HAS_GENERATOR_EXPRS
6961 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
6962 JSParseNode
*pn
= NewParseNode(PN_UNARY
, tc
);
6965 argNode
= GeneratorExpr(pn
, argNode
, tc
);
6968 if (listNode
->pn_count
> 1 ||
6969 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
6970 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
6971 JSMSG_BAD_GENERATOR_SYNTAX
,
6977 listNode
->append(argNode
);
6978 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
6980 if (js_GetToken(cx
, ts
) != TOK_RP
) {
6981 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6982 JSMSG_PAREN_AFTER_ARGS
);
6989 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6990 static JSParseNode
*
6991 CheckForImmediatelyAppliedLambda(JSParseNode
*pn
)
6993 while (pn
->pn_type
== TOK_RP
)
6995 if (pn
->pn_type
== TOK_FUNCTION
) {
6996 JS_ASSERT(pn
->pn_arity
== PN_FUNC
);
6998 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6999 JS_ASSERT(((JSFunction
*) funbox
->object
)->flags
& JSFUN_LAMBDA
);
7000 if (!(funbox
->tcflags
& (TCF_FUN_USES_ARGUMENTS
| TCF_FUN_USES_OWN_NAME
)))
7001 pn
->pn_dflags
&= ~PND_FUNARG
;
7006 static JSParseNode
*
7007 MemberExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7008 JSBool allowCallSyntax
)
7010 JSParseNode
*pn
, *pn2
, *pn3
;
7013 JS_CHECK_RECURSION(cx
, return NULL
);
7015 /* Check for new expression first. */
7016 ts
->flags
|= TSF_OPERAND
;
7017 tt
= js_GetToken(cx
, ts
);
7018 ts
->flags
&= ~TSF_OPERAND
;
7019 if (tt
== TOK_NEW
) {
7020 pn
= NewParseNode(PN_LIST
, tc
);
7023 pn2
= MemberExpr(cx
, ts
, tc
, JS_FALSE
);
7026 pn2
= CheckForImmediatelyAppliedLambda(pn2
);
7027 pn
->pn_op
= JSOP_NEW
;
7029 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
7031 if (js_MatchToken(cx
, ts
, TOK_LP
) && !ArgumentList(cx
, ts
, tc
, pn
))
7033 if (pn
->pn_count
> ARGC_LIMIT
) {
7034 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
7035 JSMSG_TOO_MANY_CON_ARGS
);
7038 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
7040 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
7044 if (pn
->pn_type
== TOK_ANYNAME
||
7045 pn
->pn_type
== TOK_AT
||
7046 pn
->pn_type
== TOK_DBLCOLON
) {
7047 pn2
= NewOrRecycledNode(tc
);
7050 pn2
->pn_type
= TOK_UNARYOP
;
7051 pn2
->pn_pos
= pn
->pn_pos
;
7052 pn2
->pn_op
= JSOP_XMLNAME
;
7053 pn2
->pn_arity
= PN_UNARY
;
7054 pn2
->pn_parens
= false;
7060 while ((tt
= js_GetToken(cx
, ts
)) > TOK_EOF
) {
7061 if (tt
== TOK_DOT
) {
7062 pn2
= NewNameNode(cx
, NULL
, tc
);
7065 #if JS_HAS_XML_SUPPORT
7066 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
7067 tt
= js_GetToken(cx
, ts
);
7068 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
7069 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
7073 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
7074 if (tt
== TOK_NAME
&& pn3
->pn_type
== TOK_NAME
) {
7075 pn2
->pn_op
= JSOP_GETPROP
;
7077 pn2
->pn_atom
= pn3
->pn_atom
;
7078 RecycleTree(pn3
, tc
);
7081 pn2
->pn_type
= TOK_FILTER
;
7082 pn2
->pn_op
= JSOP_FILTER
;
7084 /* A filtering predicate is like a with statement. */
7085 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
7086 } else if (TOKEN_TYPE_IS_XML(PN_TYPE(pn3
))) {
7087 pn2
->pn_type
= TOK_LB
;
7088 pn2
->pn_op
= JSOP_GETELEM
;
7090 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7091 JSMSG_NAME_AFTER_DOT
);
7094 pn2
->pn_arity
= PN_BINARY
;
7096 pn2
->pn_right
= pn3
;
7099 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7100 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NAME_AFTER_DOT
);
7101 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7102 pn2
->pn_op
= JSOP_GETPROP
;
7104 pn2
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
7106 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7107 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7108 #if JS_HAS_XML_SUPPORT
7109 } else if (tt
== TOK_DBLDOT
) {
7110 pn2
= NewParseNode(PN_BINARY
, tc
);
7113 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
7114 tt
= js_GetToken(cx
, ts
);
7115 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
7116 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
7120 if (tt
== TOK_NAME
&& !pn3
->pn_parens
) {
7121 pn3
->pn_type
= TOK_STRING
;
7122 pn3
->pn_arity
= PN_NULLARY
;
7123 pn3
->pn_op
= JSOP_QNAMEPART
;
7124 } else if (!TOKEN_TYPE_IS_XML(tt
)) {
7125 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7126 JSMSG_NAME_AFTER_DOT
);
7129 pn2
->pn_op
= JSOP_DESCENDANTS
;
7131 pn2
->pn_right
= pn3
;
7132 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7133 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7135 } else if (tt
== TOK_LB
) {
7136 pn2
= NewParseNode(PN_BINARY
, tc
);
7139 pn3
= Expr(cx
, ts
, tc
);
7143 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
7144 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7145 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7148 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
7149 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
7150 * the interpreter from fast property access. However, if the
7151 * bracketed string is a uint32, we rewrite pn3 to be a number
7152 * instead of a string.
7155 if (pn3
->pn_type
== TOK_STRING
) {
7158 if (!js_IdIsIndex(ATOM_TO_JSID(pn3
->pn_atom
), &index
)) {
7159 pn2
->pn_type
= TOK_DOT
;
7160 pn2
->pn_op
= JSOP_GETPROP
;
7161 pn2
->pn_arity
= PN_NAME
;
7163 pn2
->pn_atom
= pn3
->pn_atom
;
7166 pn3
->pn_type
= TOK_NUMBER
;
7167 pn3
->pn_op
= JSOP_DOUBLE
;
7168 pn3
->pn_dval
= index
;
7170 pn2
->pn_op
= JSOP_GETELEM
;
7172 pn2
->pn_right
= pn3
;
7174 } else if (allowCallSyntax
&& tt
== TOK_LP
) {
7175 pn2
= NewParseNode(PN_LIST
, tc
);
7178 pn2
->pn_op
= JSOP_CALL
;
7180 /* CheckForImmediatelyAppliedLambda skips useless TOK_RP nodes. */
7181 pn
= CheckForImmediatelyAppliedLambda(pn
);
7182 if (pn
->pn_op
== JSOP_NAME
) {
7183 if (pn
->pn_atom
== cx
->runtime
->atomState
.evalAtom
) {
7184 /* Select JSOP_EVAL and flag tc as heavyweight. */
7185 pn2
->pn_op
= JSOP_EVAL
;
7186 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
7188 } else if (pn
->pn_op
== JSOP_GETPROP
) {
7189 if (pn
->pn_atom
== cx
->runtime
->atomState
.applyAtom
||
7190 pn
->pn_atom
== cx
->runtime
->atomState
.callAtom
) {
7191 /* Select JSOP_APPLY given foo.apply(...). */
7192 pn2
->pn_op
= JSOP_APPLY
;
7197 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7199 if (!ArgumentList(cx
, ts
, tc
, pn2
))
7201 if (pn2
->pn_count
> ARGC_LIMIT
) {
7202 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
7203 JSMSG_TOO_MANY_FUN_ARGS
);
7206 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7214 if (tt
== TOK_ERROR
)
7219 static JSParseNode
*
7220 BracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7226 * Always accept the 'in' operator in a parenthesized expression,
7227 * where it's unambiguous, even if we might be parsing the init of a
7230 oldflags
= tc
->flags
;
7231 tc
->flags
&= ~TCF_IN_FOR_INIT
;
7232 pn
= Expr(cx
, ts
, tc
);
7233 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
7237 #if JS_HAS_XML_SUPPORT
7239 static JSParseNode
*
7240 EndBracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7244 pn
= BracketedExpr(cx
, ts
, tc
);
7248 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_ATTR_EXPR
);
7253 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7255 * AttributeIdentifier:
7256 * @ PropertySelector
7257 * @ QualifiedIdentifier
7264 * QualifiedIdentifier:
7265 * PropertySelector :: PropertySelector
7266 * PropertySelector :: [ Expression ]
7268 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7270 * AttributeIdentifier:
7271 * @ QualifiedIdentifier
7278 * QualifiedIdentifier:
7279 * PropertySelector :: PropertySelector
7280 * PropertySelector :: [ Expression ]
7283 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7284 * for that rule to result in a name node, but ECMA-357 extends the grammar
7285 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7287 * QualifiedIdentifier:
7288 * PropertySelector QualifiedSuffix
7291 * :: PropertySelector
7295 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7297 * PrimaryExpression:
7298 * Identifier QualifiedSuffix
7300 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7301 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7303 static JSParseNode
*
7304 PropertySelector(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7308 pn
= NewParseNode(PN_NULLARY
, tc
);
7311 if (pn
->pn_type
== TOK_STAR
) {
7312 pn
->pn_type
= TOK_ANYNAME
;
7313 pn
->pn_op
= JSOP_ANYNAME
;
7314 pn
->pn_atom
= cx
->runtime
->atomState
.starAtom
;
7316 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
7317 pn
->pn_op
= JSOP_QNAMEPART
;
7318 pn
->pn_arity
= PN_NAME
;
7319 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
7320 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
7325 static JSParseNode
*
7326 QualifiedSuffix(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
7329 JSParseNode
*pn2
, *pn3
;
7332 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_DBLCOLON
);
7333 pn2
= NewNameNode(cx
, NULL
, tc
);
7337 /* Left operand of :: must be evaluated if it is an identifier. */
7338 if (pn
->pn_op
== JSOP_QNAMEPART
)
7339 pn
->pn_op
= JSOP_NAME
;
7341 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7342 tt
= js_GetToken(cx
, ts
);
7343 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7344 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
7345 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
7346 pn2
->pn_op
= JSOP_QNAMECONST
;
7347 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7348 pn2
->pn_atom
= (tt
== TOK_STAR
)
7349 ? cx
->runtime
->atomState
.starAtom
7350 : CURRENT_TOKEN(ts
).t_atom
;
7352 pn2
->pn_cookie
= FREE_UPVAR_COOKIE
;
7357 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7358 JSMSG_SYNTAX_ERROR
);
7361 pn3
= EndBracketedExpr(cx
, ts
, tc
);
7365 pn2
->pn_op
= JSOP_QNAME
;
7366 pn2
->pn_arity
= PN_BINARY
;
7367 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7368 pn2
->pn_pos
.end
= pn3
->pn_pos
.end
;
7370 pn2
->pn_right
= pn3
;
7374 static JSParseNode
*
7375 QualifiedIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7379 pn
= PropertySelector(cx
, ts
, tc
);
7382 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
7383 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7384 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
7385 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
7390 static JSParseNode
*
7391 AttributeIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7393 JSParseNode
*pn
, *pn2
;
7396 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_AT
);
7397 pn
= NewParseNode(PN_UNARY
, tc
);
7400 pn
->pn_op
= JSOP_TOATTRNAME
;
7401 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7402 tt
= js_GetToken(cx
, ts
);
7403 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7404 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
7405 pn2
= QualifiedIdentifier(cx
, ts
, tc
);
7406 } else if (tt
== TOK_LB
) {
7407 pn2
= EndBracketedExpr(cx
, ts
, tc
);
7409 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7410 JSMSG_SYNTAX_ERROR
);
7420 * Make a TOK_LC unary node whose pn_kid is an expression.
7422 static JSParseNode
*
7423 XMLExpr(JSContext
*cx
, JSTokenStream
*ts
, JSBool inTag
, JSTreeContext
*tc
)
7425 JSParseNode
*pn
, *pn2
;
7428 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LC
);
7429 pn
= NewParseNode(PN_UNARY
, tc
);
7434 * Turn off XML tag mode, but don't restore it after parsing this braced
7435 * expression. Instead, simply restore ts's old flags. This is required
7436 * because XMLExpr is called both from within a tag, and from within text
7437 * contained in an element, but outside of any start, end, or point tag.
7439 oldflags
= ts
->flags
;
7440 ts
->flags
= oldflags
& ~TSF_XMLTAGMODE
;
7441 pn2
= Expr(cx
, ts
, tc
);
7445 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_XML_EXPR
);
7446 ts
->flags
= oldflags
;
7448 pn
->pn_op
= inTag
? JSOP_XMLTAGEXPR
: JSOP_XMLELTEXPR
;
7453 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7454 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7455 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7456 * child of a container tag.
7458 static JSParseNode
*
7459 XMLAtomNode(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7464 pn
= NewParseNode(PN_NULLARY
, tc
);
7467 tp
= &CURRENT_TOKEN(ts
);
7468 pn
->pn_op
= tp
->t_op
;
7469 pn
->pn_atom
= tp
->t_atom
;
7470 if (tp
->type
== TOK_XMLPI
)
7471 pn
->pn_atom2
= tp
->t_atom2
;
7476 * Parse the productions:
7479 * XMLName XMLNameExpr?
7480 * { Expr } XMLNameExpr?
7482 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7483 * a list of names and/or expressions, a single expression, or a single name.
7484 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7487 static JSParseNode
*
7488 XMLNameExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7490 JSParseNode
*pn
, *pn2
, *list
;
7495 tt
= CURRENT_TOKEN(ts
).type
;
7497 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
7501 JS_ASSERT(tt
== TOK_XMLNAME
);
7502 pn2
= XMLAtomNode(cx
, ts
, tc
);
7511 list
= NewParseNode(PN_LIST
, tc
);
7514 list
->pn_type
= TOK_XMLNAME
;
7515 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7517 list
->pn_xflags
= PNX_CANTFOLD
;
7520 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7523 } while ((tt
= js_GetToken(cx
, ts
)) == TOK_XMLNAME
|| tt
== TOK_LC
);
7530 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7531 * at compile time into a JSXML tree.
7533 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7534 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7535 : (pn)->pn_type != TOK_LC)
7538 * Parse the productions:
7542 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7543 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7545 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7546 * produces a list of name and attribute values and/or braced expressions, a
7547 * single expression, or a single name.
7549 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7550 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7551 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7552 * we parsed exactly one expression.
7554 static JSParseNode
*
7555 XMLTagContent(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7556 JSTokenType tagtype
, JSAtom
**namep
)
7558 JSParseNode
*pn
, *pn2
, *list
;
7561 pn
= XMLNameExpr(cx
, ts
, tc
);
7564 *namep
= (pn
->pn_arity
== PN_NULLARY
) ? pn
->pn_atom
: NULL
;
7567 while (js_MatchToken(cx
, ts
, TOK_XMLSPACE
)) {
7568 tt
= js_GetToken(cx
, ts
);
7569 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7574 pn2
= XMLNameExpr(cx
, ts
, tc
);
7578 list
= NewParseNode(PN_LIST
, tc
);
7581 list
->pn_type
= tagtype
;
7582 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7587 if (!XML_FOLDABLE(pn2
))
7588 pn
->pn_xflags
|= PNX_CANTFOLD
;
7590 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7591 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_NO_ASSIGN_IN_XML_ATTR
);
7592 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7594 tt
= js_GetToken(cx
, ts
);
7595 if (tt
== TOK_XMLATTR
) {
7596 pn2
= XMLAtomNode(cx
, ts
, tc
);
7597 } else if (tt
== TOK_LC
) {
7598 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
7599 pn
->pn_xflags
|= PNX_CANTFOLD
;
7601 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7602 JSMSG_BAD_XML_ATTR_VALUE
);
7607 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7614 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7616 if ((tt) <= TOK_EOF) { \
7617 if ((tt) == TOK_EOF) { \
7618 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
7619 JSMSG_END_OF_XML_SOURCE); \
7625 static JSParseNode
*
7626 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7630 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7631 * that opens the end tag for the container.
7634 XMLElementContent(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
7641 ts
->flags
&= ~TSF_XMLTAGMODE
;
7643 ts
->flags
|= TSF_XMLTEXTMODE
;
7644 tt
= js_GetToken(cx
, ts
);
7645 ts
->flags
&= ~TSF_XMLTEXTMODE
;
7646 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7648 JS_ASSERT(tt
== TOK_XMLSPACE
|| tt
== TOK_XMLTEXT
);
7649 textAtom
= CURRENT_TOKEN(ts
).t_atom
;
7651 /* Non-zero-length XML text scanned. */
7652 pn2
= XMLAtomNode(cx
, ts
, tc
);
7655 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7659 ts
->flags
|= TSF_OPERAND
;
7660 tt
= js_GetToken(cx
, ts
);
7661 ts
->flags
&= ~TSF_OPERAND
;
7662 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7663 if (tt
== TOK_XMLETAGO
)
7667 pn2
= XMLExpr(cx
, ts
, JS_FALSE
, tc
);
7668 pn
->pn_xflags
|= PNX_CANTFOLD
;
7669 } else if (tt
== TOK_XMLSTAGO
) {
7670 pn2
= XMLElementOrList(cx
, ts
, tc
, JS_FALSE
);
7672 pn2
->pn_xflags
&= ~PNX_XMLROOT
;
7673 pn
->pn_xflags
|= pn2
->pn_xflags
;
7676 JS_ASSERT(tt
== TOK_XMLCDATA
|| tt
== TOK_XMLCOMMENT
||
7678 pn2
= XMLAtomNode(cx
, ts
, tc
);
7682 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7686 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLETAGO
);
7687 ts
->flags
|= TSF_XMLTAGMODE
;
7692 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7694 static JSParseNode
*
7695 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7698 JSParseNode
*pn
, *pn2
, *list
;
7700 JSAtom
*startAtom
, *endAtom
;
7702 JS_CHECK_RECURSION(cx
, return NULL
);
7704 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLSTAGO
);
7705 pn
= NewParseNode(PN_LIST
, tc
);
7709 ts
->flags
|= TSF_XMLTAGMODE
;
7710 tt
= js_GetToken(cx
, ts
);
7711 if (tt
== TOK_ERROR
)
7714 if (tt
== TOK_XMLNAME
|| tt
== TOK_LC
) {
7716 * XMLElement. Append the tag and its contents, if any, to pn.
7718 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLSTAGO
, &startAtom
);
7721 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7723 tt
= js_GetToken(cx
, ts
);
7724 if (tt
== TOK_XMLPTAGC
) {
7725 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7726 if (pn2
->pn_type
== TOK_XMLSTAGO
) {
7728 RecycleTree(pn
, tc
);
7731 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
||
7732 pn2
->pn_type
== TOK_LC
);
7734 if (!XML_FOLDABLE(pn2
))
7735 pn
->pn_xflags
|= PNX_CANTFOLD
;
7737 pn
->pn_type
= TOK_XMLPTAGC
;
7738 pn
->pn_xflags
|= PNX_XMLROOT
;
7740 /* We had better have a tag-close (>) at this point. */
7741 if (tt
!= TOK_XMLTAGC
) {
7742 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7743 JSMSG_BAD_XML_TAG_SYNTAX
);
7746 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7748 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7749 if (pn2
->pn_type
!= TOK_XMLSTAGO
) {
7751 if (!XML_FOLDABLE(pn2
))
7752 pn
->pn_xflags
|= PNX_CANTFOLD
;
7754 pn
= NewParseNode(PN_LIST
, tc
);
7759 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7760 pn
->pn_type
= TOK_XMLELEM
;
7761 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
7763 if (!XML_FOLDABLE(pn2
))
7764 pn
->pn_xflags
|= PNX_CANTFOLD
;
7765 pn
->pn_xflags
|= PNX_XMLROOT
;
7767 /* Get element contents and delimiting end-tag-open sequence. */
7768 if (!XMLElementContent(cx
, ts
, pn
, tc
))
7771 tt
= js_GetToken(cx
, ts
);
7772 XML_CHECK_FOR_ERROR_AND_EOF(tt
, NULL
);
7773 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7774 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7775 JSMSG_BAD_XML_TAG_SYNTAX
);
7779 /* Parse end tag; check mismatch at compile-time if we can. */
7780 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLETAGO
, &endAtom
);
7783 if (pn2
->pn_type
== TOK_XMLETAGO
) {
7784 /* Oops, end tag has attributes! */
7785 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7786 JSMSG_BAD_XML_TAG_SYNTAX
);
7789 if (endAtom
&& startAtom
&& endAtom
!= startAtom
) {
7790 JSString
*str
= ATOM_TO_STRING(startAtom
);
7792 /* End vs. start tag name mismatch: point to the tag name. */
7793 js_ReportCompileErrorNumber(cx
, ts
, pn2
,
7794 JSREPORT_UC
| JSREPORT_ERROR
,
7795 JSMSG_XML_TAG_NAME_MISMATCH
,
7800 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7801 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
|| pn2
->pn_type
== TOK_LC
);
7802 list
= NewParseNode(PN_LIST
, tc
);
7805 list
->pn_type
= TOK_XMLETAGO
;
7806 list
->initList(pn2
);
7808 if (!XML_FOLDABLE(pn2
)) {
7809 list
->pn_xflags
|= PNX_CANTFOLD
;
7810 pn
->pn_xflags
|= PNX_CANTFOLD
;
7813 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7814 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_TAG_SYNTAX
);
7817 /* Set pn_op now that pn has been updated to its final value. */
7818 pn
->pn_op
= JSOP_TOXML
;
7819 } else if (allowList
&& tt
== TOK_XMLTAGC
) {
7820 /* XMLList Initialiser. */
7821 pn
->pn_type
= TOK_XMLLIST
;
7822 pn
->pn_op
= JSOP_TOXMLLIST
;
7824 pn
->pn_xflags
|= PNX_XMLROOT
;
7825 if (!XMLElementContent(cx
, ts
, pn
, tc
))
7828 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_LIST_SYNTAX
);
7830 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7831 JSMSG_BAD_XML_NAME_SYNTAX
);
7835 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7836 ts
->flags
&= ~TSF_XMLTAGMODE
;
7840 static JSParseNode
*
7841 XMLElementOrListRoot(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7848 * Force XML support to be enabled so that comments and CDATA literals
7849 * are recognized, instead of <! followed by -- starting an HTML comment
7850 * to end of line (used in script tags to hide content from old browsers
7851 * that don't recognize <script>).
7853 oldopts
= JS_SetOptions(cx
, cx
->options
| JSOPTION_XML
);
7854 pn
= XMLElementOrList(cx
, ts
, tc
, allowList
);
7855 JS_SetOptions(cx
, oldopts
);
7860 JSCompiler::parseXMLText(JSObject
*chain
, bool allowList
)
7863 * Push a compiler frame if we have no frames, or if the top frame is a
7864 * lightweight function activation, or if its scope chain doesn't match
7865 * the one passed to us.
7867 JSTreeContext
tc(this);
7868 tc
.scopeChain
= chain
;
7870 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7871 TS(this)->flags
|= TSF_OPERAND
| TSF_XMLONLYMODE
;
7872 JSTokenType tt
= js_GetToken(context
, TS(this));
7873 TS(this)->flags
&= ~TSF_OPERAND
;
7876 if (tt
!= TOK_XMLSTAGO
) {
7877 js_ReportCompileErrorNumber(context
, TS(this), NULL
, JSREPORT_ERROR
,
7878 JSMSG_BAD_XML_MARKUP
);
7881 pn
= XMLElementOrListRoot(context
, TS(this), &tc
, allowList
);
7884 TS(this)->flags
&= ~TSF_XMLONLYMODE
;
7888 #endif /* JS_HAS_XMLSUPPORT */
7890 #if JS_HAS_BLOCK_SCOPE
7892 * Check whether blockid is an active scoping statement in tc. This code is
7893 * necessary to qualify tc->decls.lookup() hits in PrimaryExpr's TOK_NAME case
7894 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7895 * and let blocks and expressions (not let declarations).
7897 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7898 * due to hoisting, let in a for loop head, let block, or let expression acts
7899 * like Scheme's let: initializers are evaluated without the new let bindings
7902 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7903 * bindings push on the front of the tc->decls JSAtomList (either the singular
7904 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7905 * scope bindings of the same name.
7907 * This simplifies binding lookup code at the price of a linear search here,
7908 * but only if code uses let (var predominates), and even then this function's
7909 * loop iterates more than once only in crazy cases.
7912 BlockIdInScope(uintN blockid
, JSTreeContext
*tc
)
7914 if (blockid
> tc
->blockid())
7916 for (JSStmtInfo
*stmt
= tc
->topScopeStmt
; stmt
; stmt
= stmt
->downScope
) {
7917 if (stmt
->blockid
== blockid
)
7924 static JSParseNode
*
7925 PrimaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7926 JSTokenType tt
, JSBool afterDot
)
7928 JSParseNode
*pn
, *pn2
, *pn3
;
7931 JS_CHECK_RECURSION(cx
, return NULL
);
7933 #if JS_HAS_GETTER_SETTER
7934 if (tt
== TOK_NAME
) {
7935 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
7936 if (tt
== TOK_ERROR
)
7943 #if JS_HAS_XML_SUPPORT
7944 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7945 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
7946 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7947 pn2
= NewParseNode(PN_NULLARY
, tc
);
7950 pn2
->pn_type
= TOK_FUNCTION
;
7951 pn
= QualifiedSuffix(cx
, ts
, pn2
, tc
);
7956 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7958 pn
= FunctionExpr(cx
, ts
, tc
);
7968 pn
= NewParseNode(PN_LIST
, tc
);
7971 pn
->pn_type
= TOK_RB
;
7972 pn
->pn_op
= JSOP_NEWINIT
;
7975 #if JS_HAS_GENERATORS
7976 pn
->pn_blockid
= tc
->blockidGen
;
7979 ts
->flags
|= TSF_OPERAND
;
7980 matched
= js_MatchToken(cx
, ts
, TOK_RB
);
7981 ts
->flags
&= ~TSF_OPERAND
;
7983 for (index
= 0; ; index
++) {
7984 if (index
== JS_ARGS_LENGTH_MAX
) {
7985 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7986 JSMSG_ARRAY_INIT_TOO_BIG
);
7990 ts
->flags
|= TSF_OPERAND
;
7991 tt
= js_PeekToken(cx
, ts
);
7992 ts
->flags
&= ~TSF_OPERAND
;
7994 pn
->pn_xflags
|= PNX_ENDCOMMA
;
7998 if (tt
== TOK_COMMA
) {
7999 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
8000 js_MatchToken(cx
, ts
, TOK_COMMA
);
8001 pn2
= NewParseNode(PN_NULLARY
, tc
);
8002 pn
->pn_xflags
|= PNX_HOLEY
;
8004 pn2
= AssignExpr(cx
, ts
, tc
);
8010 if (tt
!= TOK_COMMA
) {
8011 /* If we didn't already match TOK_COMMA in above case. */
8012 if (!js_MatchToken(cx
, ts
, TOK_COMMA
))
8017 #if JS_HAS_GENERATORS
8019 * At this point, (index == 0 && pn->pn_count != 0) implies one
8020 * element initialiser was parsed.
8022 * An array comprehension of the form:
8024 * [i * j for (i in o) for (j in p) if (i != j)]
8026 * translates to roughly the following let expression:
8028 * let (array = new Array, i, j) {
8029 * for (i in o) let {
8037 * where array is a nameless block-local variable. The "roughly"
8038 * means that an implementation may optimize away the array.push.
8039 * An array comprehension opens exactly one block scope, no matter
8040 * how many for heads it contains.
8042 * Each let () {...} or for (let ...) ... compiles to:
8044 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
8046 * where <o> is a literal object representing the block scope,
8047 * with <n> properties, naming each var declared in the block.
8049 * Each var declaration in a let-block binds a name in <o> at
8050 * compile time, and allocates a slot on the operand stack at
8051 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
8052 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
8053 * JSOP_FORLOCAL. These ops all have an immediate operand, the
8054 * local slot's stack index from fp->spbase.
8056 * The array comprehension iteration step, array.push(i * j) in
8057 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
8058 * where <array> is the index of array's stack slot.
8061 pn
->pn_count
!= 0 &&
8062 js_MatchToken(cx
, ts
, TOK_FOR
)) {
8063 JSParseNode
*pnexp
, *pntop
;
8065 /* Relabel pn as an array comprehension node. */
8066 pn
->pn_type
= TOK_ARRAYCOMP
;
8069 * Remove the comprehension expression from pn's linked list
8070 * and save it via pnexp. We'll re-install it underneath the
8071 * ARRAYPUSH node after we parse the rest of the comprehension.
8074 JS_ASSERT(pn
->pn_count
== 1 || pn
->pn_count
== 2);
8075 pn
->pn_tail
= (--pn
->pn_count
== 1)
8076 ? &pn
->pn_head
->pn_next
8078 *pn
->pn_tail
= NULL
;
8080 pntop
= ComprehensionTail(pnexp
, pn
->pn_blockid
, tc
,
8081 TOK_ARRAYPUSH
, JSOP_ARRAYPUSH
);
8086 #endif /* JS_HAS_GENERATORS */
8088 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_LIST
);
8090 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
8100 * A map from property names we've seen thus far to bit masks.
8101 * (We use ALE_INDEX/ALE_SET_INDEX). An atom's mask includes
8102 * JSPROP_SETTER if we've seen a setter for it, JSPROP_GETTER
8103 * if we've seen as getter, and both of those if we've just
8104 * seen an ordinary value.
8106 JSAutoAtomList
seen(tc
->compiler
);
8108 pn
= NewParseNode(PN_LIST
, tc
);
8111 pn
->pn_type
= TOK_RC
;
8112 pn
->pn_op
= JSOP_NEWINIT
;
8115 afterComma
= JS_FALSE
;
8118 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
8119 tt
= js_GetToken(cx
, ts
);
8120 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
8123 pn3
= NewParseNode(PN_NULLARY
, tc
);
8126 pn3
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
8127 if (tc
->needStrictChecks())
8128 atom
= js_AtomizeDouble(cx
, pn3
->pn_dval
);
8130 atom
= NULL
; /* for the compiler */
8133 #if JS_HAS_GETTER_SETTER
8135 atom
= CURRENT_TOKEN(ts
).t_atom
;
8136 if (atom
== cx
->runtime
->atomState
.getAtom
)
8138 else if (atom
== cx
->runtime
->atomState
.setAtom
)
8143 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
8144 tt
= js_GetToken(cx
, ts
);
8145 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
8146 if (tt
!= TOK_NAME
) {
8150 atom
= CURRENT_TOKEN(ts
).t_atom
;
8151 pn3
= NewNameNode(cx
, atom
, tc
);
8155 /* We have to fake a 'function' token here. */
8156 CURRENT_TOKEN(ts
).t_op
= JSOP_NOP
;
8157 CURRENT_TOKEN(ts
).type
= TOK_FUNCTION
;
8158 pn2
= FunctionExpr(cx
, ts
, tc
);
8159 pn2
= NewBinary(TOK_COLON
, op
, pn3
, pn2
, tc
);
8165 atom
= CURRENT_TOKEN(ts
).t_atom
;
8166 pn3
= NewParseNode(PN_NULLARY
, tc
);
8169 pn3
->pn_atom
= atom
;
8174 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8179 tt
= js_GetToken(cx
, ts
);
8180 #if JS_HAS_GETTER_SETTER
8181 if (tt
== TOK_NAME
) {
8182 tt
= CheckGetterOrSetter(cx
, ts
, TOK_COLON
);
8183 if (tt
== TOK_ERROR
)
8188 if (tt
!= TOK_COLON
) {
8189 #if JS_HAS_DESTRUCTURING_SHORTHAND
8190 if (tt
!= TOK_COMMA
&& tt
!= TOK_RC
) {
8192 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8193 JSMSG_COLON_AFTER_ID
);
8195 #if JS_HAS_DESTRUCTURING_SHORTHAND
8199 * Support, e.g., |var {x, y} = o| as destructuring shorthand
8200 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
8203 pn
->pn_xflags
|= PNX_DESTRUCT
;
8205 if (pnval
->pn_type
== TOK_NAME
) {
8206 pnval
->pn_arity
= PN_NAME
;
8207 InitNameNodeCommon(pnval
, tc
);
8212 op
= CURRENT_TOKEN(ts
).t_op
;
8213 pnval
= AssignExpr(cx
, ts
, tc
);
8216 pn2
= NewBinary(TOK_COLON
, op
, pn3
, pnval
, tc
);
8217 #if JS_HAS_GETTER_SETTER
8225 * In strict mode code, check for duplicate property names. Treat
8226 * getters and setters as distinct attributes of each property. A
8227 * plain old value conflicts with a getter or a setter.
8229 if (tc
->needStrictChecks()) {
8230 unsigned attributesMask
;
8232 attributesMask
= JSPROP_GETTER
| JSPROP_SETTER
;
8233 else if (op
== JSOP_GETTER
)
8234 attributesMask
= JSPROP_GETTER
;
8235 else if (op
== JSOP_SETTER
)
8236 attributesMask
= JSPROP_SETTER
;
8238 JS_NOT_REACHED("bad opcode in object initializer");
8242 JSAtomListElement
*ale
= seen
.lookup(atom
);
8244 if (ALE_INDEX(ale
) & attributesMask
) {
8245 const char *name
= js_AtomToPrintableString(cx
, atom
);
8247 !js_ReportStrictModeError(cx
, ts
, tc
, NULL
,
8248 JSMSG_DUPLICATE_PROPERTY
, name
)) {
8252 ALE_SET_INDEX(ale
, attributesMask
| ALE_INDEX(ale
));
8254 ale
= seen
.add(tc
->compiler
, atom
);
8257 ALE_SET_INDEX(ale
, attributesMask
);
8261 tt
= js_GetToken(cx
, ts
);
8264 if (tt
!= TOK_COMMA
) {
8265 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8266 JSMSG_CURLY_AFTER_LIST
);
8269 afterComma
= JS_TRUE
;
8273 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
8277 #if JS_HAS_BLOCK_SCOPE
8279 pn
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
8285 #if JS_HAS_SHARP_VARS
8287 pn
= NewParseNode(PN_UNARY
, tc
);
8290 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
8291 ts
->flags
|= TSF_OPERAND
;
8292 tt
= js_GetToken(cx
, ts
);
8293 ts
->flags
&= ~TSF_OPERAND
;
8294 if (tt
== TOK_USESHARP
|| tt
== TOK_DEFSHARP
||
8295 #if JS_HAS_XML_SUPPORT
8296 tt
== TOK_STAR
|| tt
== TOK_AT
||
8297 tt
== TOK_XMLSTAGO
/* XXXbe could be sharp? */ ||
8299 tt
== TOK_STRING
|| tt
== TOK_NUMBER
|| tt
== TOK_PRIMARY
) {
8300 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8301 JSMSG_BAD_SHARP_VAR_DEF
);
8304 pn
->pn_kid
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
8307 if (!tc
->ensureSharpSlots())
8312 /* Check for forward/dangling references at runtime, to allow eval. */
8313 pn
= NewParseNode(PN_NULLARY
, tc
);
8316 if (!tc
->ensureSharpSlots())
8318 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
8320 #endif /* JS_HAS_SHARP_VARS */
8326 pn
= ParenExpr(cx
, ts
, tc
, NULL
, &genexp
);
8329 pn
->pn_parens
= true;
8331 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
8335 #if JS_HAS_XML_SUPPORT
8337 pn
= QualifiedIdentifier(cx
, ts
, tc
);
8343 pn
= AttributeIdentifier(cx
, ts
, tc
);
8349 pn
= XMLElementOrListRoot(cx
, ts
, tc
, JS_TRUE
);
8353 #endif /* JS_HAS_XML_SUPPORT */
8356 #if JS_HAS_SHARP_VARS
8360 #if JS_HAS_XML_SUPPORT
8362 case TOK_XMLCOMMENT
:
8365 pn
= NewParseNode(PN_NULLARY
, tc
);
8368 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
8369 #if JS_HAS_XML_SUPPORT
8370 if (tt
== TOK_XMLPI
)
8371 pn
->pn_atom2
= CURRENT_TOKEN(ts
).t_atom2
;
8374 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
8378 pn
= NewNameNode(cx
, CURRENT_TOKEN(ts
).t_atom
, tc
);
8381 JS_ASSERT(CURRENT_TOKEN(ts
).t_op
== JSOP_NAME
);
8382 pn
->pn_op
= JSOP_NAME
;
8384 if ((tc
->flags
& (TCF_IN_FUNCTION
| TCF_FUN_PARAM_ARGUMENTS
)) == TCF_IN_FUNCTION
&&
8385 pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
) {
8387 * Flag arguments usage so we can avoid unsafe optimizations such
8388 * as formal parameter assignment analysis (because of the hated
8389 * feature whereby arguments alias formals). We do this even for
8390 * a reference of the form foo.arguments, which ancient code may
8391 * still use instead of arguments (more hate).
8393 NoteArgumentsUse(tc
);
8396 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8397 * to do this work (new rule for the emitter to count on).
8399 if (!afterDot
&& !(tc
->flags
& TCF_DECL_DESTRUCTURING
) && !tc
->inStatement(STMT_WITH
)) {
8400 pn
->pn_op
= JSOP_ARGUMENTS
;
8401 pn
->pn_dflags
|= PND_BOUND
;
8403 } else if ((!afterDot
8404 #if JS_HAS_XML_SUPPORT
8405 || js_PeekToken(cx
, ts
) == TOK_DBLCOLON
8407 ) && !(tc
->flags
& TCF_DECL_DESTRUCTURING
)) {
8408 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, pn
->pn_atom
, NULL
);
8409 if (!stmt
|| stmt
->type
!= STMT_WITH
) {
8412 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
8415 #if JS_HAS_BLOCK_SCOPE
8417 * Skip out-of-scope let bindings along an ALE list or hash
8418 * chain. These can happen due to |let (x = x) x| block and
8419 * expression bindings, where the x on the right of = comes
8420 * from an outer scope. See bug 496532.
8422 while (dn
->isLet() && !BlockIdInScope(dn
->pn_blockid
, tc
)) {
8424 ale
= ALE_NEXT(ale
);
8425 } while (ale
&& ALE_ATOM(ale
) != pn
->pn_atom
);
8436 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
8441 * No definition before this use in any lexical scope.
8442 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8443 * new node for the forward-referenced definition. This
8444 * placeholder definition node will be adopted when we
8445 * parse the real defining declaration form, or left as
8446 * a free variable definition if we never see the real
8449 ale
= MakePlaceholder(pn
, tc
);
8455 * In case this is a forward reference to a function,
8456 * we pessimistically set PND_FUNARG if the next token
8457 * is not a left parenthesis.
8459 * If the definition eventually parsed into dn is not a
8460 * function, this flag won't hurt, and if we do parse a
8461 * function with pn's name, then the PND_FUNARG flag is
8462 * necessary for safe cx->display-based optimization of
8463 * the closure's static link.
8465 JS_ASSERT(PN_TYPE(dn
) == TOK_NAME
);
8466 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
8467 if (js_PeekToken(cx
, ts
) != TOK_LP
)
8468 dn
->pn_dflags
|= PND_FUNARG
;
8472 JS_ASSERT(dn
->pn_defn
);
8473 LinkUseToDef(pn
, dn
, tc
);
8475 /* Here we handle the backward function reference case. */
8476 if (js_PeekToken(cx
, ts
) != TOK_LP
)
8477 dn
->pn_dflags
|= PND_FUNARG
;
8479 pn
->pn_dflags
|= (dn
->pn_dflags
& PND_FUNARG
);
8483 #if JS_HAS_XML_SUPPORT
8484 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
8489 * Here PrimaryExpr is called after . or .. followed by a name
8490 * followed by ::. This is the only case where a keyword after
8491 * . or .. is not treated as a property name.
8493 str
= ATOM_TO_STRING(pn
->pn_atom
);
8494 tt
= js_CheckKeyword(str
->chars(), str
->length());
8495 if (tt
== TOK_FUNCTION
) {
8496 pn
->pn_arity
= PN_NULLARY
;
8497 pn
->pn_type
= TOK_FUNCTION
;
8498 } else if (tt
!= TOK_EOF
) {
8499 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8500 JSMSG_KEYWORD_NOT_NS
);
8504 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
8515 pn
= NewParseNode(PN_NULLARY
, tc
);
8519 obj
= js_NewRegExpObject(cx
, ts
,
8520 ts
->tokenbuf
.begin(),
8521 ts
->tokenbuf
.length(),
8522 CURRENT_TOKEN(ts
).t_reflags
);
8525 if (!(tc
->flags
& TCF_COMPILE_N_GO
)) {
8526 STOBJ_CLEAR_PARENT(obj
);
8527 STOBJ_CLEAR_PROTO(obj
);
8530 pn
->pn_objbox
= tc
->compiler
->newObjectBox(obj
);
8534 pn
->pn_op
= JSOP_REGEXP
;
8539 pn
= NewParseNode(PN_NULLARY
, tc
);
8542 pn
->pn_op
= JSOP_DOUBLE
;
8543 pn
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
8547 pn
= NewParseNode(PN_NULLARY
, tc
);
8550 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
8554 /* The scanner or one of its subroutines reported the error. */
8558 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8559 JSMSG_SYNTAX_ERROR
);
8565 static JSParseNode
*
8566 ParenExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
8567 JSParseNode
*pn1
, JSBool
*genexp
)
8572 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LP
);
8573 begin
= CURRENT_TOKEN(ts
).pos
.begin
;
8577 pn
= BracketedExpr(cx
, ts
, tc
);
8581 #if JS_HAS_GENERATOR_EXPRS
8582 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
8583 if (pn
->pn_type
== TOK_YIELD
&& !pn
->pn_parens
) {
8584 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
8585 JSMSG_BAD_GENERATOR_SYNTAX
,
8589 if (pn
->pn_type
== TOK_COMMA
&& !pn
->pn_parens
) {
8590 js_ReportCompileErrorNumber(cx
, ts
, pn
->last(), JSREPORT_ERROR
,
8591 JSMSG_BAD_GENERATOR_SYNTAX
,
8596 pn1
= NewParseNode(PN_UNARY
, tc
);
8600 pn
= GeneratorExpr(pn1
, pn
, tc
);
8603 pn
->pn_pos
.begin
= begin
;
8605 if (js_GetToken(cx
, ts
) != TOK_RP
) {
8606 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8607 JSMSG_BAD_GENERATOR_SYNTAX
,
8611 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
8615 #endif /* JS_HAS_GENERATOR_EXPRS */
8621 * Fold from one constant type to another.
8622 * XXX handles only strings and numbers for now
8625 FoldType(JSContext
*cx
, JSParseNode
*pn
, JSTokenType type
)
8627 if (PN_TYPE(pn
) != type
) {
8630 if (pn
->pn_type
== TOK_STRING
) {
8632 if (!JS_ValueToNumber(cx
, ATOM_KEY(pn
->pn_atom
), &d
))
8635 pn
->pn_type
= TOK_NUMBER
;
8636 pn
->pn_op
= JSOP_DOUBLE
;
8641 if (pn
->pn_type
== TOK_NUMBER
) {
8642 JSString
*str
= js_NumberToString(cx
, pn
->pn_dval
);
8645 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8648 pn
->pn_type
= TOK_STRING
;
8649 pn
->pn_op
= JSOP_STRING
;
8660 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8661 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8662 * a successful call to this function.
8665 FoldBinaryNumeric(JSContext
*cx
, JSOp op
, JSParseNode
*pn1
, JSParseNode
*pn2
,
8666 JSParseNode
*pn
, JSTreeContext
*tc
)
8671 JS_ASSERT(pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
);
8677 i
= js_DoubleToECMAInt32(d
);
8678 j
= js_DoubleToECMAInt32(d2
);
8680 d
= (op
== JSOP_LSH
) ? i
<< j
: i
>> j
;
8684 j
= js_DoubleToECMAInt32(d2
);
8686 d
= js_DoubleToECMAUint32(d
) >> j
;
8704 /* XXX MSVC miscompiles such that (NaN == 0) */
8705 if (JSDOUBLE_IS_NaN(d2
))
8709 if (d
== 0 || JSDOUBLE_IS_NaN(d
))
8711 else if (JSDOUBLE_IS_NEG(d
) != JSDOUBLE_IS_NEG(d2
))
8712 d
= js_NegativeInfinity
;
8714 d
= js_PositiveInfinity
;
8731 /* Take care to allow pn1 or pn2 to alias pn. */
8733 RecycleTree(pn1
, tc
);
8735 RecycleTree(pn2
, tc
);
8736 pn
->pn_type
= TOK_NUMBER
;
8737 pn
->pn_op
= JSOP_DOUBLE
;
8738 pn
->pn_arity
= PN_NULLARY
;
8743 #if JS_HAS_XML_SUPPORT
8746 FoldXMLConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
8749 JSParseNode
**pnp
, *pn1
, *pn2
;
8750 JSString
*accum
, *str
;
8752 JSTempValueRooter tvr
;
8754 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
8759 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8760 if (tt
== TOK_XMLETAGO
)
8761 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.etagoAtom
);
8762 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
)
8763 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.stagoAtom
);
8767 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8768 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8769 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8770 * Therefore, we have to add additonal protection from GC nesting under
8773 for (pn2
= pn1
, i
= j
= 0; pn2
; pn2
= pn2
->pn_next
, i
++) {
8774 /* The parser already rejected end-tags with attributes. */
8775 JS_ASSERT(tt
!= TOK_XMLETAGO
|| i
== 0);
8776 switch (pn2
->pn_type
) {
8785 if (pn2
->pn_arity
== PN_LIST
)
8787 str
= ATOM_TO_STRING(pn2
->pn_atom
);
8791 str
= js_MakeXMLCDATAString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8796 case TOK_XMLCOMMENT
:
8797 str
= js_MakeXMLCommentString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8803 str
= js_MakeXMLPIString(cx
, ATOM_TO_STRING(pn2
->pn_atom
),
8804 ATOM_TO_STRING(pn2
->pn_atom2
));
8811 JS_ASSERT(*pnp
== pn1
);
8812 if ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) &&
8813 (i
& 1) ^ (j
& 1)) {
8814 #ifdef DEBUG_brendanXXX
8815 printf("1: %d, %d => ", i
, j
);
8817 js_FileEscapedString(stdout
, accum
, 0);
8819 fputs("NULL", stdout
);
8820 fputc('\n', stdout
);
8822 } else if (accum
&& pn1
!= pn2
) {
8823 while (pn1
->pn_next
!= pn2
) {
8824 pn1
= RecycleTree(pn1
, tc
);
8827 pn1
->pn_type
= TOK_XMLTEXT
;
8828 pn1
->pn_op
= JSOP_STRING
;
8829 pn1
->pn_arity
= PN_NULLARY
;
8830 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8833 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8836 pnp
= &pn2
->pn_next
;
8843 JS_PUSH_TEMP_ROOT_STRING(cx
, accum
, &tvr
);
8844 str
= ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) && i
!= 0)
8845 ? js_AddAttributePart(cx
, i
& 1, accum
, str
)
8846 : js_ConcatStrings(cx
, accum
, str
);
8847 JS_POP_TEMP_ROOT(cx
, &tvr
);
8850 #ifdef DEBUG_brendanXXX
8851 printf("2: %d, %d => ", i
, j
);
8852 js_FileEscapedString(stdout
, str
, 0);
8853 printf(" (%u)\n", str
->length());
8862 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8863 if (tt
== TOK_XMLPTAGC
)
8864 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.ptagcAtom
);
8865 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLETAGO
)
8866 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.tagcAtom
);
8869 accum
= js_ConcatStrings(cx
, accum
, str
);
8874 JS_ASSERT(*pnp
== pn1
);
8875 while (pn1
->pn_next
) {
8876 pn1
= RecycleTree(pn1
, tc
);
8879 pn1
->pn_type
= TOK_XMLTEXT
;
8880 pn1
->pn_op
= JSOP_STRING
;
8881 pn1
->pn_arity
= PN_NULLARY
;
8882 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8885 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8889 if (pn1
&& pn
->pn_count
== 1) {
8891 * Only one node under pn, and it has been folded: move pn1 onto pn
8892 * unless pn is an XML root (in which case we need it to tell the code
8893 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8894 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8895 * extra "<" and "/>" bracketing at runtime.
8897 if (!(pn
->pn_xflags
& PNX_XMLROOT
)) {
8899 } else if (tt
== TOK_XMLPTAGC
) {
8900 pn
->pn_type
= TOK_XMLELEM
;
8901 pn
->pn_op
= JSOP_TOXML
;
8907 #endif /* JS_HAS_XML_SUPPORT */
8910 Boolish(JSParseNode
*pn
)
8912 switch (pn
->pn_op
) {
8914 return pn
->pn_dval
!= 0 && !JSDOUBLE_IS_NaN(pn
->pn_dval
);
8917 return ATOM_TO_STRING(pn
->pn_atom
)->length() != 0;
8919 #if JS_HAS_GENERATOR_EXPRS
8923 * A generator expression as an if or loop condition has no effects, it
8924 * simply results in a truthy object reference. This condition folding
8925 * is needed for the decompiler. See bug 442342 and bug 443074.
8927 if (pn
->pn_count
!= 1)
8929 JSParseNode
*pn2
= pn
->pn_head
;
8930 if (pn2
->pn_type
!= TOK_FUNCTION
)
8932 if (!(pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
))
8954 js_FoldConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, bool inCond
)
8956 JSParseNode
*pn1
= NULL
, *pn2
= NULL
, *pn3
= NULL
;
8958 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
8960 switch (pn
->pn_arity
) {
8963 uint32 oldflags
= tc
->flags
;
8964 JSFunctionBox
*oldlist
= tc
->functionList
;
8966 tc
->flags
= pn
->pn_funbox
->tcflags
;
8967 tc
->functionList
= pn
->pn_funbox
->kids
;
8968 if (!js_FoldConstants(cx
, pn
->pn_body
, tc
))
8970 pn
->pn_funbox
->kids
= tc
->functionList
;
8971 tc
->flags
= oldflags
;
8972 tc
->functionList
= oldlist
;
8978 /* Propagate inCond through logical connectives. */
8979 bool cond
= inCond
&& (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
);
8981 /* Save the list head in pn1 for later use. */
8982 for (pn1
= pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
8983 if (!js_FoldConstants(cx
, pn2
, tc
, cond
))
8990 /* Any kid may be null (e.g. for (;;)). */
8994 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_IF
))
8997 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_FORHEAD
))
8999 if (pn
->pn_type
== TOK_FORHEAD
&& pn2
->pn_op
== JSOP_TRUE
) {
9000 RecycleTree(pn2
, tc
);
9004 if (pn3
&& !js_FoldConstants(cx
, pn3
, tc
))
9012 /* Propagate inCond through logical connectives. */
9013 if (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
) {
9014 if (!js_FoldConstants(cx
, pn1
, tc
, inCond
))
9016 if (!js_FoldConstants(cx
, pn2
, tc
, inCond
))
9021 /* First kid may be null (for default case in switch). */
9022 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_WHILE
))
9024 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_DO
))
9032 * Kludge to deal with typeof expressions: because constant folding
9033 * can turn an expression into a name node, we have to check here,
9034 * before folding, to see if we should throw undefined name errors.
9036 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
9037 * null. This assumption does not hold true for other unary
9040 if (pn
->pn_op
== JSOP_TYPEOF
&& pn1
->pn_type
!= TOK_NAME
)
9041 pn
->pn_op
= JSOP_TYPEOFEXPR
;
9043 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_op
== JSOP_NOT
))
9049 * Skip pn1 down along a chain of dotted member expressions to avoid
9050 * excessive recursion. Our only goal here is to fold constants (if
9051 * any) in the primary expression operand to the left of the first
9056 while (pn1
&& pn1
->pn_arity
== PN_NAME
&& !pn1
->pn_used
)
9058 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
9065 if (!js_FoldConstants(cx
, pn1
, tc
))
9073 switch (pn
->pn_type
) {
9075 if (ContainsStmt(pn2
, TOK_VAR
) || ContainsStmt(pn3
, TOK_VAR
))
9080 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
9081 switch (pn1
->pn_type
) {
9083 if (pn1
->pn_dval
== 0 || JSDOUBLE_IS_NaN(pn1
->pn_dval
))
9087 if (ATOM_TO_STRING(pn1
->pn_atom
)->length() == 0)
9091 if (pn1
->pn_op
== JSOP_TRUE
)
9093 if (pn1
->pn_op
== JSOP_FALSE
|| pn1
->pn_op
== JSOP_NULL
) {
9099 /* Early return to dodge common code that copies pn2 to pn. */
9103 #if JS_HAS_GENERATOR_EXPRS
9104 /* Don't fold a trailing |if (0)| in a generator expression. */
9105 if (!pn2
&& (tc
->flags
& TCF_GENEXP_LAMBDA
))
9109 if (pn2
&& !pn2
->pn_defn
)
9111 if (!pn2
|| (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
)) {
9113 * False condition and no else, or an empty then-statement was
9114 * moved up over pn. Either way, make pn an empty block (not an
9115 * empty statement, which does not decompile, even when labeled).
9116 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
9117 * or an empty statement for a child.
9119 pn
->pn_type
= TOK_LC
;
9120 pn
->pn_arity
= PN_LIST
;
9123 RecycleTree(pn2
, tc
);
9124 if (pn3
&& pn3
!= pn2
)
9125 RecycleTree(pn3
, tc
);
9131 if (pn
->pn_arity
== PN_LIST
) {
9132 JSParseNode
**pnp
= &pn
->pn_head
;
9133 JS_ASSERT(*pnp
== pn1
);
9135 int cond
= Boolish(pn1
);
9136 if (cond
== (pn
->pn_type
== TOK_OR
)) {
9137 for (pn2
= pn1
->pn_next
; pn2
; pn2
= pn3
) {
9139 RecycleTree(pn2
, tc
);
9142 pn1
->pn_next
= NULL
;
9146 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
9147 if (pn
->pn_count
== 1)
9149 *pnp
= pn1
->pn_next
;
9150 RecycleTree(pn1
, tc
);
9153 pnp
= &pn1
->pn_next
;
9155 } while ((pn1
= *pnp
) != NULL
);
9157 // We may have to change arity from LIST to BINARY.
9159 if (pn
->pn_count
== 2) {
9161 pn1
->pn_next
= NULL
;
9162 JS_ASSERT(!pn2
->pn_next
);
9163 pn
->pn_arity
= PN_BINARY
;
9166 } else if (pn
->pn_count
== 1) {
9168 RecycleTree(pn1
, tc
);
9171 int cond
= Boolish(pn1
);
9172 if (cond
== (pn
->pn_type
== TOK_OR
)) {
9173 RecycleTree(pn2
, tc
);
9175 } else if (cond
!= -1) {
9176 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
9177 RecycleTree(pn1
, tc
);
9186 * Compound operators such as *= should be subject to folding, in case
9187 * the left-hand side is constant, and so that the decompiler produces
9188 * the same string that you get from decompiling a script or function
9189 * compiled from that same string. As with +, += is special.
9191 if (pn
->pn_op
== JSOP_NOP
)
9193 if (pn
->pn_op
!= JSOP_ADD
)
9198 if (pn
->pn_arity
== PN_LIST
) {
9199 size_t length
, length2
;
9201 JSString
*str
, *str2
;
9204 * Any string literal term with all others number or string means
9205 * this is a concatenation. If any term is not a string or number
9206 * literal, we can't fold.
9208 JS_ASSERT(pn
->pn_count
> 2);
9209 if (pn
->pn_xflags
& PNX_CANTFOLD
)
9211 if (pn
->pn_xflags
!= PNX_STRCAT
)
9214 /* Ok, we're concatenating: convert non-string constant operands. */
9216 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9217 if (!FoldType(cx
, pn2
, TOK_STRING
))
9219 /* XXX fold only if all operands convert to string */
9220 if (pn2
->pn_type
!= TOK_STRING
)
9222 length
+= ATOM_TO_STRING(pn2
->pn_atom
)->flatLength();
9225 /* Allocate a new buffer and string descriptor for the result. */
9226 chars
= (jschar
*) cx
->malloc((length
+ 1) * sizeof(jschar
));
9229 str
= js_NewString(cx
, chars
, length
);
9235 /* Fill the buffer, advancing chars and recycling kids as we go. */
9236 for (pn2
= pn1
; pn2
; pn2
= RecycleTree(pn2
, tc
)) {
9237 str2
= ATOM_TO_STRING(pn2
->pn_atom
);
9238 length2
= str2
->flatLength();
9239 js_strncpy(chars
, str2
->flatChars(), length2
);
9244 /* Atomize the result string and mutate pn to refer to it. */
9245 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
9248 pn
->pn_type
= TOK_STRING
;
9249 pn
->pn_op
= JSOP_STRING
;
9250 pn
->pn_arity
= PN_NULLARY
;
9254 /* Handle a binary string concatenation. */
9255 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
9256 if (pn1
->pn_type
== TOK_STRING
|| pn2
->pn_type
== TOK_STRING
) {
9257 JSString
*left
, *right
, *str
;
9259 if (!FoldType(cx
, (pn1
->pn_type
!= TOK_STRING
) ? pn1
: pn2
,
9263 if (pn1
->pn_type
!= TOK_STRING
|| pn2
->pn_type
!= TOK_STRING
)
9265 left
= ATOM_TO_STRING(pn1
->pn_atom
);
9266 right
= ATOM_TO_STRING(pn2
->pn_atom
);
9267 str
= js_ConcatStrings(cx
, left
, right
);
9270 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
9273 pn
->pn_type
= TOK_STRING
;
9274 pn
->pn_op
= JSOP_STRING
;
9275 pn
->pn_arity
= PN_NULLARY
;
9276 RecycleTree(pn1
, tc
);
9277 RecycleTree(pn2
, tc
);
9281 /* Can't concatenate string literals, let's try numbers. */
9289 if (pn
->pn_arity
== PN_LIST
) {
9290 JS_ASSERT(pn
->pn_count
> 2);
9291 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9292 if (!FoldType(cx
, pn2
, TOK_NUMBER
))
9295 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9296 /* XXX fold only if all operands convert to number */
9297 if (pn2
->pn_type
!= TOK_NUMBER
)
9301 JSOp op
= PN_OP(pn
);
9305 if (!FoldBinaryNumeric(cx
, op
, pn1
, pn2
, pn
, tc
))
9307 while ((pn2
= pn3
) != NULL
) {
9309 if (!FoldBinaryNumeric(cx
, op
, pn
, pn2
, pn
, tc
))
9314 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
9315 if (!FoldType(cx
, pn1
, TOK_NUMBER
) ||
9316 !FoldType(cx
, pn2
, TOK_NUMBER
)) {
9319 if (pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
) {
9320 if (!FoldBinaryNumeric(cx
, PN_OP(pn
), pn1
, pn2
, pn
, tc
))
9327 if (pn1
->pn_type
== TOK_NUMBER
) {
9330 /* Operate on one numeric constant. */
9332 switch (pn
->pn_op
) {
9334 d
= ~js_DoubleToECMAInt32(d
);
9345 pn
->pn_type
= TOK_PRIMARY
;
9346 pn
->pn_op
= (d
== 0 || JSDOUBLE_IS_NaN(d
)) ? JSOP_TRUE
: JSOP_FALSE
;
9347 pn
->pn_arity
= PN_NULLARY
;
9351 /* Return early to dodge the common TOK_NUMBER code. */
9354 pn
->pn_type
= TOK_NUMBER
;
9355 pn
->pn_op
= JSOP_DOUBLE
;
9356 pn
->pn_arity
= PN_NULLARY
;
9358 RecycleTree(pn1
, tc
);
9359 } else if (pn1
->pn_type
== TOK_PRIMARY
) {
9360 if (pn
->pn_op
== JSOP_NOT
&&
9361 (pn1
->pn_op
== JSOP_TRUE
||
9362 pn1
->pn_op
== JSOP_FALSE
)) {
9364 pn
->pn_op
= (pn
->pn_op
== JSOP_TRUE
) ? JSOP_FALSE
: JSOP_TRUE
;
9365 RecycleTree(pn1
, tc
);
9370 #if JS_HAS_XML_SUPPORT
9377 if (pn
->pn_arity
== PN_LIST
) {
9378 JS_ASSERT(pn
->pn_type
== TOK_XMLLIST
|| pn
->pn_count
!= 0);
9379 if (!FoldXMLConstants(cx
, pn
, tc
))
9385 if (pn1
->pn_type
== TOK_XMLNAME
) {
9387 JSObjectBox
*xmlbox
;
9389 v
= ATOM_KEY(pn1
->pn_atom
);
9390 if (!js_ToAttributeName(cx
, &v
))
9392 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
9394 xmlbox
= tc
->compiler
->newObjectBox(JSVAL_TO_OBJECT(v
));
9398 pn
->pn_type
= TOK_XMLNAME
;
9399 pn
->pn_op
= JSOP_OBJECT
;
9400 pn
->pn_arity
= PN_NULLARY
;
9401 pn
->pn_objbox
= xmlbox
;
9402 RecycleTree(pn1
, tc
);
9405 #endif /* JS_HAS_XML_SUPPORT */
9411 int cond
= Boolish(pn
);
9413 switch (pn
->pn_arity
) {
9418 RecycleTree(pn2
, tc
);
9419 } while ((pn2
= pn3
) != NULL
);
9422 RecycleFuncNameKids(pn
, tc
);
9427 JS_NOT_REACHED("unhandled arity");
9429 pn
->pn_type
= TOK_PRIMARY
;
9430 pn
->pn_op
= cond
? JSOP_TRUE
: JSOP_FALSE
;
9431 pn
->pn_arity
= PN_NULLARY
;