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"
82 #if JS_HAS_XML_SUPPORT
86 #if JS_HAS_DESTRUCTURING
91 * Asserts to verify assumptions behind pn_ macros.
93 #define pn_offsetof(m) offsetof(JSParseNode, m)
95 JS_STATIC_ASSERT(pn_offsetof(pn_link
) == pn_offsetof(dn_uses
));
96 JS_STATIC_ASSERT(pn_offsetof(pn_u
.name
.atom
) == pn_offsetof(pn_u
.apair
.atom
));
101 * JS parsers, from lowest to highest precedence.
103 * Each parser takes a context, a token stream, and a tree context struct.
104 * Each returns a parse node tree or null on error.
107 typedef JSParseNode
*
108 JSParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
);
110 typedef JSParseNode
*
111 JSVariablesParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
114 typedef JSParseNode
*
115 JSMemberParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
116 JSBool allowCallSyntax
);
118 typedef JSParseNode
*
119 JSPrimaryParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
120 JSTokenType tt
, JSBool afterDot
);
122 typedef JSParseNode
*
123 JSParenParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
124 JSParseNode
*pn1
, JSBool
*genexp
);
126 static JSParser FunctionStmt
;
127 static JSParser FunctionExpr
;
128 static JSParser Statements
;
129 static JSParser Statement
;
130 static JSVariablesParser Variables
;
131 static JSParser Expr
;
132 static JSParser AssignExpr
;
133 static JSParser CondExpr
;
134 static JSParser OrExpr
;
135 static JSParser AndExpr
;
136 static JSParser BitOrExpr
;
137 static JSParser BitXorExpr
;
138 static JSParser BitAndExpr
;
139 static JSParser EqExpr
;
140 static JSParser RelExpr
;
141 static JSParser ShiftExpr
;
142 static JSParser AddExpr
;
143 static JSParser MulExpr
;
144 static JSParser UnaryExpr
;
145 static JSMemberParser MemberExpr
;
146 static JSPrimaryParser PrimaryExpr
;
147 static JSParenParser ParenExpr
;
150 * Insist that the next token be of type tt, or report errno and return null.
151 * NB: this macro uses cx and ts from its lexical environment.
153 #define MUST_MATCH_TOKEN(tt, errno) \
155 if (js_GetToken(cx, ts) != tt) { \
156 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
161 #ifdef METER_PARSENODES
162 static uint32 parsenodes
= 0;
163 static uint32 maxparsenodes
= 0;
164 static uint32 recyclednodes
= 0;
168 JSParseNode::become(JSParseNode
*pn2
)
171 JS_ASSERT(!pn2
->pn_defn
);
175 JSParseNode
**pnup
= &pn2
->pn_lexdef
->dn_uses
;
177 pnup
= &(*pnup
)->pn_link
;
179 pn_link
= pn2
->pn_link
;
182 pn2
->pn_used
= false;
185 /* If this is a function node fix up the pn_funbox->node back-pointer. */
186 if (PN_TYPE(pn2
) == TOK_FUNCTION
&& pn2
->pn_arity
== PN_FUNC
)
187 pn2
->pn_funbox
->node
= this;
189 pn_type
= pn2
->pn_type
;
191 pn_arity
= pn2
->pn_arity
;
201 pn_used
= pn_defn
= false;
202 pn_arity
= PN_NULLARY
;
206 JSCompiler::init(const jschar
*base
, size_t length
,
207 FILE *fp
, const char *filename
, uintN lineno
)
209 JSContext
*cx
= context
;
211 tempPoolMark
= JS_ARENA_MARK(&cx
->tempPool
);
212 if (!js_InitTokenStream(cx
, TS(this), base
, length
, fp
, filename
, lineno
)) {
213 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
217 /* Root atoms and objects allocated for the parsed tree. */
218 JS_KEEP_ATOMS(cx
->runtime
);
219 JS_PUSH_TEMP_ROOT_COMPILER(cx
, this, &tempRoot
);
223 JSCompiler::~JSCompiler()
225 JSContext
*cx
= context
;
228 JSPRINCIPALS_DROP(cx
, principals
);
229 JS_ASSERT(tempRoot
.u
.compiler
== this);
230 JS_POP_TEMP_ROOT(cx
, &tempRoot
);
231 JS_UNKEEP_ATOMS(cx
->runtime
);
232 js_CloseTokenStream(cx
, TS(this));
233 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
237 JSCompiler::setPrincipals(JSPrincipals
*prin
)
239 JS_ASSERT(!principals
);
241 JSPRINCIPALS_HOLD(context
, prin
);
246 JSCompiler::newObjectBox(JSObject
*obj
)
251 * We use JSContext.tempPool to allocate parsed objects and place them on
252 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
253 * containing the entries must be alive until we are done with scanning,
254 * parsing and code generation for the whole script or top-level function.
257 JS_ARENA_ALLOCATE_TYPE(objbox
, JSObjectBox
, &context
->tempPool
);
259 js_ReportOutOfScriptQuota(context
);
262 objbox
->traceLink
= traceListHead
;
263 traceListHead
= objbox
;
264 objbox
->emitLink
= NULL
;
265 objbox
->object
= obj
;
270 JSCompiler::newFunctionBox(JSObject
*obj
, JSParseNode
*fn
, JSTreeContext
*tc
)
273 JS_ASSERT(HAS_FUNCTION_CLASS(obj
));
276 * We use JSContext.tempPool to allocate parsed objects and place them on
277 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
278 * containing the entries must be alive until we are done with scanning,
279 * parsing and code generation for the whole script or top-level function.
281 JSFunctionBox
*funbox
;
282 JS_ARENA_ALLOCATE_TYPE(funbox
, JSFunctionBox
, &context
->tempPool
);
284 js_ReportOutOfScriptQuota(context
);
287 funbox
->traceLink
= traceListHead
;
288 traceListHead
= funbox
;
289 funbox
->emitLink
= NULL
;
290 funbox
->object
= obj
;
292 funbox
->siblings
= tc
->functionList
;
293 tc
->functionList
= funbox
;
294 ++tc
->compiler
->functionCount
;
296 funbox
->parent
= tc
->funbox
;
297 funbox
->queued
= false;
298 funbox
->inLoop
= false;
299 for (JSStmtInfo
*stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
300 if (STMT_IS_LOOP(stmt
)) {
301 funbox
->inLoop
= true;
305 funbox
->level
= tc
->staticLevel
;
306 funbox
->tcflags
= TCF_IN_FUNCTION
| (tc
->flags
& TCF_COMPILE_N_GO
);
311 JSCompiler::trace(JSTracer
*trc
)
315 JS_ASSERT(tempRoot
.u
.compiler
== this);
316 objbox
= traceListHead
;
318 JS_CALL_OBJECT_TRACER(trc
, objbox
->object
, "parser.object");
319 objbox
= objbox
->traceLink
;
324 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
);
327 UnlinkFunctionBox(JSParseNode
*pn
, JSTreeContext
*tc
)
329 JSFunctionBox
*funbox
= pn
->pn_funbox
;
331 JS_ASSERT(funbox
->node
== pn
);
334 JSFunctionBox
**funboxp
= &tc
->functionList
;
336 if (*funboxp
== funbox
) {
337 *funboxp
= funbox
->siblings
;
340 funboxp
= &(*funboxp
)->siblings
;
343 uint16 oldflags
= tc
->flags
;
344 JSFunctionBox
*oldlist
= tc
->functionList
;
346 tc
->flags
= (uint16
) funbox
->tcflags
;
347 tc
->functionList
= funbox
->kids
;
348 UnlinkFunctionBoxes(pn
->pn_body
, tc
);
349 funbox
->kids
= tc
->functionList
;
350 tc
->flags
= oldflags
;
351 tc
->functionList
= oldlist
;
353 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
354 pn
->pn_funbox
= NULL
;
359 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
)
362 switch (pn
->pn_arity
) {
366 UnlinkFunctionBoxes(pn
->pn_kid
, tc
);
369 UnlinkFunctionBoxes(pn
->pn_left
, tc
);
370 UnlinkFunctionBoxes(pn
->pn_right
, tc
);
373 UnlinkFunctionBoxes(pn
->pn_kid1
, tc
);
374 UnlinkFunctionBoxes(pn
->pn_kid2
, tc
);
375 UnlinkFunctionBoxes(pn
->pn_kid3
, tc
);
378 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
379 UnlinkFunctionBoxes(pn2
, tc
);
382 UnlinkFunctionBox(pn
, tc
);
385 UnlinkFunctionBoxes(pn
->maybeExpr(), tc
);
388 UnlinkFunctionBoxes(pn
->pn_tree
, tc
);
394 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
);
397 RecycleTree(JSParseNode
*pn
, JSTreeContext
*tc
)
399 JSParseNode
*next
, **head
;
404 /* Catch back-to-back dup recycles. */
405 JS_ASSERT(pn
!= tc
->compiler
->nodeList
);
407 if (pn
->pn_used
|| pn
->pn_defn
) {
409 * JSAtomLists own definition nodes along with their used-node chains.
410 * Defer recycling such nodes until we unwind to top level to avoid
411 * linkage overhead or (alternatively) unlinking runtime complexity.
412 * Yes, this means dead code can contribute to static analysis results!
414 * Do recycle kids here, since they are no longer needed.
417 RecycleFuncNameKids(pn
, tc
);
419 UnlinkFunctionBoxes(pn
, tc
);
420 head
= &tc
->compiler
->nodeList
;
423 #ifdef METER_PARSENODES
431 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
)
433 switch (pn
->pn_arity
) {
435 UnlinkFunctionBox(pn
, tc
);
440 * Only a definition node might have a non-null strong pn_expr link
441 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
442 * Every node with the pn_used flag set has a non-null pn_lexdef
443 * weak reference to its definition node.
445 if (!pn
->pn_used
&& pn
->pn_expr
) {
446 RecycleTree(pn
->pn_expr
, tc
);
452 JS_ASSERT(PN_TYPE(pn
) == TOK_FUNCTION
);
457 NewOrRecycledNode(JSTreeContext
*tc
)
459 JSParseNode
*pn
, *pn2
;
461 pn
= tc
->compiler
->nodeList
;
463 JSContext
*cx
= tc
->compiler
->context
;
465 JS_ARENA_ALLOCATE_TYPE(pn
, JSParseNode
, &cx
->tempPool
);
467 js_ReportOutOfScriptQuota(cx
);
469 tc
->compiler
->nodeList
= pn
->pn_next
;
471 /* Recycle immediate descendents only, to save work and working set. */
472 switch (pn
->pn_arity
) {
474 RecycleTree(pn
->pn_body
, tc
);
479 while (pn2
&& !pn2
->pn_used
&& !pn2
->pn_defn
)
484 pn2
= RecycleTree(pn2
, tc
);
487 *pn
->pn_tail
= tc
->compiler
->nodeList
;
488 tc
->compiler
->nodeList
= pn
->pn_head
;
489 #ifdef METER_PARSENODES
490 recyclednodes
+= pn
->pn_count
;
497 RecycleTree(pn
->pn_kid1
, tc
);
498 RecycleTree(pn
->pn_kid2
, tc
);
499 RecycleTree(pn
->pn_kid3
, tc
);
502 if (pn
->pn_left
!= pn
->pn_right
)
503 RecycleTree(pn
->pn_left
, tc
);
504 RecycleTree(pn
->pn_right
, tc
);
507 RecycleTree(pn
->pn_kid
, tc
);
511 RecycleTree(pn
->pn_expr
, tc
);
518 #ifdef METER_PARSENODES
520 if (parsenodes
- recyclednodes
> maxparsenodes
)
521 maxparsenodes
= parsenodes
- recyclednodes
;
523 pn
->pn_used
= pn
->pn_defn
= false;
524 memset(&pn
->pn_u
, 0, sizeof pn
->pn_u
);
531 InitParseNode(JSParseNode
*pn
, JSTokenType type
, JSOp op
, JSParseNodeArity arity
)
535 pn
->pn_arity
= arity
;
536 JS_ASSERT(!pn
->pn_used
);
537 JS_ASSERT(!pn
->pn_defn
);
538 pn
->pn_next
= pn
->pn_link
= NULL
;
542 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
546 NewParseNode(JSParseNodeArity arity
, JSTreeContext
*tc
)
551 pn
= NewOrRecycledNode(tc
);
554 tp
= &CURRENT_TOKEN(&tc
->compiler
->tokenStream
);
555 InitParseNode(pn
, tp
->type
, JSOP_NOP
, arity
);
556 pn
->pn_pos
= tp
->pos
;
561 InitNameNodeCommon(JSParseNode
*pn
, JSTreeContext
*tc
)
564 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
565 pn
->pn_dflags
= tc
->atTopLevel() ? PND_TOPLEVEL
: 0;
566 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
567 pn
->pn_dflags
|= PND_BLOCKCHILD
;
568 pn
->pn_blockid
= tc
->blockid();
572 NewNameNode(JSContext
*cx
, JSTokenStream
*ts
, JSAtom
*atom
, JSTreeContext
*tc
)
576 pn
= NewParseNode(PN_NAME
, tc
);
579 InitNameNodeCommon(pn
, tc
);
585 NewBinary(JSTokenType tt
, JSOp op
, JSParseNode
*left
, JSParseNode
*right
,
588 JSParseNode
*pn
, *pn1
, *pn2
;
594 * Flatten a left-associative (left-heavy) tree of a given operator into
595 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
597 if (PN_TYPE(left
) == tt
&&
599 (js_CodeSpec
[op
].format
& JOF_LEFTASSOC
)) {
600 if (left
->pn_arity
!= PN_LIST
) {
601 pn1
= left
->pn_left
, pn2
= left
->pn_right
;
602 left
->pn_arity
= PN_LIST
;
605 if (tt
== TOK_PLUS
) {
606 if (pn1
->pn_type
== TOK_STRING
)
607 left
->pn_xflags
|= PNX_STRCAT
;
608 else if (pn1
->pn_type
!= TOK_NUMBER
)
609 left
->pn_xflags
|= PNX_CANTFOLD
;
610 if (pn2
->pn_type
== TOK_STRING
)
611 left
->pn_xflags
|= PNX_STRCAT
;
612 else if (pn2
->pn_type
!= TOK_NUMBER
)
613 left
->pn_xflags
|= PNX_CANTFOLD
;
617 left
->pn_pos
.end
= right
->pn_pos
.end
;
618 if (tt
== TOK_PLUS
) {
619 if (right
->pn_type
== TOK_STRING
)
620 left
->pn_xflags
|= PNX_STRCAT
;
621 else if (right
->pn_type
!= TOK_NUMBER
)
622 left
->pn_xflags
|= PNX_CANTFOLD
;
628 * Fold constant addition immediately, to conserve node space and, what's
629 * more, so js_FoldConstants never sees mixed addition and concatenation
630 * operations with more than one leading non-string operand in a PN_LIST
631 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
632 * to "3pt", not "12pt").
634 if (tt
== TOK_PLUS
&&
635 left
->pn_type
== TOK_NUMBER
&&
636 right
->pn_type
== TOK_NUMBER
) {
637 left
->pn_dval
+= right
->pn_dval
;
638 left
->pn_pos
.end
= right
->pn_pos
.end
;
639 RecycleTree(right
, tc
);
643 pn
= NewOrRecycledNode(tc
);
646 InitParseNode(pn
, tt
, op
, PN_BINARY
);
647 pn
->pn_pos
.begin
= left
->pn_pos
.begin
;
648 pn
->pn_pos
.end
= right
->pn_pos
.end
;
650 pn
->pn_right
= right
;
654 #if JS_HAS_GETTER_SETTER
656 CheckGetterOrSetter(JSContext
*cx
, JSTokenStream
*ts
, JSTokenType tt
)
663 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_NAME
);
664 atom
= CURRENT_TOKEN(ts
).t_atom
;
666 if (atom
== rt
->atomState
.getterAtom
)
668 else if (atom
== rt
->atomState
.setterAtom
)
672 if (js_PeekTokenSameLine(cx
, ts
) != tt
)
674 (void) js_GetToken(cx
, ts
);
675 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
676 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
677 JSMSG_BAD_GETTER_OR_SETTER
,
683 CURRENT_TOKEN(ts
).t_op
= op
;
684 if (JS_HAS_STRICT_OPTION(cx
)) {
685 name
= js_AtomToPrintableString(cx
, atom
);
687 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
688 JSREPORT_WARNING
| JSREPORT_STRICT
,
689 JSMSG_DEPRECATED_USAGE
,
699 GenerateBlockId(JSTreeContext
*tc
, uint32
& blockid
)
701 if (tc
->blockidGen
== JS_BIT(20)) {
702 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
703 JSMSG_NEED_DIET
, "program");
706 blockid
= tc
->blockidGen
++;
711 GenerateBlockIdForStmtNode(JSParseNode
*pn
, JSTreeContext
*tc
)
713 JS_ASSERT(tc
->topStmt
);
714 JS_ASSERT(STMT_MAYBE_SCOPE(tc
->topStmt
));
715 JS_ASSERT(pn
->pn_type
== TOK_LC
|| pn
->pn_type
== TOK_LEXICALSCOPE
);
716 if (!GenerateBlockId(tc
, tc
->topStmt
->blockid
))
718 pn
->pn_blockid
= tc
->topStmt
->blockid
;
723 * Parse a top-level JS script.
726 JSCompiler::parse(JSObject
*chain
)
729 * Protect atoms from being collected by a GC activation, which might
730 * - nest on this thread due to out of memory (the so-called "last ditch"
731 * GC attempted within js_NewGCThing), or
732 * - run for any reason on another thread if this thread is suspended on
733 * an object lock before it finishes generating bytecode into a script
734 * protected from the GC by a root or a stack frame reference.
736 JSTreeContext
tc(this);
737 tc
.scopeChain
= chain
;
738 if (!GenerateBlockId(&tc
, tc
.bodyid
))
741 JSParseNode
*pn
= Statements(context
, TS(this), &tc
);
743 if (!js_MatchToken(context
, TS(this), TOK_EOF
)) {
744 js_ReportCompileErrorNumber(context
, TS(this), NULL
, JSREPORT_ERROR
,
748 if (!js_FoldConstants(context
, pn
, &tc
))
755 JS_STATIC_ASSERT(FREE_STATIC_LEVEL
== JS_BITMASK(JSFB_LEVEL_BITS
));
758 SetStaticLevel(JSTreeContext
*tc
, uintN staticLevel
)
761 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
762 * (0xffffffff) and other cookies with that level.
764 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
765 * practically speaking it leaves more than enough room for upvars. In fact
766 * we might want to split cookie fields giving fewer bits for skip and more
767 * for slot, but only based on evidence.
769 if (staticLevel
>= FREE_STATIC_LEVEL
) {
770 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
771 JSMSG_TOO_DEEP
, js_function_str
);
774 tc
->staticLevel
= staticLevel
;
779 * Compile a top-level script.
782 JSCompiler::compileScript(JSContext
*cx
, JSObject
*scopeChain
, JSStackFrame
*callerFrame
,
783 JSPrincipals
*principals
, uint32 tcflags
,
784 const jschar
*chars
, size_t length
,
785 FILE *file
, const char *filename
, uintN lineno
,
786 JSString
*source
/* = NULL */)
788 JSCompiler
jsc(cx
, principals
, callerFrame
);
789 JSArenaPool codePool
, notePool
;
792 uint32 scriptGlobals
;
794 #ifdef METER_PARSENODES
795 void *sbrk(ptrdiff_t), *before
= sbrk(0);
798 JS_ASSERT(!(tcflags
& ~(TCF_COMPILE_N_GO
| TCF_NO_SCRIPT_RVAL
|
799 TCF_STATIC_LEVEL_MASK
)));
802 * The scripted callerFrame can only be given for compile-and-go scripts
803 * and non-zero static level requires callerFrame.
805 JS_ASSERT_IF(callerFrame
, tcflags
& TCF_COMPILE_N_GO
);
806 JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags
) != 0, callerFrame
);
808 if (!jsc
.init(chars
, length
, file
, filename
, lineno
))
811 JS_INIT_ARENA_POOL(&codePool
, "code", 1024, sizeof(jsbytecode
),
812 &cx
->scriptStackQuota
);
813 JS_INIT_ARENA_POOL(¬ePool
, "note", 1024, sizeof(jssrcnote
),
814 &cx
->scriptStackQuota
);
816 JSCodeGenerator
cg(&jsc
, &codePool
, ¬ePool
, jsc
.tokenStream
.lineno
);
818 MUST_FLOW_THROUGH("out");
820 /* Null script early in case of error, to reduce our code footprint. */
823 cg
.flags
|= uint16(tcflags
);
824 cg
.scopeChain
= scopeChain
;
825 if (!SetStaticLevel(&cg
, TCF_GET_STATIC_LEVEL(tcflags
)))
829 * If funbox is non-null after we create the new script, callerFrame->fun
830 * was saved in the 0th object table entry.
835 if (tcflags
& TCF_COMPILE_N_GO
) {
838 * Save eval program source in script->atomMap.vector[0] for the
839 * eval cache (see obj_eval in jsobj.cpp).
841 JSAtom
*atom
= js_AtomizeString(cx
, source
, 0);
842 if (!atom
|| !cg
.atomList
.add(&jsc
, atom
))
846 if (callerFrame
&& callerFrame
->fun
) {
848 * An eval script in a caller frame needs to have its enclosing
849 * function captured in case it refers to an upvar, and someone
850 * wishes to decompile it while it's running.
852 funbox
= jsc
.newObjectBox(FUN_OBJECT(callerFrame
->fun
));
855 funbox
->emitLink
= cg
.objectList
.lastbox
;
856 cg
.objectList
.lastbox
= funbox
;
857 cg
.objectList
.length
++;
862 * Inline Statements to emit as we go to save AST space. We must generate
863 * our script-body blockid since we aren't calling Statements.
866 if (!GenerateBlockId(&cg
, bodyid
))
870 #if JS_HAS_XML_SUPPORT
877 jsc
.tokenStream
.flags
|= TSF_OPERAND
;
878 tt
= js_PeekToken(cx
, &jsc
.tokenStream
);
879 jsc
.tokenStream
.flags
&= ~TSF_OPERAND
;
883 JS_ASSERT(tt
== TOK_ERROR
);
887 pn
= Statement(cx
, &jsc
.tokenStream
, &cg
);
890 JS_ASSERT(!cg
.blockNode
);
892 if (!js_FoldConstants(cx
, pn
, &cg
))
895 if (cg
.functionList
) {
896 if (!jsc
.analyzeFunctions(cg
.functionList
, cg
.flags
))
898 cg
.functionList
= NULL
;
901 if (!js_EmitTree(cx
, &cg
, pn
))
903 #if JS_HAS_XML_SUPPORT
904 if (PN_TYPE(pn
) != TOK_SEMI
||
906 !TREE_TYPE_IS_XML(PN_TYPE(pn
->pn_kid
))) {
910 RecycleTree(pn
, &cg
);
913 #if JS_HAS_XML_SUPPORT
915 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
916 * For background, see:
918 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
920 if (pn
&& onlyXML
&& (tcflags
& TCF_NO_SCRIPT_RVAL
)) {
921 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
, JSREPORT_ERROR
,
922 JSMSG_XML_WHOLE_PROGRAM
);
928 * Global variables and regexps share the index space with locals. Due to
929 * incremental code generation we need to patch the bytecode to adjust the
930 * local references to skip the globals.
932 scriptGlobals
= cg
.ngvars
+ cg
.regexpList
.length
;
933 if (scriptGlobals
!= 0) {
934 jsbytecode
*code
, *end
;
936 const JSCodeSpec
*cs
;
939 if (scriptGlobals
>= SLOTNO_LIMIT
)
942 for (end
= code
+ CG_OFFSET(&cg
); code
!= end
; code
+= len
) {
943 JS_ASSERT(code
< end
);
945 cs
= &js_CodeSpec
[op
];
946 len
= (cs
->length
> 0)
948 : js_GetVariableBytecodeLength(code
);
949 if (JOF_TYPE(cs
->format
) == JOF_LOCAL
||
950 (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
)) {
952 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
953 * emitted only for a function.
955 JS_ASSERT((JOF_TYPE(cs
->format
) == JOF_SLOTATOM
) ==
956 (op
== JSOP_GETLOCALPROP
));
957 slot
= GET_SLOTNO(code
);
958 slot
+= scriptGlobals
;
959 if (slot
>= SLOTNO_LIMIT
)
961 SET_SLOTNO(code
, slot
);
966 #ifdef METER_PARSENODES
967 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
968 (char *)sbrk(0) - (char *)before
,
971 parsenodes
- recyclednodes
);
976 * Nowadays the threaded interpreter needs a stop instruction, so we
977 * do have to emit that here.
979 if (js_Emit1(cx
, &cg
, JSOP_STOP
) < 0)
981 #ifdef METER_PARSENODES
982 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
983 (char *)sbrk(0) - (char *)before
, CG_OFFSET(&cg
), cg
.noteCount
);
986 JS_DumpArenaStats(stdout
);
988 script
= js_NewScriptFromCG(cx
, &cg
);
989 if (script
&& funbox
)
990 script
->flags
|= JSSF_SAVED_CALLER_FUN
;
992 #ifdef JS_SCOPE_DEPTH_METER
994 JSObject
*obj
= scopeChain
;
996 while ((obj
= OBJ_GET_PARENT(cx
, obj
)) != NULL
)
998 JS_BASIC_STATS_ACCUM(&cx
->runtime
->hostenvScopeDepthStats
, depth
);
1003 JS_FinishArenaPool(&codePool
);
1004 JS_FinishArenaPool(¬ePool
);
1008 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
,
1009 JSREPORT_ERROR
, JSMSG_TOO_MANY_LOCALS
);
1015 * Insist on a final return before control flows out of pn. Try to be a bit
1016 * smart about loops: do {...; return e2;} while(0) at the end of a function
1017 * that contains an early return e1 will get a strict warning. Similarly for
1018 * iloops: while (true){...} is treated as though ... returns.
1020 #define ENDS_IN_OTHER 0
1021 #define ENDS_IN_RETURN 1
1022 #define ENDS_IN_BREAK 2
1025 HasFinalReturn(JSParseNode
*pn
)
1027 JSParseNode
*pn2
, *pn3
;
1028 uintN rv
, rv2
, hasDefault
;
1030 switch (pn
->pn_type
) {
1033 return ENDS_IN_OTHER
;
1034 return HasFinalReturn(pn
->last());
1038 return ENDS_IN_OTHER
;
1039 return HasFinalReturn(pn
->pn_kid2
) & HasFinalReturn(pn
->pn_kid3
);
1043 if (pn2
->pn_type
== TOK_PRIMARY
&& pn2
->pn_op
== JSOP_TRUE
)
1044 return ENDS_IN_RETURN
;
1045 if (pn2
->pn_type
== TOK_NUMBER
&& pn2
->pn_dval
)
1046 return ENDS_IN_RETURN
;
1047 return ENDS_IN_OTHER
;
1051 if (pn2
->pn_type
== TOK_PRIMARY
) {
1052 if (pn2
->pn_op
== JSOP_FALSE
)
1053 return HasFinalReturn(pn
->pn_left
);
1054 if (pn2
->pn_op
== JSOP_TRUE
)
1055 return ENDS_IN_RETURN
;
1057 if (pn2
->pn_type
== TOK_NUMBER
) {
1058 if (pn2
->pn_dval
== 0)
1059 return HasFinalReturn(pn
->pn_left
);
1060 return ENDS_IN_RETURN
;
1062 return ENDS_IN_OTHER
;
1066 if (pn2
->pn_arity
== PN_TERNARY
&& !pn2
->pn_kid2
)
1067 return ENDS_IN_RETURN
;
1068 return ENDS_IN_OTHER
;
1071 rv
= ENDS_IN_RETURN
;
1072 hasDefault
= ENDS_IN_OTHER
;
1074 if (pn2
->pn_type
== TOK_LEXICALSCOPE
)
1076 for (pn2
= pn2
->pn_head
; rv
&& pn2
; pn2
= pn2
->pn_next
) {
1077 if (pn2
->pn_type
== TOK_DEFAULT
)
1078 hasDefault
= ENDS_IN_RETURN
;
1079 pn3
= pn2
->pn_right
;
1080 JS_ASSERT(pn3
->pn_type
== TOK_LC
);
1082 rv2
= HasFinalReturn(pn3
->last());
1083 if (rv2
== ENDS_IN_OTHER
&& pn2
->pn_next
)
1084 /* Falling through to next case or default. */;
1089 /* If a final switch has no default case, we judge it harshly. */
1094 return ENDS_IN_BREAK
;
1097 return HasFinalReturn(pn
->pn_right
);
1100 return ENDS_IN_RETURN
;
1103 case TOK_LEXICALSCOPE
:
1104 return HasFinalReturn(pn
->expr());
1107 return ENDS_IN_RETURN
;
1110 /* If we have a finally block that returns, we are done. */
1112 rv
= HasFinalReturn(pn
->pn_kid3
);
1113 if (rv
== ENDS_IN_RETURN
)
1117 /* Else check the try block and any and all catch statements. */
1118 rv
= HasFinalReturn(pn
->pn_kid1
);
1120 JS_ASSERT(pn
->pn_kid2
->pn_arity
== PN_LIST
);
1121 for (pn2
= pn
->pn_kid2
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
1122 rv
&= HasFinalReturn(pn2
);
1127 /* Check this catch block's body. */
1128 return HasFinalReturn(pn
->pn_kid3
);
1131 /* Non-binary let statements are let declarations. */
1132 if (pn
->pn_arity
!= PN_BINARY
)
1133 return ENDS_IN_OTHER
;
1134 return HasFinalReturn(pn
->pn_right
);
1137 return ENDS_IN_OTHER
;
1142 ReportBadReturn(JSContext
*cx
, JSTreeContext
*tc
, uintN flags
, uintN errnum
,
1147 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1148 if (tc
->fun
->atom
) {
1149 name
= js_AtomToPrintableString(cx
, tc
->fun
->atom
);
1151 errnum
= anonerrnum
;
1154 return js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
, flags
,
1159 CheckFinalReturn(JSContext
*cx
, JSTreeContext
*tc
, JSParseNode
*pn
)
1161 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1162 return HasFinalReturn(pn
) == ENDS_IN_RETURN
||
1163 ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
1164 JSMSG_NO_RETURN_VALUE
, JSMSG_ANON_NO_RETURN_VALUE
);
1167 static JSParseNode
*
1168 FunctionBody(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1170 JSStmtInfo stmtInfo
;
1171 uintN oldflags
, firstLine
;
1174 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1175 js_PushStatement(tc
, &stmtInfo
, STMT_BLOCK
, -1);
1176 stmtInfo
.flags
= SIF_BODY_BLOCK
;
1178 oldflags
= tc
->flags
;
1179 tc
->flags
&= ~(TCF_RETURN_EXPR
| TCF_RETURN_VOID
);
1182 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1183 * later, because we may have not peeked in ts yet, so Statements won't
1184 * acquire a valid pn->pn_pos.begin from the current token.
1186 firstLine
= ts
->lineno
;
1187 #if JS_HAS_EXPR_CLOSURES
1188 if (CURRENT_TOKEN(ts
).type
== TOK_LC
) {
1189 pn
= Statements(cx
, ts
, tc
);
1191 pn
= NewParseNode(PN_UNARY
, tc
);
1193 pn
->pn_kid
= AssignExpr(cx
, ts
, tc
);
1197 if (tc
->flags
& TCF_FUN_IS_GENERATOR
) {
1198 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
1199 JSMSG_BAD_GENERATOR_RETURN
,
1200 JSMSG_BAD_ANON_GENERATOR_RETURN
);
1203 pn
->pn_type
= TOK_RETURN
;
1204 pn
->pn_op
= JSOP_RETURN
;
1205 pn
->pn_pos
.end
= pn
->pn_kid
->pn_pos
.end
;
1211 pn
= Statements(cx
, ts
, tc
);
1215 JS_ASSERT(!(tc
->topStmt
->flags
& SIF_SCOPE
));
1216 js_PopStatement(tc
);
1217 pn
->pn_pos
.begin
.lineno
= firstLine
;
1219 /* Check for falling off the end of a function that returns a value. */
1220 if (JS_HAS_STRICT_OPTION(cx
) && (tc
->flags
& TCF_RETURN_EXPR
) &&
1221 !CheckFinalReturn(cx
, tc
, pn
)) {
1226 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
1230 static JSAtomListElement
*
1231 MakePlaceholder(JSParseNode
*pn
, JSTreeContext
*tc
)
1233 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->compiler
, pn
->pn_atom
);
1237 JSDefinition
*dn
= (JSDefinition
*)
1238 NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), pn
->pn_atom
, tc
);
1242 ALE_SET_DEFN(ale
, dn
);
1244 dn
->pn_dflags
|= PND_PLACEHOLDER
;
1249 Define(JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
1251 JS_ASSERT(!pn
->pn_used
);
1252 JS_ASSERT_IF(pn
->pn_defn
, pn
->isPlaceholder());
1255 JSAtomListElement
*ale
= NULL
;
1256 JSAtomList
*list
= NULL
;
1259 ale
= (list
= &tc
->decls
)->rawLookup(atom
, hep
);
1261 ale
= (list
= &tc
->lexdeps
)->rawLookup(atom
, hep
);
1264 JSDefinition
*dn
= ALE_DEFN(ale
);
1266 JSParseNode
**pnup
= &dn
->dn_uses
;
1268 uintN start
= let
? pn
->pn_blockid
: tc
->bodyid
;
1270 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_blockid
>= start
) {
1271 JS_ASSERT(pnu
->pn_used
);
1272 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1273 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1274 pnup
= &pnu
->pn_link
;
1277 if (pnu
!= dn
->dn_uses
) {
1278 *pnup
= pn
->dn_uses
;
1279 pn
->dn_uses
= dn
->dn_uses
;
1282 if ((!pnu
|| pnu
->pn_blockid
< tc
->bodyid
) && list
!= &tc
->decls
)
1283 list
->rawRemove(tc
->compiler
, ale
, hep
);
1288 ale
= tc
->decls
.add(tc
->compiler
, atom
, let
? JSAtomList::SHADOW
: JSAtomList::UNIQUE
);
1291 ALE_SET_DEFN(ale
, pn
);
1293 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
1298 LinkUseToDef(JSParseNode
*pn
, JSDefinition
*dn
, JSTreeContext
*tc
)
1300 JS_ASSERT(!pn
->pn_used
);
1301 JS_ASSERT(!pn
->pn_defn
);
1302 JS_ASSERT(pn
!= dn
->dn_uses
);
1303 pn
->pn_link
= dn
->dn_uses
;
1310 ForgetUse(JSParseNode
*pn
)
1313 JS_ASSERT(!pn
->pn_defn
);
1317 JSParseNode
**pnup
= &pn
->lexdef()->dn_uses
;
1319 while ((pnu
= *pnup
) != pn
)
1320 pnup
= &pnu
->pn_link
;
1321 *pnup
= pn
->pn_link
;
1322 pn
->pn_used
= false;
1325 static JSParseNode
*
1326 MakeAssignment(JSParseNode
*pn
, JSParseNode
*rhs
, JSTreeContext
*tc
)
1328 JSParseNode
*lhs
= NewOrRecycledNode(tc
);
1334 JSDefinition
*dn
= pn
->pn_lexdef
;
1335 JSParseNode
**pnup
= &dn
->dn_uses
;
1338 pnup
= &(*pnup
)->pn_link
;
1340 lhs
->pn_link
= pn
->pn_link
;
1344 pn
->pn_type
= TOK_ASSIGN
;
1345 pn
->pn_op
= JSOP_NOP
;
1346 pn
->pn_arity
= PN_BINARY
;
1347 pn
->pn_used
= pn
->pn_defn
= false;
1353 static JSParseNode
*
1354 MakeDefIntoUse(JSDefinition
*dn
, JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
)
1357 * If dn is var, const, or let, and it has an initializer, then we must
1358 * rewrite it to be an assignment node, whose freshly allocated left-hand
1359 * side becomes a use of pn.
1361 if (dn
->isBindingForm()) {
1362 JSParseNode
*rhs
= dn
->expr();
1364 JSParseNode
*lhs
= MakeAssignment(dn
, rhs
, tc
);
1367 //pn->dn_uses = lhs;
1368 dn
= (JSDefinition
*) lhs
;
1371 dn
->pn_op
= (js_CodeSpec
[dn
->pn_op
].format
& JOF_SET
) ? JSOP_SETNAME
: JSOP_NAME
;
1372 } else if (dn
->kind() == JSDefinition::FUNCTION
) {
1373 JS_ASSERT(dn
->isTopLevel());
1374 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
1375 dn
->pn_type
= TOK_NAME
;
1376 dn
->pn_arity
= PN_NAME
;
1380 /* Now make dn no longer a definition, rather a use of pn. */
1381 JS_ASSERT(dn
->pn_type
== TOK_NAME
);
1382 JS_ASSERT(dn
->pn_arity
== PN_NAME
);
1383 JS_ASSERT(dn
->pn_atom
== atom
);
1385 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
1386 JS_ASSERT(pnu
->pn_used
);
1387 JS_ASSERT(!pnu
->pn_defn
);
1388 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1389 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1391 pn
->pn_dflags
|= dn
->pn_dflags
& PND_USE2DEF_FLAGS
;
1394 dn
->pn_defn
= false;
1396 dn
->pn_lexdef
= (JSDefinition
*) pn
;
1397 dn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1398 dn
->pn_dflags
&= ~PND_BOUND
;
1403 DefineArg(JSParseNode
*pn
, JSAtom
*atom
, uintN i
, JSTreeContext
*tc
)
1405 JSParseNode
*argpn
, *argsbody
;
1407 /* Flag tc so we don't have to lookup arguments on every use. */
1408 if (atom
== tc
->compiler
->context
->runtime
->atomState
.argumentsAtom
)
1409 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1412 * Make an argument definition node, distinguished by being in tc->decls
1413 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1414 * list node returned via pn->pn_body.
1416 argpn
= NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), atom
, tc
);
1419 JS_ASSERT(PN_TYPE(argpn
) == TOK_NAME
&& PN_OP(argpn
) == JSOP_NOP
);
1421 /* Arguments are initialized by definition. */
1422 argpn
->pn_dflags
|= PND_INITIALIZED
;
1423 if (!Define(argpn
, atom
, tc
))
1426 argsbody
= pn
->pn_body
;
1428 argsbody
= NewParseNode(PN_LIST
, tc
);
1431 argsbody
->pn_type
= TOK_ARGSBODY
;
1432 argsbody
->pn_op
= JSOP_NOP
;
1433 argsbody
->makeEmpty();
1434 pn
->pn_body
= argsbody
;
1436 argsbody
->append(argpn
);
1438 argpn
->pn_op
= JSOP_GETARG
;
1439 argpn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, i
);
1440 argpn
->pn_dflags
|= PND_BOUND
;
1445 * Compile a JS function body, which might appear as the value of an event
1446 * handler attribute in an HTML <INPUT> tag.
1449 JSCompiler::compileFunctionBody(JSContext
*cx
, JSFunction
*fun
, JSPrincipals
*principals
,
1450 const jschar
*chars
, size_t length
,
1451 const char *filename
, uintN lineno
)
1453 JSCompiler
jsc(cx
, principals
);
1455 if (!jsc
.init(chars
, length
, NULL
, filename
, lineno
))
1458 /* No early return from after here until the js_FinishArenaPool calls. */
1459 JSArenaPool codePool
, notePool
;
1460 JS_INIT_ARENA_POOL(&codePool
, "code", 1024, sizeof(jsbytecode
),
1461 &cx
->scriptStackQuota
);
1462 JS_INIT_ARENA_POOL(¬ePool
, "note", 1024, sizeof(jssrcnote
),
1463 &cx
->scriptStackQuota
);
1465 JSCodeGenerator
funcg(&jsc
, &codePool
, ¬ePool
, jsc
.tokenStream
.lineno
);
1466 funcg
.flags
|= TCF_IN_FUNCTION
;
1468 if (!GenerateBlockId(&funcg
, funcg
.bodyid
))
1471 /* FIXME: make Function format the source for a function definition. */
1472 jsc
.tokenStream
.tokens
[0].type
= TOK_NAME
;
1473 JSParseNode
*fn
= NewParseNode(PN_FUNC
, &funcg
);
1476 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1478 uintN nargs
= fun
->nargs
;
1480 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
1484 for (uintN i
= 0; i
< nargs
; i
++) {
1485 JSAtom
*name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1486 if (!DefineArg(fn
, name
, i
, &funcg
)) {
1496 * Farble the body so that it looks like a block statement to js_EmitTree,
1497 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1498 * done parsing, we must fold constants, analyze any nested functions, and
1499 * generate code for this function, including a stop opcode at the end.
1501 CURRENT_TOKEN(&jsc
.tokenStream
).type
= TOK_LC
;
1502 JSParseNode
*pn
= fn
? FunctionBody(cx
, &jsc
.tokenStream
, &funcg
) : NULL
;
1504 if (!js_MatchToken(cx
, &jsc
.tokenStream
, TOK_EOF
)) {
1505 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
,
1506 JSREPORT_ERROR
, JSMSG_SYNTAX_ERROR
);
1508 } else if (!js_FoldConstants(cx
, pn
, &funcg
)) {
1509 /* js_FoldConstants reported the error already. */
1511 } else if (funcg
.functionList
&&
1512 !jsc
.analyzeFunctions(funcg
.functionList
, funcg
.flags
)) {
1516 JS_ASSERT(PN_TYPE(fn
->pn_body
) == TOK_ARGSBODY
);
1517 fn
->pn_body
->append(pn
);
1518 fn
->pn_body
->pn_pos
= pn
->pn_pos
;
1522 if (!js_EmitFunctionScript(cx
, &funcg
, pn
))
1527 /* Restore saved state and release code generation arenas. */
1528 JS_FinishArenaPool(&codePool
);
1529 JS_FinishArenaPool(¬ePool
);
1534 * Parameter block types for the several Binder functions. We use a common
1535 * helper function signature in order to share code among destructuring and
1536 * simple variable declaration parsers. In the destructuring case, the binder
1537 * function is called indirectly from the variable declaration parser by way
1538 * of CheckDestructuring and its friends.
1540 typedef struct BindData BindData
;
1543 (*Binder
)(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
);
1546 BindData() : fresh(true) {}
1548 JSParseNode
*pn
; /* name node for definition processing and
1549 error source coordinates */
1550 JSOp op
; /* prolog bytecode or nop */
1551 Binder binder
; /* binder, discriminates u */
1561 BindLocalVariable(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
,
1562 JSLocalKind localKind
)
1564 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1567 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1568 * Instead 'var arguments' always restates the predefined property of the
1569 * activation objects whose name is 'arguments'. Assignment to such a
1570 * variable must be handled specially.
1572 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
1575 return js_AddLocal(cx
, fun
, atom
, localKind
);
1578 #if JS_HAS_DESTRUCTURING
1580 * Forward declaration to maintain top-down presentation.
1582 static JSParseNode
*
1583 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
1587 BindDestructuringArg(JSContext
*cx
, BindData
*data
, JSAtom
*atom
,
1590 JSAtomListElement
*ale
;
1593 /* Flag tc so we don't have to lookup arguments on every use. */
1594 if (atom
== tc
->compiler
->context
->runtime
->atomState
.argumentsAtom
)
1595 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1597 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1598 ale
= tc
->decls
.lookup(atom
);
1600 if (!ale
&& !Define(pn
, atom
, tc
))
1603 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
1604 if (localKind
!= JSLOCAL_NONE
) {
1605 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
,
1606 JSREPORT_ERROR
, JSMSG_DESTRUCT_DUP_ARG
);
1610 uintN index
= tc
->fun
->u
.i
.nvars
;
1611 if (!BindLocalVariable(cx
, tc
->fun
, atom
, JSLOCAL_VAR
))
1613 pn
->pn_op
= JSOP_SETLOCAL
;
1614 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
1615 pn
->pn_dflags
|= PND_BOUND
;
1618 #endif /* JS_HAS_DESTRUCTURING */
1621 JSCompiler::newFunction(JSTreeContext
*tc
, JSAtom
*atom
, uintN lambda
)
1626 JS_ASSERT((lambda
& ~JSFUN_LAMBDA
) == 0);
1629 * Find the global compilation context in order to pre-set the newborn
1630 * function's parent slot to tc->scopeChain. If the global context is a
1631 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1632 * clear parent and proto.
1636 parent
= (tc
->flags
& TCF_IN_FUNCTION
) ? NULL
: tc
->scopeChain
;
1638 fun
= js_NewFunction(context
, NULL
, NULL
, 0, JSFUN_INTERPRETED
| lambda
,
1641 if (fun
&& !(tc
->flags
& TCF_COMPILE_N_GO
)) {
1642 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun
));
1643 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun
));
1649 MatchOrInsertSemicolon(JSContext
*cx
, JSTokenStream
*ts
)
1653 ts
->flags
|= TSF_OPERAND
;
1654 tt
= js_PeekTokenSameLine(cx
, ts
);
1655 ts
->flags
&= ~TSF_OPERAND
;
1656 if (tt
== TOK_ERROR
)
1658 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
1659 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1660 JSMSG_SEMI_BEFORE_STMNT
);
1663 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
1668 JSCompiler::analyzeFunctions(JSFunctionBox
*funbox
, uint16
& tcflags
)
1670 if (!markFunArgs(funbox
, tcflags
))
1672 setFunctionKinds(funbox
, tcflags
);
1677 * Mark as funargs any functions that reach up to one or more upvars across an
1678 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1680 * function f(o, p) {
1681 * o.m = function o_m(a) {
1682 * function g() { return p; }
1683 * function h() { return a; }
1688 * but without this extra marking phase, function g will not be marked as a
1689 * funarg since it is called from within its parent scope. But g reaches up to
1690 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1691 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1692 * nor uses an upvar "above" o_m's level.
1694 * If function g itself contained lambdas that contained non-lambdas that reach
1695 * up above its level, then those non-lambdas would have to be marked too. This
1696 * process is potentially exponential in the number of functions, but generally
1697 * not so complex. But it can't be done during a single recursive traversal of
1698 * the funbox tree, so we must use a work queue.
1700 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1701 * between the static level of the bodies of funbox and its peers (which must
1702 * be funbox->level + 1), and the static level of the nearest upvar among all
1703 * the upvars contained by funbox and its peers. If there are no upvars, return
1704 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1707 FindFunArgs(JSFunctionBox
*funbox
, int level
, JSFunctionBoxQueue
*queue
)
1709 uintN allskipmin
= FREE_STATIC_LEVEL
;
1712 JSParseNode
*fn
= funbox
->node
;
1713 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
1714 int fnlevel
= level
;
1717 * An eval can leak funbox, functions along its ancestor line, and its
1718 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1719 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1720 * already been marked as funargs by this point. Therefore we have to
1721 * flag only funbox->node and funbox->kids' nodes here.
1723 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
1725 for (JSFunctionBox
*kid
= funbox
->kids
; kid
; kid
= kid
->siblings
)
1726 kid
->node
->setFunArg();
1730 * Compute in skipmin the least distance from fun's static level up to
1731 * an upvar, whether used directly by fun, or indirectly by a function
1734 uintN skipmin
= FREE_STATIC_LEVEL
;
1735 JSParseNode
*pn
= fn
->pn_body
;
1737 if (pn
->pn_type
== TOK_UPVARS
) {
1738 JSAtomList
upvars(pn
->pn_names
);
1739 JS_ASSERT(upvars
.count
!= 0);
1741 JSAtomListIterator
iter(&upvars
);
1742 JSAtomListElement
*ale
;
1744 while ((ale
= iter()) != NULL
) {
1745 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1747 if (!lexdep
->isFreeVar()) {
1748 uintN upvarLevel
= lexdep
->frameLevel();
1750 if (int(upvarLevel
) <= fnlevel
)
1753 uintN skip
= (funbox
->level
+ 1) - upvarLevel
;
1761 * If this function escapes, whether directly (the parser detects such
1762 * escapes) or indirectly (because this non-escaping function uses an
1763 * upvar that reaches across an outer function boundary where the outer
1764 * function escapes), enqueue it for further analysis, and bump fnlevel
1765 * to trap any non-escaping children.
1767 if (fn
->isFunArg()) {
1768 queue
->push(funbox
);
1769 fnlevel
= int(funbox
->level
);
1773 * Now process the current function's children, and recalibrate their
1774 * cumulative skipmin to be relative to the current static level.
1777 uintN kidskipmin
= FindFunArgs(funbox
->kids
, fnlevel
, queue
);
1779 JS_ASSERT(kidskipmin
!= 0);
1780 if (kidskipmin
!= FREE_STATIC_LEVEL
) {
1782 if (kidskipmin
!= 0 && kidskipmin
< skipmin
)
1783 skipmin
= kidskipmin
;
1788 * Finally, after we've traversed all of the current function's kids,
1789 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1790 * with allskipmin, but minimize across funbox and all of its siblings,
1791 * to compute our return value.
1793 if (skipmin
!= FREE_STATIC_LEVEL
) {
1794 fun
->u
.i
.skipmin
= skipmin
;
1795 if (skipmin
< allskipmin
)
1796 allskipmin
= skipmin
;
1798 } while ((funbox
= funbox
->siblings
) != NULL
);
1804 JSCompiler::markFunArgs(JSFunctionBox
*funbox
, uintN tcflags
)
1806 JSFunctionBoxQueue queue
;
1807 if (!queue
.init(functionCount
))
1810 FindFunArgs(funbox
, -1, &queue
);
1811 while ((funbox
= queue
.pull()) != NULL
) {
1812 JSParseNode
*fn
= funbox
->node
;
1813 JS_ASSERT(fn
->isFunArg());
1815 JSParseNode
*pn
= fn
->pn_body
;
1816 if (pn
->pn_type
== TOK_UPVARS
) {
1817 JSAtomList
upvars(pn
->pn_names
);
1818 JS_ASSERT(upvars
.count
!= 0);
1820 JSAtomListIterator
iter(&upvars
);
1821 JSAtomListElement
*ale
;
1823 while ((ale
= iter()) != NULL
) {
1824 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1826 if (!lexdep
->isFreeVar() &&
1827 !lexdep
->isFunArg() &&
1828 lexdep
->kind() == JSDefinition::FUNCTION
) {
1830 * Mark this formerly-Algol-like function as an escaping
1831 * function (i.e., as a funarg), because it is used from a
1832 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1835 * Progress is guaranteed because we set the funarg flag
1836 * here, which suppresses revisiting this function (thanks
1837 * to the !lexdep->isFunArg() test just above).
1839 lexdep
->setFunArg();
1841 JSFunctionBox
*afunbox
= lexdep
->pn_funbox
;
1842 queue
.push(afunbox
);
1845 * Walk over nested functions again, now that we have
1846 * changed the level across which it is unsafe to access
1847 * upvars using the runtime dynamic link (frame chain).
1850 FindFunArgs(afunbox
->kids
, afunbox
->level
, &queue
);
1859 MinBlockId(JSParseNode
*fn
, uint32 id
)
1861 if (fn
->pn_blockid
< id
)
1864 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
1865 if (pn
->pn_blockid
< id
)
1873 OneBlockId(JSParseNode
*fn
, uint32 id
)
1875 if (fn
->pn_blockid
!= id
)
1878 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
1879 if (pn
->pn_blockid
!= id
)
1887 JSCompiler::setFunctionKinds(JSFunctionBox
*funbox
, uint16
& tcflags
)
1889 #ifdef JS_FUNCTION_METERING
1890 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
1892 # define FUN_METER(x) ((void)0)
1894 JSFunctionBox
*parent
= funbox
->parent
;
1897 JSParseNode
*fn
= funbox
->node
;
1900 setFunctionKinds(funbox
->kids
, tcflags
);
1902 JSParseNode
*pn
= fn
->pn_body
;
1903 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
1906 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
1908 JS_ASSERT(FUN_KIND(fun
) == JSFUN_INTERPRETED
);
1909 } else if (pn
->pn_type
!= TOK_UPVARS
) {
1911 * No lexical dependencies => null closure, for best performance.
1912 * A null closure needs no scope chain, but alas we've coupled
1913 * principals-finding to scope (for good fundamental reasons, but
1914 * the implementation overloads the parent slot and we should fix
1915 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
1917 * In more detail: the ES3 spec allows the implementation to create
1918 * "joined function objects", or not, at its discretion. But real-
1919 * world implementations always create unique function objects for
1920 * closures, and this can be detected via mutation. Open question:
1921 * do popular implementations create unique function objects for
1924 * FIXME: bug 476950.
1926 FUN_METER(nofreeupvar
);
1927 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1929 JSAtomList
upvars(pn
->pn_names
);
1930 JS_ASSERT(upvars
.count
!= 0);
1932 JSAtomListIterator
iter(&upvars
);
1933 JSAtomListElement
*ale
;
1935 if (!fn
->isFunArg()) {
1937 * This function is Algol-like, it never escapes. So long as it
1938 * does not assign to outer variables, it needs only an upvars
1939 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
1940 * bytecode to reach up the frame stack at runtime based on
1941 * those upvars' cookies.
1943 * Any assignments to upvars from functions called by this one
1944 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
1945 * which load from stack homes when interpreting or from native
1946 * stack slots when executing a trace.
1948 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
1949 * nested function to assign to an outer lexical variable, so
1950 * we defer adding yet more code footprint in the absence of
1951 * evidence motivating these opcodes.
1953 bool mutation
= !!(funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
);
1957 * Check that at least one outer lexical binding was assigned
1958 * to (global variables don't count). This is conservative: we
1959 * could limit assignments to those in the current function,
1960 * but that's too much work. As with flat closures (handled
1961 * below), we optimize for the case where outer bindings are
1962 * not reassigned anywhere.
1964 while ((ale
= iter()) != NULL
) {
1965 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1967 if (!lexdep
->isFreeVar()) {
1968 JS_ASSERT(lexdep
->frameLevel() <= funbox
->level
);
1970 if (lexdep
->isAssigned())
1978 FUN_METER(onlyfreevar
);
1979 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1980 } else if (!mutation
&& !(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
)) {
1982 * Algol-like functions can read upvars using the dynamic
1983 * link (cx->fp/fp->down). They do not need to entrain and
1984 * search their environment.
1987 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1989 if (!(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
))
1990 FUN_METER(setupvar
);
1996 * For each lexical dependency from this closure to an outer
1997 * binding, analyze whether it is safe to copy the binding's
1998 * value into a flat closure slot when the closure is formed.
2000 while ((ale
= iter()) != NULL
) {
2001 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2003 if (!lexdep
->isFreeVar()) {
2007 * Consider the current function (the lambda, innermost
2008 * below) using a var x defined two static levels up:
2014 * return function () { return x; };
2019 * So long as (1) the initialization in 'var x = 42'
2020 * dominates all uses of g and (2) x is not reassigned,
2021 * it is safe to optimize the lambda to a flat closure.
2022 * Uncommenting the early call to g makes it unsafe to
2023 * so optimize (z could name a global setter that calls
2026 JSFunctionBox
*afunbox
= funbox
;
2027 uintN lexdepLevel
= lexdep
->frameLevel();
2029 JS_ASSERT(lexdepLevel
<= funbox
->level
);
2030 while (afunbox
->level
!= lexdepLevel
) {
2031 afunbox
= afunbox
->parent
;
2034 * afunbox can't be null because we are sure
2035 * to find a function box whose level == lexdepLevel
2036 * before walking off the top of the funbox tree.
2037 * See bug 493260 comments 16-18.
2039 * Assert but check anyway, to check future changes
2040 * that bind eval upvars in the parser.
2045 * If this function is reaching up across an
2046 * enclosing funarg, we cannot make a flat
2047 * closure. The display stops working once the
2050 if (!afunbox
|| afunbox
->node
->isFunArg())
2055 * If afunbox's function (which is at the same level as
2056 * lexdep) is in a loop, pessimistically assume the
2057 * variable initializer may be in the same loop. A flat
2058 * closure would then be unsafe, as the captured
2059 * variable could be assigned after the closure is
2060 * created. See bug 493232.
2062 if (afunbox
->inLoop
)
2066 * with and eval defeat lexical scoping; eval anywhere
2067 * in a variable's scope can assign to it. Both defeat
2068 * the flat closure optimization. The parser detects
2069 * these cases and flags the function heavyweight.
2071 if ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
)
2072 & TCF_FUN_HEAVYWEIGHT
) {
2077 * If afunbox's function is not a lambda, it will be
2078 * hoisted, so it could capture the undefined value
2079 * that by default initializes var/let/const
2080 * bindings. And if lexdep is a function that comes at
2081 * (meaning a function refers to its own name) or
2082 * strictly after afunbox, we also break to defeat the
2083 * flat closure optimization.
2085 JSFunction
*afun
= (JSFunction
*) afunbox
->object
;
2086 if (!(afun
->flags
& JSFUN_LAMBDA
)) {
2087 if (lexdep
->isBindingForm())
2089 if (lexdep
->pn_pos
>= afunbox
->node
->pn_pos
)
2093 if (!lexdep
->isInitialized())
2096 JSDefinition::Kind lexdepKind
= lexdep
->kind();
2097 if (lexdepKind
!= JSDefinition::CONST
) {
2098 if (lexdep
->isAssigned())
2102 * Any formal could be mutated behind our back via
2103 * the arguments object, so deoptimize if the outer
2104 * function uses arguments.
2106 * In a Function constructor call where the final
2107 * argument -- the body source for the function to
2108 * create -- contains a nested function definition
2109 * or expression, afunbox->parent will be null. The
2110 * body source might use |arguments| outside of any
2111 * nested functions it may contain, so we have to
2112 * check the tcflags parameter that was passed in
2113 * from JSCompiler::compileFunctionBody.
2115 if (lexdepKind
== JSDefinition::ARG
&&
2116 ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
) &
2117 TCF_FUN_USES_ARGUMENTS
)) {
2123 * Check quick-and-dirty dominance relation. Function
2124 * definitions dominate their uses thanks to hoisting.
2125 * Other binding forms hoist as undefined, of course,
2126 * so check forward-reference and blockid relations.
2128 if (lexdepKind
!= JSDefinition::FUNCTION
) {
2130 * Watch out for code such as
2134 * var jQuery = ... = function (...) {
2135 * return new jQuery.foo.bar(baz);
2140 * where the jQuery var is not reassigned, but of
2141 * course is not initialized at the time that the
2142 * would-be-flat closure containing the jQuery
2145 if (lexdep
->pn_pos
.end
>= afunbox
->node
->pn_pos
.end
)
2148 if (lexdep
->isTopLevel()
2149 ? !MinBlockId(afunbox
->node
, lexdep
->pn_blockid
)
2150 : !lexdep
->isBlockChild() ||
2151 !afunbox
->node
->isBlockChild() ||
2152 !OneBlockId(afunbox
->node
, lexdep
->pn_blockid
)) {
2161 FUN_METER(onlyfreevar
);
2162 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2165 * We made it all the way through the upvar loop, so it's
2166 * safe to optimize to a flat closure.
2169 FUN_SET_KIND(fun
, JSFUN_FLAT_CLOSURE
);
2170 switch (PN_OP(fn
)) {
2172 fn
->pn_op
= JSOP_DEFFUN_FC
;
2174 case JSOP_DEFLOCALFUN
:
2175 fn
->pn_op
= JSOP_DEFLOCALFUN_FC
;
2178 fn
->pn_op
= JSOP_LAMBDA_FC
;
2181 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2182 JS_ASSERT(PN_OP(fn
) == JSOP_NOP
);
2185 FUN_METER(badfunarg
);
2190 if (FUN_KIND(fun
) == JSFUN_INTERPRETED
) {
2191 if (pn
->pn_type
!= TOK_UPVARS
) {
2193 parent
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2195 JSAtomList
upvars(pn
->pn_names
);
2196 JS_ASSERT(upvars
.count
!= 0);
2198 JSAtomListIterator
iter(&upvars
);
2199 JSAtomListElement
*ale
;
2202 * One or more upvars cannot be safely snapshot into a flat
2203 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2204 * all upvars, and for each non-free upvar, ensure that its
2205 * containing function has been flagged as heavyweight.
2207 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2208 * generating any code for a tree of nested functions.
2210 while ((ale
= iter()) != NULL
) {
2211 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2213 if (!lexdep
->isFreeVar()) {
2214 JSFunctionBox
*afunbox
= funbox
->parent
;
2215 uintN lexdepLevel
= lexdep
->frameLevel();
2219 * NB: afunbox->level is the static level of
2220 * the definition or expression of the function
2221 * parsed into afunbox, not the static level of
2222 * its body. Therefore we must add 1 to match
2223 * lexdep's level to find the afunbox whose
2224 * body contains the lexdep definition.
2226 if (afunbox
->level
+ 1U == lexdepLevel
||
2227 (lexdepLevel
== 0 && lexdep
->isLet())) {
2228 afunbox
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2231 afunbox
= afunbox
->parent
;
2233 if (!afunbox
&& (tcflags
& TCF_IN_FUNCTION
))
2234 tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2240 funbox
= funbox
->siblings
;
2243 JS_ASSERT(funbox
->parent
== parent
);
2248 const char js_argument_str
[] = "argument";
2249 const char js_variable_str
[] = "variable";
2250 const char js_unknown_str
[] = "unknown";
2253 JSDefinition::kindString(Kind kind
)
2255 static const char *table
[] = {
2256 js_var_str
, js_const_str
, js_let_str
,
2257 js_function_str
, js_argument_str
, js_unknown_str
2260 JS_ASSERT(unsigned(kind
) <= unsigned(ARG
));
2264 static JSFunctionBox
*
2265 EnterFunction(JSParseNode
*fn
, JSTreeContext
*tc
, JSTreeContext
*funtc
,
2266 JSAtom
*funAtom
= NULL
, uintN lambda
= JSFUN_LAMBDA
)
2268 JSFunction
*fun
= tc
->compiler
->newFunction(tc
, funAtom
, lambda
);
2272 /* Create box for fun->object early to protect against last-ditch GC. */
2273 JSFunctionBox
*funbox
= tc
->compiler
->newFunctionBox(FUN_OBJECT(fun
), fn
, tc
);
2277 /* Initialize non-default members of funtc. */
2278 funtc
->flags
|= funbox
->tcflags
;
2279 funtc
->blockidGen
= tc
->blockidGen
;
2280 if (!GenerateBlockId(funtc
, funtc
->bodyid
))
2283 funtc
->funbox
= funbox
;
2285 if (!SetStaticLevel(funtc
, tc
->staticLevel
+ 1))
2292 LeaveFunction(JSParseNode
*fn
, JSTreeContext
*funtc
, JSTreeContext
*tc
,
2293 JSAtom
*funAtom
= NULL
, uintN lambda
= JSFUN_LAMBDA
)
2295 tc
->blockidGen
= funtc
->blockidGen
;
2297 fn
->pn_funbox
->tcflags
|= funtc
->flags
& (TCF_FUN_FLAGS
| TCF_COMPILE_N_GO
);
2299 fn
->pn_dflags
|= PND_INITIALIZED
;
2300 JS_ASSERT_IF(tc
->atTopLevel() && lambda
== 0 && funAtom
,
2301 fn
->pn_dflags
& PND_TOPLEVEL
);
2302 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
2303 fn
->pn_dflags
|= PND_BLOCKCHILD
;
2306 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2307 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2308 * params and body. We do this only if there are lexical dependencies not
2309 * satisfied by the function's declarations, to avoid penalizing functions
2310 * that use only their arguments and other local bindings.
2312 if (funtc
->lexdeps
.count
!= 0) {
2313 JSAtomListIterator
iter(&funtc
->lexdeps
);
2314 JSAtomListElement
*ale
;
2315 int foundCallee
= 0;
2317 while ((ale
= iter()) != NULL
) {
2318 JSAtom
*atom
= ALE_ATOM(ale
);
2319 JSDefinition
*dn
= ALE_DEFN(ale
);
2320 JS_ASSERT(dn
->isPlaceholder());
2322 if (atom
== funAtom
&& lambda
!= 0) {
2323 dn
->pn_op
= JSOP_CALLEE
;
2324 dn
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
->staticLevel
, CALLEE_UPVAR_SLOT
);
2325 dn
->pn_dflags
|= PND_BOUND
;
2328 * If this named function expression uses its own name other
2329 * than to call itself, flag this function as using arguments,
2330 * as if it had used arguments.callee instead of its own name.
2332 * This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
2333 * we are out of tcflags bits at the moment. If it deoptimizes
2334 * code unfairly (see JSCompiler::setFunctionKinds, where this
2335 * flag is interpreted in its broader sense, not only to mean
2336 * "this function might leak arguments.callee"), we can perhaps
2337 * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
2338 * use that more precisely, both here and for unnamed function
2342 fn
->pn_funbox
->tcflags
|= TCF_FUN_USES_ARGUMENTS
;
2347 if (!(fn
->pn_funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
) &&
2350 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2351 * any use of dn in funtc assigns. See NoteLValue for the easy
2352 * backward-reference case; this is the hard forward-reference
2353 * case where we pay a higher price.
2355 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
2356 if (pnu
->isAssigned() && pnu
->pn_blockid
>= funtc
->bodyid
) {
2357 fn
->pn_funbox
->tcflags
|= TCF_FUN_SETS_OUTER_NAME
;
2363 JSAtomListElement
*outer_ale
= tc
->decls
.lookup(atom
);
2365 outer_ale
= tc
->lexdeps
.lookup(atom
);
2368 * Insert dn's uses list at the front of outer_dn's list.
2370 * Without loss of generality or correctness, we allow a dn to
2371 * be in inner and outer lexdeps, since the purpose of lexdeps
2372 * is one-pass coordination of name use and definition across
2373 * functions, and if different dn's are used we'll merge lists
2374 * when leaving the inner function.
2376 * The dn == outer_dn case arises with generator expressions
2377 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2378 * case), and nowhere else, currently.
2380 JSDefinition
*outer_dn
= ALE_DEFN(outer_ale
);
2382 if (dn
!= outer_dn
) {
2383 JSParseNode
**pnup
= &dn
->dn_uses
;
2386 while ((pnu
= *pnup
) != NULL
) {
2387 pnu
->pn_lexdef
= outer_dn
;
2388 pnup
= &pnu
->pn_link
;
2392 * Make dn be a use that redirects to outer_dn, because we
2393 * can't replace dn with outer_dn in all the pn_namesets in
2394 * the AST where it may be. Instead we make it forward to
2395 * outer_dn. See JSDefinition::resolve.
2397 *pnup
= outer_dn
->dn_uses
;
2398 outer_dn
->dn_uses
= dn
;
2399 outer_dn
->pn_dflags
|= dn
->pn_dflags
& ~PND_PLACEHOLDER
;
2400 dn
->pn_defn
= false;
2402 dn
->pn_lexdef
= outer_dn
;
2405 /* Add an outer lexical dependency for ale's definition. */
2406 outer_ale
= tc
->lexdeps
.add(tc
->compiler
, atom
);
2409 ALE_SET_DEFN(outer_ale
, ALE_DEFN(ale
));
2413 if (funtc
->lexdeps
.count
- foundCallee
!= 0) {
2414 JSParseNode
*body
= fn
->pn_body
;
2416 fn
->pn_body
= NewParseNode(PN_NAMESET
, tc
);
2420 fn
->pn_body
->pn_type
= TOK_UPVARS
;
2421 fn
->pn_body
->pn_pos
= body
->pn_pos
;
2423 funtc
->lexdeps
.remove(tc
->compiler
, funAtom
);
2424 fn
->pn_body
->pn_names
= funtc
->lexdeps
;
2425 fn
->pn_body
->pn_tree
= body
;
2428 funtc
->lexdeps
.clear();
2434 static JSParseNode
*
2435 FunctionDef(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2439 JSParseNode
*pn
, *body
, *result
;
2442 JSAtomListElement
*ale
;
2443 #if JS_HAS_DESTRUCTURING
2444 JSParseNode
*item
, *list
= NULL
;
2445 bool destructuringArg
= false, duplicatedArg
= false;
2448 /* Make a TOK_FUNCTION node. */
2449 #if JS_HAS_GETTER_SETTER
2450 op
= CURRENT_TOKEN(ts
).t_op
;
2452 pn
= NewParseNode(PN_FUNC
, tc
);
2456 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2459 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2460 * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2462 * Also treat function sub-statements (non-lambda, non-top-level functions)
2463 * as escaping funargs, since we can't statically analyze their definitions
2466 bool topLevel
= tc
->atTopLevel();
2467 pn
->pn_dflags
= (lambda
|| !topLevel
) ? PND_FUNARG
: 0;
2469 /* Scan the optional function name into funAtom. */
2470 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
2471 tt
= js_GetToken(cx
, ts
);
2472 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
2473 if (tt
== TOK_NAME
) {
2474 funAtom
= CURRENT_TOKEN(ts
).t_atom
;
2476 if (lambda
== 0 && (cx
->options
& JSOPTION_ANONFUNFIX
)) {
2477 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2478 JSMSG_SYNTAX_ERROR
);
2486 * Record names for function statements in tc->decls so we know when to
2487 * avoid optimizing variable references that might name a function.
2489 if (lambda
== 0 && funAtom
) {
2490 ale
= tc
->decls
.lookup(funAtom
);
2492 JSDefinition
*dn
= ALE_DEFN(ale
);
2493 JSDefinition::Kind dn_kind
= dn
->kind();
2495 JS_ASSERT(!dn
->pn_used
);
2496 JS_ASSERT(dn
->pn_defn
);
2498 if (JS_HAS_STRICT_OPTION(cx
) || dn_kind
== JSDefinition::CONST
) {
2499 const char *name
= js_AtomToPrintableString(cx
, funAtom
);
2501 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2502 (dn_kind
!= JSDefinition::CONST
)
2503 ? JSREPORT_WARNING
| JSREPORT_STRICT
2505 JSMSG_REDECLARED_VAR
,
2506 JSDefinition::kindString(dn_kind
),
2513 ALE_SET_DEFN(ale
, pn
);
2515 pn
->dn_uses
= dn
; /* dn->dn_uses is now pn_link */
2517 if (!MakeDefIntoUse(dn
, pn
, funAtom
, tc
))
2520 } else if (topLevel
) {
2522 * If this function was used before it was defined, claim the
2523 * pre-created definition node for this function that PrimaryExpr
2524 * put in tc->lexdeps on first forward reference, and recycle pn.
2528 ale
= tc
->lexdeps
.rawLookup(funAtom
, hep
);
2530 JSDefinition
*fn
= ALE_DEFN(ale
);
2532 JS_ASSERT(fn
->pn_defn
);
2533 fn
->pn_type
= TOK_FUNCTION
;
2534 fn
->pn_arity
= PN_FUNC
;
2535 fn
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2537 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2539 tc
->lexdeps
.rawRemove(tc
->compiler
, ale
, hep
);
2540 RecycleTree(pn
, tc
);
2544 if (!Define(pn
, funAtom
, tc
))
2549 * A function nested at top level inside another's body needs only a
2550 * local variable to bind its name to its value, and not an activation
2551 * object property (it might also need the activation property, if the
2552 * outer function contains with statements, e.g., but the stack slot
2553 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2554 * JSOP_GETLOCAL bytecode).
2557 pn
->pn_dflags
|= PND_TOPLEVEL
;
2559 if (tc
->flags
& TCF_IN_FUNCTION
) {
2560 JSLocalKind localKind
;
2564 * Define a local in the outer function so that BindNameToSlot
2565 * can properly optimize accesses. Note that we need a local
2566 * variable, not an argument, for the function statement. Thus
2567 * we add a variable even if a parameter with the given name
2570 localKind
= js_LookupLocal(cx
, tc
->fun
, funAtom
, &index
);
2571 switch (localKind
) {
2574 index
= tc
->fun
->u
.i
.nvars
;
2575 if (!js_AddLocal(cx
, tc
->fun
, funAtom
, JSLOCAL_VAR
))
2580 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
2581 pn
->pn_dflags
|= PND_BOUND
;
2590 /* Initialize early for possible flags mutation via DestructuringExpr. */
2591 JSTreeContext
funtc(tc
->compiler
);
2593 JSFunctionBox
*funbox
= EnterFunction(pn
, tc
, &funtc
, funAtom
, lambda
);
2597 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
2599 #if JS_HAS_GETTER_SETTER
2601 fun
->flags
|= (op
== JSOP_GETTER
) ? JSPROP_GETTER
: JSPROP_SETTER
;
2604 /* Now parse formal argument list and compute fun->nargs. */
2605 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_FORMAL
);
2606 if (!js_MatchToken(cx
, ts
, TOK_RP
)) {
2608 tt
= js_GetToken(cx
, ts
);
2610 #if JS_HAS_DESTRUCTURING
2615 JSParseNode
*lhs
, *rhs
;
2618 /* See comment below in the TOK_NAME case. */
2620 goto report_dup_and_destructuring
;
2621 destructuringArg
= true;
2624 * A destructuring formal parameter turns into one or more
2625 * local variables initialized from properties of a single
2626 * anonymous positional parameter, so here we must tweak our
2627 * binder and its data.
2630 data
.op
= JSOP_DEFVAR
;
2631 data
.binder
= BindDestructuringArg
;
2632 lhs
= DestructuringExpr(cx
, &data
, &funtc
, tt
);
2637 * Adjust fun->nargs to count the single anonymous positional
2638 * parameter that is to be destructured.
2641 if (!js_AddLocal(cx
, fun
, NULL
, JSLOCAL_ARG
))
2645 * Synthesize a destructuring assignment from the single
2646 * anonymous positional parameter into the destructuring
2647 * left-hand-side expression and accumulate it in list.
2649 rhs
= NewNameNode(cx
, ts
, cx
->runtime
->atomState
.emptyAtom
, &funtc
);
2652 rhs
->pn_type
= TOK_NAME
;
2653 rhs
->pn_op
= JSOP_GETARG
;
2654 rhs
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
.staticLevel
, slot
);
2655 rhs
->pn_dflags
|= PND_BOUND
;
2657 item
= NewBinary(TOK_ASSIGN
, JSOP_NOP
, lhs
, rhs
, &funtc
);
2661 list
= NewParseNode(PN_LIST
, &funtc
);
2664 list
->pn_type
= TOK_COMMA
;
2670 #endif /* JS_HAS_DESTRUCTURING */
2675 * Check for a duplicate parameter name, a "feature" that
2676 * ECMA-262 requires. This is a SpiderMonkey strict warning,
2677 * soon to be an ES3.1 strict error.
2679 * Further, if any argument is a destructuring pattern, forbid
2680 * duplicates. We will report the error either now if we have
2681 * seen a destructuring pattern already, or later when we find
2682 * the first pattern.
2684 JSAtom
*atom
= CURRENT_TOKEN(ts
).t_atom
;
2685 if (JS_HAS_STRICT_OPTION(cx
) &&
2686 js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2687 #if JS_HAS_DESTRUCTURING
2688 if (destructuringArg
)
2689 goto report_dup_and_destructuring
;
2690 duplicatedArg
= true;
2692 const char *name
= js_AtomToPrintableString(cx
, atom
);
2694 !js_ReportCompileErrorNumber(cx
, TS(funtc
.compiler
),
2698 JSMSG_DUPLICATE_FORMAL
,
2703 if (!DefineArg(pn
, atom
, fun
->nargs
, &funtc
))
2705 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
2711 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2712 JSMSG_MISSING_FORMAL
);
2717 #if JS_HAS_DESTRUCTURING
2718 report_dup_and_destructuring
:
2719 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
,
2721 JSMSG_DESTRUCT_DUP_ARG
);
2725 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
2727 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FORMAL
);
2730 #if JS_HAS_EXPR_CLOSURES
2731 ts
->flags
|= TSF_OPERAND
;
2732 tt
= js_GetToken(cx
, ts
);
2733 ts
->flags
&= ~TSF_OPERAND
;
2736 fun
->flags
|= JSFUN_EXPR_CLOSURE
;
2739 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_BODY
);
2742 body
= FunctionBody(cx
, ts
, &funtc
);
2746 #if JS_HAS_EXPR_CLOSURES
2748 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2749 else if (lambda
== 0 && !MatchOrInsertSemicolon(cx
, ts
))
2752 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2754 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2756 #if JS_HAS_DESTRUCTURING
2758 * If there were destructuring formal parameters, prepend the initializing
2759 * comma expression that we synthesized to body. If the body is a lexical
2760 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2761 * parameter destructuring code without bracing the decompilation of the
2762 * function body's lexical scope.
2765 if (body
->pn_arity
!= PN_LIST
) {
2768 block
= NewParseNode(PN_LIST
, tc
);
2771 block
->pn_type
= TOK_SEQ
;
2772 block
->pn_pos
= body
->pn_pos
;
2773 block
->initList(body
);
2778 item
= NewParseNode(PN_UNARY
, tc
);
2782 item
->pn_type
= TOK_SEMI
;
2783 item
->pn_pos
.begin
= item
->pn_pos
.end
= body
->pn_pos
.begin
;
2784 item
->pn_kid
= list
;
2785 item
->pn_next
= body
->pn_head
;
2786 body
->pn_head
= item
;
2787 if (body
->pn_tail
== &body
->pn_head
)
2788 body
->pn_tail
= &item
->pn_next
;
2790 body
->pn_xflags
|= PNX_DESTRUCT
;
2795 * If we collected flags that indicate nested heavyweight functions, or
2796 * this function contains heavyweight-making statements (with statement,
2797 * visible eval call, or assignment to 'arguments'), flag the function as
2798 * heavyweight (requiring a call object per invocation).
2800 if (funtc
.flags
& TCF_FUN_HEAVYWEIGHT
) {
2801 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
2802 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2805 * If this function is a named statement function not at top-level
2806 * (i.e. not a top-level function definiton or expression), then our
2807 * enclosing function, if any, must be heavyweight.
2809 if (!topLevel
&& lambda
== 0 && funAtom
)
2810 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2816 * ECMA ed. 3 standard: function expression, possibly anonymous.
2819 } else if (!funAtom
) {
2821 * If this anonymous function definition is *not* embedded within a
2822 * larger expression, we treat it as an expression statement, not as
2823 * a function declaration -- and not as a syntax error (as ECMA-262
2824 * Edition 3 would have it). Backward compatibility must trump all,
2825 * unless JSOPTION_ANONFUNFIX is set.
2827 result
= NewParseNode(PN_UNARY
, tc
);
2830 result
->pn_type
= TOK_SEMI
;
2831 result
->pn_pos
= pn
->pn_pos
;
2832 result
->pn_kid
= pn
;
2834 } else if (!topLevel
) {
2836 * ECMA ed. 3 extension: a function expression statement not at the
2837 * top level, e.g., in a compound statement such as the "then" part
2838 * of an "if" statement, binds a closure only if control reaches that
2846 funbox
->kids
= funtc
.functionList
;
2848 pn
->pn_funbox
= funbox
;
2851 pn
->pn_body
->append(body
);
2852 pn
->pn_body
->pn_pos
= body
->pn_pos
;
2857 pn
->pn_blockid
= tc
->blockid();
2859 if (!LeaveFunction(pn
, &funtc
, tc
, funAtom
, lambda
))
2865 static JSParseNode
*
2866 FunctionStmt(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2868 return FunctionDef(cx
, ts
, tc
, 0);
2871 static JSParseNode
*
2872 FunctionExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2874 return FunctionDef(cx
, ts
, tc
, JSFUN_LAMBDA
);
2878 * Parse the statements in a block, creating a TOK_LC node that lists the
2879 * statements' trees. If called from block-parsing code, the caller must
2880 * match { before and } after.
2882 static JSParseNode
*
2883 Statements(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2885 JSParseNode
*pn
, *pn2
, *saveBlock
;
2888 JS_CHECK_RECURSION(cx
, return NULL
);
2890 pn
= NewParseNode(PN_LIST
, tc
);
2893 pn
->pn_type
= TOK_LC
;
2895 pn
->pn_blockid
= tc
->blockid();
2896 saveBlock
= tc
->blockNode
;
2900 ts
->flags
|= TSF_OPERAND
;
2901 tt
= js_PeekToken(cx
, ts
);
2902 ts
->flags
&= ~TSF_OPERAND
;
2903 if (tt
<= TOK_EOF
|| tt
== TOK_RC
) {
2904 if (tt
== TOK_ERROR
) {
2905 if (ts
->flags
& TSF_EOF
)
2906 ts
->flags
|= TSF_UNEXPECTED_EOF
;
2911 pn2
= Statement(cx
, ts
, tc
);
2913 if (ts
->flags
& TSF_EOF
)
2914 ts
->flags
|= TSF_UNEXPECTED_EOF
;
2918 if (pn2
->pn_type
== TOK_FUNCTION
) {
2920 * PNX_FUNCDEFS notifies the emitter that the block contains top-
2921 * level function definitions that should be processed before the
2924 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
2925 * is relevant only for function definitions not at top-level,
2926 * which we call function statements.
2928 if (tc
->atTopLevel())
2929 pn
->pn_xflags
|= PNX_FUNCDEFS
;
2931 tc
->flags
|= TCF_HAS_FUNCTION_STMT
;
2937 * Handle the case where there was a let declaration under this block. If
2938 * it replaced tc->blockNode with a new block node then we must refresh pn
2939 * and then restore tc->blockNode.
2941 if (tc
->blockNode
!= pn
)
2943 tc
->blockNode
= saveBlock
;
2945 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2949 static JSParseNode
*
2950 Condition(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2954 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
2955 pn
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
2958 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
2961 * Check for (a = b) and warn about possible (a == b) mistype iff b's
2962 * operator has greater precedence than ==.
2964 if (pn
->pn_type
== TOK_ASSIGN
&&
2965 pn
->pn_op
== JSOP_NOP
&&
2966 pn
->pn_right
->pn_type
> TOK_EQOP
)
2968 if (!js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2969 JSREPORT_WARNING
| JSREPORT_STRICT
,
2970 JSMSG_EQUAL_AS_ASSIGN
,
2979 MatchLabel(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
)
2984 tt
= js_PeekTokenSameLine(cx
, ts
);
2985 if (tt
== TOK_ERROR
)
2987 if (tt
== TOK_NAME
) {
2988 (void) js_GetToken(cx
, ts
);
2989 label
= CURRENT_TOKEN(ts
).t_atom
;
2993 pn
->pn_atom
= label
;
2998 BindLet(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3002 JSAtomListElement
*ale
;
3006 * Top-level 'let' is the same as 'var' currently -- this may change in a
3007 * successor standard to ES3.1 that specifies 'let'.
3009 JS_ASSERT(!tc
->atTopLevel());
3012 blockObj
= tc
->blockChain
;
3013 ale
= tc
->decls
.lookup(atom
);
3014 if (ale
&& ALE_DEFN(ale
)->pn_blockid
== tc
->blockid()) {
3015 const char *name
= js_AtomToPrintableString(cx
, atom
);
3017 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3018 JSREPORT_ERROR
, JSMSG_REDECLARED_VAR
,
3019 (ale
&& ALE_DEFN(ale
)->isConst())
3027 n
= OBJ_BLOCK_COUNT(cx
, blockObj
);
3028 if (n
== JS_BIT(16)) {
3029 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3030 JSREPORT_ERROR
, data
->let
.overflow
);
3035 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3036 * This is balanced by PopStatement, defined immediately below.
3038 if (!Define(pn
, atom
, tc
, true))
3042 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3043 * upvar cookie whose skip tells the current static level. The emitter will
3044 * adjust the node's slot based on its stack depth model -- and, for global
3045 * and eval code, JSCompiler::compileScript will adjust the slot again to
3046 * include script->nfixed.
3048 pn
->pn_op
= JSOP_GETLOCAL
;
3049 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, n
);
3050 pn
->pn_dflags
|= PND_LET
| PND_BOUND
;
3053 * Use JSPROP_ENUMERATE to aid the disassembler. Define the let binding's
3054 * property before storing pn in a reserved slot, since block_reserveSlots
3055 * depends on OBJ_SCOPE(blockObj)->entryCount.
3057 if (!js_DefineNativeProperty(cx
, blockObj
, ATOM_TO_JSID(atom
), JSVAL_VOID
,
3062 SPROP_HAS_SHORTID
, (int16
) n
, NULL
)) {
3067 * Store pn temporarily in what would be reserved slots in a cloned block
3068 * object (once the prototype's final population is known, after all 'let'
3069 * bindings for this block have been parsed). We will free these reserved
3070 * slots in jsemit.cpp:EmitEnterBlock.
3072 uintN slot
= JSSLOT_FREE(&js_BlockClass
) + n
;
3073 if (slot
>= STOBJ_NSLOTS(blockObj
) &&
3074 !js_GrowSlots(cx
, blockObj
, slot
+ 1)) {
3077 OBJ_SCOPE(blockObj
)->freeslot
= slot
+ 1;
3078 STOBJ_SET_SLOT(blockObj
, slot
, PRIVATE_TO_JSVAL(pn
));
3083 PopStatement(JSTreeContext
*tc
)
3085 JSStmtInfo
*stmt
= tc
->topStmt
;
3087 if (stmt
->flags
& SIF_SCOPE
) {
3088 JSObject
*obj
= stmt
->blockObj
;
3089 JSScope
*scope
= OBJ_SCOPE(obj
);
3090 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj
));
3092 for (JSScopeProperty
*sprop
= scope
->lastProp
; sprop
; sprop
= sprop
->parent
) {
3093 JSAtom
*atom
= JSID_TO_ATOM(sprop
->id
);
3095 /* Beware the empty destructuring dummy. */
3096 if (atom
== tc
->compiler
->context
->runtime
->atomState
.emptyAtom
)
3098 tc
->decls
.remove(tc
->compiler
, atom
);
3102 * The block scope will not be modified again. It may be shared. Clear
3103 * scope->object to make scope->owned() false.
3105 scope
->object
= NULL
;
3107 js_PopStatement(tc
);
3111 OuterLet(JSTreeContext
*tc
, JSStmtInfo
*stmt
, JSAtom
*atom
)
3113 while (stmt
->downScope
) {
3114 stmt
= js_LexicalLookup(tc
, atom
, NULL
, stmt
->downScope
);
3117 if (stmt
->type
== STMT_BLOCK
)
3124 BindVarOrConst(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3126 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
3127 JSParseNode
*pn
= data
->pn
;
3129 if (stmt
&& stmt
->type
== STMT_WITH
) {
3130 pn
->pn_op
= JSOP_NAME
;
3131 data
->fresh
= false;
3135 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
3139 JSDefinition
*dn
= ale
? ALE_DEFN(ale
) : NULL
;
3140 JSDefinition::Kind dn_kind
= dn
? dn
->kind() : JSDefinition::VAR
;
3143 if (dn_kind
== JSDefinition::ARG
) {
3144 name
= js_AtomToPrintableString(cx
, atom
);
3148 if (op
== JSOP_DEFCONST
) {
3149 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3150 JSREPORT_ERROR
, JSMSG_REDECLARED_PARAM
,
3154 if (!js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3155 JSREPORT_WARNING
| JSREPORT_STRICT
,
3156 JSMSG_VAR_HIDES_ARG
, name
)) {
3160 bool error
= (op
== JSOP_DEFCONST
||
3161 dn_kind
== JSDefinition::CONST
||
3162 (dn_kind
== JSDefinition::LET
&&
3163 (stmt
->type
!= STMT_CATCH
|| OuterLet(tc
, stmt
, atom
))));
3165 if (JS_HAS_STRICT_OPTION(cx
)
3166 ? op
!= JSOP_DEFVAR
|| dn_kind
!= JSDefinition::VAR
3168 name
= js_AtomToPrintableString(cx
, atom
);
3170 !js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3172 ? JSREPORT_WARNING
| JSREPORT_STRICT
3174 JSMSG_REDECLARED_VAR
,
3175 JSDefinition::kindString(dn_kind
),
3184 if (!Define(pn
, atom
, tc
))
3188 * A var declaration never recreates an existing binding, it restates
3189 * it and possibly reinitializes its value. Beware that if pn becomes a
3190 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3191 * const (typically a const would ;-), then pn must be rewritten into a
3192 * TOK_ASSIGN node. See Variables, further below.
3194 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3195 * There the x definition is hoisted but the x = 2 assignment mutates
3196 * the block-local binding of x.
3198 JSDefinition
*dn
= ALE_DEFN(ale
);
3200 data
->fresh
= false;
3203 /* Make pnu be a fresh name node that uses dn. */
3204 JSParseNode
*pnu
= pn
;
3207 pnu
= NewNameNode(cx
, TS(tc
->compiler
), atom
, tc
);
3212 LinkUseToDef(pnu
, dn
, tc
);
3213 pnu
->pn_op
= JSOP_NAME
;
3216 while (dn
->kind() == JSDefinition::LET
) {
3218 ale
= ALE_NEXT(ale
);
3219 } while (ale
&& ALE_ATOM(ale
) != atom
);
3226 JS_ASSERT_IF(data
->op
== JSOP_DEFCONST
,
3227 dn
->kind() == JSDefinition::CONST
);
3232 * A var or const that is shadowed by one or more let bindings of the
3233 * same name, but that has not been declared until this point, must be
3234 * hoisted above the let bindings.
3239 ale
= tc
->lexdeps
.rawLookup(atom
, hep
);
3242 tc
->lexdeps
.rawRemove(tc
->compiler
, ale
, hep
);
3244 JSParseNode
*pn2
= NewNameNode(cx
, TS(tc
->compiler
), atom
, tc
);
3248 /* The token stream may be past the location for pn. */
3249 pn2
->pn_type
= TOK_NAME
;
3250 pn2
->pn_pos
= pn
->pn_pos
;
3253 pn
->pn_op
= JSOP_NAME
;
3256 ale
= tc
->decls
.add(tc
->compiler
, atom
, JSAtomList::HOIST
);
3259 ALE_SET_DEFN(ale
, pn
);
3261 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
3264 if (data
->op
== JSOP_DEFCONST
)
3265 pn
->pn_dflags
|= PND_CONST
;
3267 if (!(tc
->flags
& TCF_IN_FUNCTION
)) {
3269 * If we are generating global or eval-called-from-global code, bind a
3270 * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3271 * up global variable access by memoizing name-to-slot mappings in the
3272 * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3273 * can't be done due to a pre-existing property of the same name as the
3274 * var or const but incompatible attributes/getter/setter/etc, these
3275 * ops devolve to JSOP_NAME, etc.
3277 * For now, don't try to lookup eval frame variables at compile time.
3278 * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3279 * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3281 pn
->pn_op
= JSOP_NAME
;
3282 if ((tc
->flags
& TCF_COMPILING
) && !tc
->compiler
->callerFrame
) {
3283 JSCodeGenerator
*cg
= (JSCodeGenerator
*) tc
;
3285 /* Index atom so we can map fast global number to name. */
3286 ale
= cg
->atomList
.add(tc
->compiler
, atom
);
3290 /* Defend against cg->ngvars 16-bit overflow. */
3291 uintN slot
= ALE_INDEX(ale
);
3292 if ((slot
+ 1) >> 16)
3295 if ((uint16
)(slot
+ 1) > cg
->ngvars
)
3296 cg
->ngvars
= (uint16
)(slot
+ 1);
3298 pn
->pn_op
= JSOP_GETGVAR
;
3299 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, slot
);
3300 pn
->pn_dflags
|= PND_BOUND
| PND_GVAR
;
3305 if (atom
== cx
->runtime
->atomState
.argumentsAtom
) {
3306 pn
->pn_op
= JSOP_ARGUMENTS
;
3307 pn
->pn_dflags
|= PND_BOUND
;
3311 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
3312 if (localKind
== JSLOCAL_NONE
) {
3314 * Property not found in current variable scope: we have not seen this
3315 * variable before. Define a new local variable by adding a property to
3316 * the function's scope and allocating one slot in the function's vars
3317 * frame. Any locals declared in a with statement body are handled at
3318 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3319 * and heavyweight-function-local vars.
3321 localKind
= (data
->op
== JSOP_DEFCONST
) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
3323 uintN index
= tc
->fun
->u
.i
.nvars
;
3324 if (!BindLocalVariable(cx
, tc
->fun
, atom
, localKind
))
3326 pn
->pn_op
= JSOP_GETLOCAL
;
3327 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
3328 pn
->pn_dflags
|= PND_BOUND
;
3332 if (localKind
== JSLOCAL_ARG
) {
3333 /* We checked errors and strict warnings earlier -- see above. */
3334 JS_ASSERT(ale
&& ALE_DEFN(ale
)->kind() == JSDefinition::ARG
);
3336 /* Not an argument, must be a redeclared local var. */
3337 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
3339 pn
->pn_op
= JSOP_NAME
;
3344 MakeSetCall(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN msg
)
3348 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
3349 JS_ASSERT(pn
->pn_op
== JSOP_CALL
|| pn
->pn_op
== JSOP_EVAL
|| pn
->pn_op
== JSOP_APPLY
);
3351 if (pn2
->pn_type
== TOK_FUNCTION
&& (pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
)) {
3352 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
, JSREPORT_ERROR
, msg
);
3355 pn
->pn_op
= JSOP_SETCALL
;
3360 NoteLValue(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN dflag
= PND_ASSIGNED
)
3363 JSDefinition
*dn
= pn
->pn_lexdef
;
3366 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3367 * occur as direct kids of the same block with no forward refs to x.
3369 if (!(dn
->pn_dflags
& (PND_INITIALIZED
| PND_CONST
| PND_PLACEHOLDER
)) &&
3370 dn
->isBlockChild() &&
3371 pn
->isBlockChild() &&
3372 dn
->pn_blockid
== pn
->pn_blockid
&&
3373 dn
->pn_pos
.end
<= pn
->pn_pos
.begin
&&
3374 dn
->dn_uses
== pn
) {
3375 dflag
= PND_INITIALIZED
;
3378 dn
->pn_dflags
|= dflag
;
3380 if (dn
->frameLevel() != tc
->staticLevel
) {
3382 * The above condition takes advantage of the all-ones nature of
3383 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3384 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3386 JS_ASSERT_IF(dn
->pn_cookie
!= FREE_UPVAR_COOKIE
,
3387 dn
->frameLevel() < tc
->staticLevel
);
3388 tc
->flags
|= TCF_FUN_SETS_OUTER_NAME
;
3392 pn
->pn_dflags
|= dflag
;
3394 if (pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
3395 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3398 #if JS_HAS_DESTRUCTURING
3401 BindDestructuringVar(JSContext
*cx
, BindData
*data
, JSParseNode
*pn
,
3407 * Destructuring is a form of assignment, so just as for an initialized
3408 * simple variable, we must check for assignment to 'arguments' and flag
3409 * the enclosing function (if any) as heavyweight.
3411 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
3413 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
3414 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3417 if (!data
->binder(cx
, data
, atom
, tc
))
3421 * Select the appropriate name-setting opcode, respecting eager selection
3422 * done by the data->binder function.
3424 if (pn
->pn_dflags
& PND_BOUND
) {
3425 pn
->pn_op
= (pn
->pn_op
== JSOP_ARGUMENTS
)
3427 : (pn
->pn_dflags
& PND_GVAR
)
3431 pn
->pn_op
= (data
->op
== JSOP_DEFCONST
)
3436 if (data
->op
== JSOP_DEFCONST
)
3437 pn
->pn_dflags
|= PND_CONST
;
3439 NoteLValue(cx
, pn
, tc
, PND_INITIALIZED
);
3444 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3445 * LHS expression except a destructuring initialiser, and R is on the stack.
3446 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3447 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3448 * then push its property name QN. At this point the stack looks like
3450 * [... R, R[P], QB, QN]
3452 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3453 * its operands with left-hand side above right-hand side:
3455 * [rval, lval, xval]
3457 * and pops all three values, setting lval[xval] = rval. But we cannot select
3458 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3459 * which can be optimized further. So we select JSOP_SETNAME.
3462 BindDestructuringLHS(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
3464 while (pn
->pn_type
== TOK_RP
)
3467 switch (pn
->pn_type
) {
3469 NoteLValue(cx
, pn
, tc
);
3474 pn
->pn_op
= JSOP_SETNAME
;
3477 #if JS_HAS_LVALUE_RETURN
3479 if (!MakeSetCall(cx
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
3484 #if JS_HAS_XML_SUPPORT
3486 if (pn
->pn_op
== JSOP_XMLNAME
) {
3487 pn
->pn_op
= JSOP_BINDXMLNAME
;
3494 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3495 JSREPORT_ERROR
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
3502 typedef struct FindPropValData
{
3503 uint32 numvars
; /* # of destructuring vars in left side */
3504 uint32 maxstep
; /* max # of steps searching right side */
3505 JSDHashTable table
; /* hash table for O(1) right side search */
3508 typedef struct FindPropValEntry
{
3509 JSDHashEntryHdr hdr
;
3514 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3515 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3516 ((pnkey)->pn_type == TOK_NUMBER || \
3517 (pnkey)->pn_type == TOK_STRING || \
3518 (pnkey)->pn_type == TOK_NAME)) || \
3519 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3521 static JSDHashNumber
3522 HashFindPropValKey(JSDHashTable
*table
, const void *key
)
3524 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3526 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3527 return (pnkey
->pn_type
== TOK_NUMBER
)
3528 ? (JSDHashNumber
) (JSDOUBLE_HI32(pnkey
->pn_dval
) ^
3529 JSDOUBLE_LO32(pnkey
->pn_dval
))
3530 : ATOM_HASH(pnkey
->pn_atom
);
3534 MatchFindPropValEntry(JSDHashTable
*table
,
3535 const JSDHashEntryHdr
*entry
,
3538 const FindPropValEntry
*fpve
= (const FindPropValEntry
*)entry
;
3539 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3541 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3542 return pnkey
->pn_type
== fpve
->pnkey
->pn_type
&&
3543 ((pnkey
->pn_type
== TOK_NUMBER
)
3544 ? pnkey
->pn_dval
== fpve
->pnkey
->pn_dval
3545 : pnkey
->pn_atom
== fpve
->pnkey
->pn_atom
);
3548 static const JSDHashTableOps FindPropValOps
= {
3552 MatchFindPropValEntry
,
3553 JS_DHashMoveEntryStub
,
3554 JS_DHashClearEntryStub
,
3555 JS_DHashFinalizeStub
,
3559 #define STEP_HASH_THRESHOLD 10
3560 #define BIG_DESTRUCTURING 5
3561 #define BIG_OBJECT_INIT 20
3563 static JSParseNode
*
3564 FindPropertyValue(JSParseNode
*pn
, JSParseNode
*pnid
, FindPropValData
*data
)
3566 FindPropValEntry
*entry
;
3567 JSParseNode
*pnhit
, *pnhead
, *pnprop
, *pnkey
;
3570 /* If we have a hash table, use it as the sole source of truth. */
3571 if (data
->table
.ops
) {
3572 entry
= (FindPropValEntry
*)
3573 JS_DHashTableOperate(&data
->table
, pnid
, JS_DHASH_LOOKUP
);
3574 return JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
) ? entry
->pnval
: NULL
;
3577 /* If pn is not an object initialiser node, we can't do anything here. */
3578 if (pn
->pn_type
!= TOK_RC
)
3582 * We must search all the way through pn's list, to handle the case of an
3583 * id duplicated for two or more property initialisers.
3587 ASSERT_VALID_PROPERTY_KEY(pnid
);
3588 pnhead
= pn
->pn_head
;
3589 if (pnid
->pn_type
== TOK_NUMBER
) {
3590 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3591 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3592 if (pnprop
->pn_op
== JSOP_NOP
) {
3593 pnkey
= pnprop
->pn_left
;
3594 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3595 if (pnkey
->pn_type
== TOK_NUMBER
&&
3596 pnkey
->pn_dval
== pnid
->pn_dval
) {
3603 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3604 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3605 if (pnprop
->pn_op
== JSOP_NOP
) {
3606 pnkey
= pnprop
->pn_left
;
3607 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3608 if (pnkey
->pn_type
== pnid
->pn_type
&&
3609 pnkey
->pn_atom
== pnid
->pn_atom
) {
3619 /* Hit via full search -- see whether it's time to create the hash table. */
3620 JS_ASSERT(!data
->table
.ops
);
3621 if (step
> data
->maxstep
) {
3622 data
->maxstep
= step
;
3623 if (step
>= STEP_HASH_THRESHOLD
&&
3624 data
->numvars
>= BIG_DESTRUCTURING
&&
3625 pn
->pn_count
>= BIG_OBJECT_INIT
&&
3626 JS_DHashTableInit(&data
->table
, &FindPropValOps
, pn
,
3627 sizeof(FindPropValEntry
),
3628 JS_DHASH_DEFAULT_CAPACITY(pn
->pn_count
)))
3630 for (pn
= pnhead
; pn
; pn
= pn
->pn_next
) {
3631 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3632 ASSERT_VALID_PROPERTY_KEY(pn
->pn_left
);
3633 entry
= (FindPropValEntry
*)
3634 JS_DHashTableOperate(&data
->table
, pn
->pn_left
,
3636 entry
->pnval
= pn
->pn_right
;
3640 return pnhit
->pn_right
;
3644 * If data is null, the caller is AssignExpr and instead of binding variables,
3645 * we specialize lvalues in the propery value positions of the left-hand side.
3646 * If right is null, just check for well-formed lvalues.
3648 * See also UndominateInitializers, immediately below. If you change either of
3649 * these functions, you might have to change the other to match.
3652 CheckDestructuring(JSContext
*cx
, BindData
*data
,
3653 JSParseNode
*left
, JSParseNode
*right
,
3657 FindPropValData fpvd
;
3658 JSParseNode
*lhs
, *rhs
, *pn
, *pn2
;
3660 if (left
->pn_type
== TOK_ARRAYCOMP
) {
3661 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), left
,
3662 JSREPORT_ERROR
, JSMSG_ARRAY_COMP_LEFTSIDE
);
3666 #if JS_HAS_DESTRUCTURING_SHORTHAND
3667 if (right
&& right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
3668 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), right
,
3669 JSREPORT_ERROR
, JSMSG_BAD_OBJECT_INIT
);
3674 fpvd
.table
.ops
= NULL
;
3675 lhs
= left
->pn_head
;
3676 if (left
->pn_type
== TOK_RB
) {
3677 rhs
= (right
&& right
->pn_type
== left
->pn_type
)
3682 pn
= lhs
, pn2
= rhs
;
3684 /* Skip parenthesization if not in a variable declaration. */
3685 while (pn
->pn_type
== TOK_RP
)
3688 while (pn2
->pn_type
== TOK_RP
)
3693 /* Nullary comma is an elision; binary comma is an expression.*/
3694 if (pn
->pn_type
!= TOK_COMMA
|| pn
->pn_arity
!= PN_NULLARY
) {
3695 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3696 ok
= CheckDestructuring(cx
, data
, pn
, pn2
, tc
);
3699 if (pn
->pn_type
!= TOK_NAME
)
3702 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3704 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3716 JS_ASSERT(left
->pn_type
== TOK_RC
);
3717 fpvd
.numvars
= left
->pn_count
;
3722 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
3725 /* Skip parenthesization if not in a variable declaration. */
3726 while (pn
->pn_type
== TOK_RP
)
3730 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3732 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
3734 while (rhs
->pn_type
== TOK_RP
)
3739 ok
= CheckDestructuring(cx
, data
, pn
, rhs
, tc
);
3741 if (pn
->pn_type
!= TOK_NAME
)
3744 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3746 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3756 * The catch/finally handler implementation in the interpreter assumes
3757 * that any operation that introduces a new scope (like a "let" or "with"
3758 * block) increases the stack depth. This way, it is possible to restore
3759 * the scope chain based on stack depth of the handler alone. "let" with
3760 * an empty destructuring pattern like in
3764 * would violate this assumption as the there would be no let locals to
3765 * store on the stack. To satisfy it we add an empty property to such
3766 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3767 * slots, would be always positive.
3769 * Note that we add such a property even if the block has locals due to
3770 * later let declarations in it. We optimize for code simplicity here,
3771 * not the fastest runtime performance with empty [] or {}.
3774 data
->binder
== BindLet
&&
3775 OBJ_BLOCK_COUNT(cx
, tc
->blockChain
) == 0) {
3776 ok
= !!js_DefineNativeProperty(cx
, tc
->blockChain
,
3777 ATOM_TO_JSID(cx
->runtime
->
3778 atomState
.emptyAtom
),
3779 JSVAL_VOID
, NULL
, NULL
,
3783 SPROP_HAS_SHORTID
, 0, NULL
);
3792 JS_DHashTableFinish(&fpvd
.table
);
3796 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
, JSREPORT_ERROR
,
3797 JSMSG_NO_VARIABLE_NAME
);
3803 * This is a greatly pared down version of CheckDestructuring that extends the
3804 * pn_pos.end source coordinate of each name in a destructuring binding such as
3806 * var [x, y] = [function () y, 42];
3808 * to cover its corresponding initializer, so that the initialized binding does
3809 * not appear to dominate any closures in its initializer. See bug 496134.
3811 * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
3812 * not very precise. With one-pass SSA construction from structured source code
3813 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3814 * Languages", Brandis and Mössenböck), we could do much better.
3816 * See CheckDestructuring, immediately above. If you change either of these
3817 * functions, you might have to change the other to match.
3820 UndominateInitializers(JSParseNode
*left
, JSParseNode
*right
, JSTreeContext
*tc
)
3822 FindPropValData fpvd
;
3823 JSParseNode
*lhs
, *rhs
;
3825 JS_ASSERT(left
->pn_type
!= TOK_ARRAYCOMP
);
3828 #if JS_HAS_DESTRUCTURING_SHORTHAND
3829 if (right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
3830 js_ReportCompileErrorNumber(tc
->compiler
->context
, TS(tc
->compiler
), right
,
3831 JSREPORT_ERROR
, JSMSG_BAD_OBJECT_INIT
);
3836 if (right
->pn_type
!= left
->pn_type
)
3839 fpvd
.table
.ops
= NULL
;
3840 lhs
= left
->pn_head
;
3841 if (left
->pn_type
== TOK_RB
) {
3842 rhs
= right
->pn_head
;
3844 while (lhs
&& rhs
) {
3845 /* Nullary comma is an elision; binary comma is an expression.*/
3846 if (lhs
->pn_type
!= TOK_COMMA
|| lhs
->pn_arity
!= PN_NULLARY
) {
3847 if (lhs
->pn_type
== TOK_RB
|| lhs
->pn_type
== TOK_RC
) {
3848 if (!UndominateInitializers(lhs
, rhs
, tc
))
3851 lhs
->pn_pos
.end
= rhs
->pn_pos
.end
;
3859 JS_ASSERT(left
->pn_type
== TOK_RC
);
3860 fpvd
.numvars
= left
->pn_count
;
3864 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
3865 JSParseNode
*pn
= lhs
->pn_right
;
3867 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
3868 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3869 if (rhs
&& !UndominateInitializers(pn
, rhs
, tc
))
3873 pn
->pn_pos
.end
= rhs
->pn_pos
.end
;
3882 static JSParseNode
*
3883 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
3889 ts
= TS(tc
->compiler
);
3890 ts
->flags
|= TSF_DESTRUCTURING
;
3891 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
3892 ts
->flags
&= ~TSF_DESTRUCTURING
;
3895 if (!CheckDestructuring(cx
, data
, pn
, NULL
, tc
))
3901 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
3902 * This function assumes the cloned tree is for use in the same statement and
3903 * binding context as the original tree.
3905 static JSParseNode
*
3906 CloneParseTree(JSParseNode
*opn
, JSTreeContext
*tc
)
3908 JSParseNode
*pn
, *pn2
, *opn2
;
3910 pn
= NewOrRecycledNode(tc
);
3913 pn
->pn_type
= opn
->pn_type
;
3914 pn
->pn_pos
= opn
->pn_pos
;
3915 pn
->pn_op
= opn
->pn_op
;
3916 pn
->pn_used
= opn
->pn_used
;
3917 pn
->pn_defn
= opn
->pn_defn
;
3918 pn
->pn_arity
= opn
->pn_arity
;
3920 switch (pn
->pn_arity
) {
3921 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
3924 NULLCHECK(pn
->pn_funbox
=
3925 tc
->compiler
->newFunctionBox(opn
->pn_funbox
->object
, pn
, tc
));
3926 NULLCHECK(pn
->pn_body
= CloneParseTree(opn
->pn_body
, tc
));
3927 pn
->pn_cookie
= opn
->pn_cookie
;
3928 pn
->pn_dflags
= opn
->pn_dflags
;
3929 pn
->pn_blockid
= opn
->pn_blockid
;
3934 for (opn2
= opn
->pn_head
; opn2
; opn2
= opn2
->pn_next
) {
3935 NULLCHECK(pn2
= CloneParseTree(opn2
, tc
));
3938 pn
->pn_xflags
= opn
->pn_xflags
;
3942 NULLCHECK(pn
->pn_kid1
= CloneParseTree(opn
->pn_kid1
, tc
));
3943 NULLCHECK(pn
->pn_kid2
= CloneParseTree(opn
->pn_kid2
, tc
));
3944 NULLCHECK(pn
->pn_kid3
= CloneParseTree(opn
->pn_kid3
, tc
));
3948 NULLCHECK(pn
->pn_left
= CloneParseTree(opn
->pn_left
, tc
));
3949 if (opn
->pn_right
!= opn
->pn_left
)
3950 NULLCHECK(pn
->pn_right
= CloneParseTree(opn
->pn_right
, tc
));
3952 pn
->pn_right
= pn
->pn_left
;
3953 pn
->pn_val
= opn
->pn_val
;
3954 pn
->pn_iflags
= opn
->pn_iflags
;
3958 NULLCHECK(pn
->pn_kid
= CloneParseTree(opn
->pn_kid
, tc
));
3959 pn
->pn_num
= opn
->pn_num
;
3960 pn
->pn_hidden
= opn
->pn_hidden
;
3964 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
3965 pn
->pn_u
= opn
->pn_u
;
3968 * The old name is a use of its pn_lexdef. Make the clone also be a
3969 * use of that definition.
3971 JSDefinition
*dn
= pn
->pn_lexdef
;
3973 pn
->pn_link
= dn
->dn_uses
;
3975 } else if (opn
->pn_expr
) {
3976 NULLCHECK(pn
->pn_expr
= CloneParseTree(opn
->pn_expr
, tc
));
3979 * If the old name is a definition, the new one has pn_defn set.
3980 * Make the old name a use of the new node.
3983 opn
->pn_defn
= false;
3984 LinkUseToDef(opn
, (JSDefinition
*) pn
, tc
);
3990 pn
->pn_names
= opn
->pn_names
;
3991 NULLCHECK(pn
->pn_tree
= CloneParseTree(opn
->pn_tree
, tc
));
3995 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
3996 pn
->pn_u
= opn
->pn_u
;
4004 #endif /* JS_HAS_DESTRUCTURING */
4006 extern const char js_with_statement_str
[];
4008 static JSParseNode
*
4009 ContainsStmt(JSParseNode
*pn
, JSTokenType tt
)
4011 JSParseNode
*pn2
, *pnt
;
4015 if (PN_TYPE(pn
) == tt
)
4017 switch (pn
->pn_arity
) {
4019 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
4020 pnt
= ContainsStmt(pn2
, tt
);
4026 pnt
= ContainsStmt(pn
->pn_kid1
, tt
);
4029 pnt
= ContainsStmt(pn
->pn_kid2
, tt
);
4032 return ContainsStmt(pn
->pn_kid3
, tt
);
4035 * Limit recursion if pn is a binary expression, which can't contain a
4038 if (pn
->pn_op
!= JSOP_NOP
)
4040 pnt
= ContainsStmt(pn
->pn_left
, tt
);
4043 return ContainsStmt(pn
->pn_right
, tt
);
4045 if (pn
->pn_op
!= JSOP_NOP
)
4047 return ContainsStmt(pn
->pn_kid
, tt
);
4049 return ContainsStmt(pn
->maybeExpr(), tt
);
4051 return ContainsStmt(pn
->pn_tree
, tt
);
4057 static JSParseNode
*
4058 ReturnOrYield(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4059 JSParser operandParser
)
4061 JSTokenType tt
, tt2
;
4062 JSParseNode
*pn
, *pn2
;
4064 tt
= CURRENT_TOKEN(ts
).type
;
4065 if (tt
== TOK_RETURN
&& !(tc
->flags
& TCF_IN_FUNCTION
)) {
4066 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4067 JSMSG_BAD_RETURN_OR_YIELD
, js_return_str
);
4071 pn
= NewParseNode(PN_UNARY
, tc
);
4075 #if JS_HAS_GENERATORS
4076 if (tt
== TOK_YIELD
)
4077 tc
->flags
|= TCF_FUN_IS_GENERATOR
;
4080 /* This is ugly, but we don't want to require a semicolon. */
4081 ts
->flags
|= TSF_OPERAND
;
4082 tt2
= js_PeekTokenSameLine(cx
, ts
);
4083 ts
->flags
&= ~TSF_OPERAND
;
4084 if (tt2
== TOK_ERROR
)
4087 if (tt2
!= TOK_EOF
&& tt2
!= TOK_EOL
&& tt2
!= TOK_SEMI
&& tt2
!= TOK_RC
4088 #if JS_HAS_GENERATORS
4089 && (tt
!= TOK_YIELD
||
4090 (tt2
!= tt
&& tt2
!= TOK_RB
&& tt2
!= TOK_RP
&&
4091 tt2
!= TOK_COLON
&& tt2
!= TOK_COMMA
))
4094 pn2
= operandParser(cx
, ts
, tc
);
4097 #if JS_HAS_GENERATORS
4098 if (tt
== TOK_RETURN
)
4100 tc
->flags
|= TCF_RETURN_EXPR
;
4101 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4104 #if JS_HAS_GENERATORS
4105 if (tt
== TOK_RETURN
)
4107 tc
->flags
|= TCF_RETURN_VOID
;
4110 if ((~tc
->flags
& (TCF_RETURN_EXPR
| TCF_FUN_IS_GENERATOR
)) == 0) {
4111 /* As in Python (see PEP-255), disallow return v; in generators. */
4112 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
4113 JSMSG_BAD_GENERATOR_RETURN
,
4114 JSMSG_BAD_ANON_GENERATOR_RETURN
);
4118 if (JS_HAS_STRICT_OPTION(cx
) &&
4119 (~tc
->flags
& (TCF_RETURN_EXPR
| TCF_RETURN_VOID
)) == 0 &&
4120 !ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
4121 JSMSG_NO_RETURN_VALUE
,
4122 JSMSG_ANON_NO_RETURN_VALUE
)) {
4129 static JSParseNode
*
4130 PushLexicalScope(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4135 JSObjectBox
*blockbox
;
4137 pn
= NewParseNode(PN_NAME
, tc
);
4141 obj
= js_NewBlockObject(cx
);
4145 blockbox
= tc
->compiler
->newObjectBox(obj
);
4149 js_PushBlockScope(tc
, stmt
, obj
, -1);
4150 pn
->pn_type
= TOK_LEXICALSCOPE
;
4151 pn
->pn_op
= JSOP_LEAVEBLOCK
;
4152 pn
->pn_objbox
= blockbox
;
4153 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
4155 if (!GenerateBlockId(tc
, stmt
->blockid
))
4157 pn
->pn_blockid
= stmt
->blockid
;
4161 #if JS_HAS_BLOCK_SCOPE
4163 static JSParseNode
*
4164 LetBlock(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, JSBool statement
)
4166 JSParseNode
*pn
, *pnblock
, *pnlet
;
4167 JSStmtInfo stmtInfo
;
4169 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LET
);
4171 /* Create the let binary node. */
4172 pnlet
= NewParseNode(PN_BINARY
, tc
);
4176 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_LET
);
4178 /* This is a let block or expression of the form: let (a, b, c) .... */
4179 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
4183 pn
->pn_expr
= pnlet
;
4185 pnlet
->pn_left
= Variables(cx
, ts
, tc
, true);
4186 if (!pnlet
->pn_left
)
4188 pnlet
->pn_left
->pn_xflags
= PNX_POPVAR
;
4190 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_LET
);
4192 ts
->flags
|= TSF_OPERAND
;
4193 if (statement
&& !js_MatchToken(cx
, ts
, TOK_LC
)) {
4195 * If this is really an expression in let statement guise, then we
4196 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4197 * the return value of the expression.
4199 pn
= NewParseNode(PN_UNARY
, tc
);
4202 pn
->pn_type
= TOK_SEMI
;
4204 pn
->pn_kid
= pnblock
;
4206 statement
= JS_FALSE
;
4208 ts
->flags
&= ~TSF_OPERAND
;
4211 pnlet
->pn_right
= Statements(cx
, ts
, tc
);
4212 if (!pnlet
->pn_right
)
4214 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_LET
);
4217 * Change pnblock's opcode to the variant that propagates the last
4218 * result down after popping the block, and clear statement.
4220 pnblock
->pn_op
= JSOP_LEAVEBLOCKEXPR
;
4221 pnlet
->pn_right
= AssignExpr(cx
, ts
, tc
);
4222 if (!pnlet
->pn_right
)
4230 #endif /* JS_HAS_BLOCK_SCOPE */
4233 PushBlocklikeStatement(JSStmtInfo
*stmt
, JSStmtType type
, JSTreeContext
*tc
)
4235 js_PushStatement(tc
, stmt
, type
, -1);
4236 return GenerateBlockId(tc
, stmt
->blockid
);
4239 static JSParseNode
*
4240 NewBindingNode(JSTokenStream
*ts
, JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
4242 JSParseNode
*pn
= NULL
;
4244 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
4247 JS_ASSERT(!pn
->isPlaceholder());
4249 ale
= tc
->lexdeps
.lookup(atom
);
4252 JS_ASSERT(pn
->isPlaceholder());
4257 JS_ASSERT(pn
->pn_defn
);
4260 * A let binding at top level becomes a var before we get here, so if
4261 * pn and tc have the same blockid then that id must not be the bodyid.
4262 * If pn is a forward placeholder definition from the same or a higher
4263 * block then we claim it.
4265 JS_ASSERT_IF(let
&& pn
->pn_blockid
== tc
->blockid(),
4266 pn
->pn_blockid
!= tc
->bodyid
);
4268 if (pn
->isPlaceholder() && pn
->pn_blockid
>= (let
? tc
->blockid() : tc
->bodyid
)) {
4270 pn
->pn_blockid
= tc
->blockid();
4272 tc
->lexdeps
.remove(tc
->compiler
, atom
);
4277 /* Make a new node for this declarator name (or destructuring pattern). */
4278 pn
= NewNameNode(tc
->compiler
->context
, ts
, atom
, tc
);
4284 #if JS_HAS_BLOCK_SCOPE
4286 RebindLets(JSParseNode
*pn
, JSTreeContext
*tc
)
4291 switch (pn
->pn_arity
) {
4293 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
4294 RebindLets(pn2
, tc
);
4298 RebindLets(pn
->pn_kid1
, tc
);
4299 RebindLets(pn
->pn_kid2
, tc
);
4300 RebindLets(pn
->pn_kid3
, tc
);
4304 RebindLets(pn
->pn_left
, tc
);
4305 RebindLets(pn
->pn_right
, tc
);
4309 RebindLets(pn
->pn_kid
, tc
);
4313 RebindLets(pn
->pn_body
, tc
);
4317 RebindLets(pn
->maybeExpr(), tc
);
4320 JS_ASSERT(pn
->pn_blockid
> tc
->topStmt
->blockid
);
4321 } else if (pn
->pn_used
) {
4322 if (pn
->pn_lexdef
->pn_blockid
== tc
->topStmt
->blockid
) {
4325 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
4327 while ((ale
= ALE_NEXT(ale
)) != NULL
) {
4328 if (ALE_ATOM(ale
) == pn
->pn_atom
) {
4329 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4335 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
4337 ale
= MakePlaceholder(pn
, tc
);
4341 JSDefinition
*dn
= ALE_DEFN(ale
);
4342 dn
->pn_type
= TOK_NAME
;
4343 dn
->pn_op
= JSOP_NOP
;
4344 dn
->pn_dflags
|= pn
->pn_dflags
& PND_USE2DEF_FLAGS
;
4346 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4352 RebindLets(pn
->pn_tree
, tc
);
4358 #endif /* JS_HAS_BLOCK_SCOPE */
4360 static JSParseNode
*
4361 Statement(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4364 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
, *pn4
;
4365 JSStmtInfo stmtInfo
, *stmt
, *stmt2
;
4368 JS_CHECK_RECURSION(cx
, return NULL
);
4370 ts
->flags
|= TSF_OPERAND
;
4371 tt
= js_GetToken(cx
, ts
);
4372 ts
->flags
&= ~TSF_OPERAND
;
4374 #if JS_HAS_GETTER_SETTER
4375 if (tt
== TOK_NAME
) {
4376 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
4377 if (tt
== TOK_ERROR
)
4384 #if JS_HAS_XML_SUPPORT
4385 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
4386 tt
= js_PeekToken(cx
, ts
);
4387 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
4388 if (tt
== TOK_DBLCOLON
)
4391 return FunctionStmt(cx
, ts
, tc
);
4394 /* An IF node has three kids: condition, then, and optional else. */
4395 pn
= NewParseNode(PN_TERNARY
, tc
);
4398 pn1
= Condition(cx
, ts
, tc
);
4401 js_PushStatement(tc
, &stmtInfo
, STMT_IF
, -1);
4402 pn2
= Statement(cx
, ts
, tc
);
4405 ts
->flags
|= TSF_OPERAND
;
4406 if (js_MatchToken(cx
, ts
, TOK_ELSE
)) {
4407 ts
->flags
&= ~TSF_OPERAND
;
4408 stmtInfo
.type
= STMT_ELSE
;
4409 pn3
= Statement(cx
, ts
, tc
);
4412 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
4414 ts
->flags
&= ~TSF_OPERAND
;
4416 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4426 JSParseNode
*pn5
, *saveBlock
;
4427 JSBool seenDefault
= JS_FALSE
;
4429 pn
= NewParseNode(PN_BINARY
, tc
);
4432 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_SWITCH
);
4434 /* pn1 points to the switch's discriminant. */
4435 pn1
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
4439 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_SWITCH
);
4440 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_SWITCH
);
4443 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4444 * because that function states tc->topStmt->blockid.
4446 js_PushStatement(tc
, &stmtInfo
, STMT_SWITCH
, -1);
4448 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4449 pn2
= NewParseNode(PN_LIST
, tc
);
4453 if (!GenerateBlockIdForStmtNode(pn2
, tc
))
4455 saveBlock
= tc
->blockNode
;
4456 tc
->blockNode
= pn2
;
4458 while ((tt
= js_GetToken(cx
, ts
)) != TOK_RC
) {
4462 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4463 JSMSG_TOO_MANY_DEFAULTS
);
4466 seenDefault
= JS_TRUE
;
4470 pn3
= NewParseNode(PN_BINARY
, tc
);
4473 if (tt
== TOK_CASE
) {
4474 pn3
->pn_left
= Expr(cx
, ts
, tc
);
4479 if (pn2
->pn_count
== JS_BIT(16)) {
4480 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4481 JSMSG_TOO_MANY_CASES
);
4490 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4494 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_AFTER_CASE
);
4496 pn4
= NewParseNode(PN_LIST
, tc
);
4499 pn4
->pn_type
= TOK_LC
;
4501 ts
->flags
|= TSF_OPERAND
;
4502 while ((tt
= js_PeekToken(cx
, ts
)) != TOK_RC
&&
4503 tt
!= TOK_CASE
&& tt
!= TOK_DEFAULT
) {
4504 ts
->flags
&= ~TSF_OPERAND
;
4505 if (tt
== TOK_ERROR
)
4507 pn5
= Statement(cx
, ts
, tc
);
4510 pn4
->pn_pos
.end
= pn5
->pn_pos
.end
;
4512 ts
->flags
|= TSF_OPERAND
;
4514 ts
->flags
&= ~TSF_OPERAND
;
4516 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4518 pn4
->pn_pos
.begin
= pn4
->pn_head
->pn_pos
.begin
;
4519 pn3
->pn_pos
.end
= pn4
->pn_pos
.end
;
4520 pn3
->pn_right
= pn4
;
4524 * Handle the case where there was a let declaration in any case in
4525 * the switch body, but not within an inner block. If it replaced
4526 * tc->blockNode with a new block node then we must refresh pn2 and
4527 * then restore tc->blockNode.
4529 if (tc
->blockNode
!= pn2
)
4530 pn2
= tc
->blockNode
;
4531 tc
->blockNode
= saveBlock
;
4534 pn
->pn_pos
.end
= pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4541 pn
= NewParseNode(PN_BINARY
, tc
);
4544 js_PushStatement(tc
, &stmtInfo
, STMT_WHILE_LOOP
, -1);
4545 pn2
= Condition(cx
, ts
, tc
);
4549 pn2
= Statement(cx
, ts
, tc
);
4553 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4558 pn
= NewParseNode(PN_BINARY
, tc
);
4561 js_PushStatement(tc
, &stmtInfo
, STMT_DO_LOOP
, -1);
4562 pn2
= Statement(cx
, ts
, tc
);
4566 MUST_MATCH_TOKEN(TOK_WHILE
, JSMSG_WHILE_AFTER_DO
);
4567 pn2
= Condition(cx
, ts
, tc
);
4571 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4573 if (JSVERSION_NUMBER(cx
) != JSVERSION_ECMA_3
) {
4575 * All legacy and extended versions must do automatic semicolon
4576 * insertion after do-while. See the testcase and discussion in
4577 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4579 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
4586 JSParseNode
*pnseq
= NULL
;
4587 #if JS_HAS_BLOCK_SCOPE
4588 JSParseNode
*pnlet
= NULL
;
4589 JSStmtInfo blockInfo
;
4592 /* A FOR node is binary, left is loop control and right is the body. */
4593 pn
= NewParseNode(PN_BINARY
, tc
);
4596 js_PushStatement(tc
, &stmtInfo
, STMT_FOR_LOOP
, -1);
4598 pn
->pn_op
= JSOP_ITER
;
4600 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
4601 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
4602 pn
->pn_iflags
= JSITER_FOREACH
;
4607 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
4608 ts
->flags
|= TSF_OPERAND
;
4609 tt
= js_PeekToken(cx
, ts
);
4610 ts
->flags
&= ~TSF_OPERAND
;
4612 #if JS_HAS_BLOCK_SCOPE
4616 if (tt
== TOK_SEMI
) {
4617 if (pn
->pn_iflags
& JSITER_FOREACH
)
4620 /* No initializer -- set first kid of left sub-node to null. */
4624 * Set pn1 to a var list or an initializing expression.
4626 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4627 * of the for statement. This flag will be used by the RelExpr
4628 * production; if it is set, then the 'in' keyword will not be
4629 * recognized as an operator, leaving it available to be parsed as
4630 * part of a for/in loop.
4632 * A side effect of this restriction is that (unparenthesized)
4633 * expressions involving an 'in' operator are illegal in the init
4634 * clause of an ordinary for loop.
4636 tc
->flags
|= TCF_IN_FOR_INIT
;
4637 if (tt
== TOK_VAR
) {
4638 (void) js_GetToken(cx
, ts
);
4639 pn1
= Variables(cx
, ts
, tc
, false);
4640 #if JS_HAS_BLOCK_SCOPE
4641 } else if (tt
== TOK_LET
) {
4643 (void) js_GetToken(cx
, ts
);
4644 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
4645 pn1
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
4646 tt
= TOK_LEXICALSCOPE
;
4648 pnlet
= PushLexicalScope(cx
, ts
, tc
, &blockInfo
);
4651 blockInfo
.flags
|= SIF_FOR_BLOCK
;
4652 pn1
= Variables(cx
, ts
, tc
, false);
4656 pn1
= Expr(cx
, ts
, tc
);
4658 while (pn1
->pn_type
== TOK_RP
)
4662 tc
->flags
&= ~TCF_IN_FOR_INIT
;
4668 * We can be sure that it's a for/in loop if there's still an 'in'
4669 * keyword here, even if JavaScript recognizes 'in' as an operator,
4670 * as we've excluded 'in' from being parsed in RelExpr by setting
4671 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4673 if (pn1
&& js_MatchToken(cx
, ts
, TOK_IN
)) {
4674 pn
->pn_iflags
|= JSITER_ENUMERATE
;
4675 stmtInfo
.type
= STMT_FOR_IN_LOOP
;
4677 /* Check that the left side of the 'in' is valid. */
4678 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt
) || PN_TYPE(pn1
) == tt
);
4679 if (TOKEN_TYPE_IS_DECL(tt
)
4680 ? (pn1
->pn_count
> 1 || pn1
->pn_op
== JSOP_DEFCONST
4681 #if JS_HAS_DESTRUCTURING
4682 || (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
4683 pn
->pn_op
== JSOP_ITER
&&
4684 !(pn
->pn_iflags
& JSITER_FOREACH
) &&
4685 (pn1
->pn_head
->pn_type
== TOK_RC
||
4686 (pn1
->pn_head
->pn_type
== TOK_RB
&&
4687 pn1
->pn_head
->pn_count
!= 2) ||
4688 (pn1
->pn_head
->pn_type
== TOK_ASSIGN
&&
4689 (pn1
->pn_head
->pn_left
->pn_type
!= TOK_RB
||
4690 pn1
->pn_head
->pn_left
->pn_count
!= 2))))
4693 : (pn1
->pn_type
!= TOK_NAME
&&
4694 pn1
->pn_type
!= TOK_DOT
&&
4695 #if JS_HAS_DESTRUCTURING
4696 ((JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
4697 pn
->pn_op
== JSOP_ITER
&&
4698 !(pn
->pn_iflags
& JSITER_FOREACH
))
4699 ? (pn1
->pn_type
!= TOK_RB
|| pn1
->pn_count
!= 2)
4700 : (pn1
->pn_type
!= TOK_RB
&& pn1
->pn_type
!= TOK_RC
)) &&
4702 #if JS_HAS_LVALUE_RETURN
4703 pn1
->pn_type
!= TOK_LP
&&
4705 #if JS_HAS_XML_SUPPORT
4706 (pn1
->pn_type
!= TOK_UNARYOP
||
4707 pn1
->pn_op
!= JSOP_XMLNAME
) &&
4709 pn1
->pn_type
!= TOK_LB
)) {
4710 js_ReportCompileErrorNumber(cx
, ts
, pn1
, JSREPORT_ERROR
,
4711 JSMSG_BAD_FOR_LEFTSIDE
);
4715 /* pn2 points to the name or destructuring pattern on in's left. */
4717 uintN dflag
= PND_ASSIGNED
;
4719 if (TOKEN_TYPE_IS_DECL(tt
)) {
4720 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4721 pn1
->pn_xflags
|= PNX_FORINVAR
;
4724 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4725 * 'var', or 'const' to hoist the initializer or the entire
4726 * decl out of the loop head. TOK_VAR is the type for both
4727 * 'var' and 'const'.
4730 if ((pn2
->pn_type
== TOK_NAME
&& pn2
->maybeExpr())
4731 #if JS_HAS_DESTRUCTURING
4732 || pn2
->pn_type
== TOK_ASSIGN
4735 pnseq
= NewParseNode(PN_LIST
, tc
);
4738 pnseq
->pn_type
= TOK_SEQ
;
4739 pnseq
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4741 #if JS_HAS_BLOCK_SCOPE
4742 if (tt
== TOK_LET
) {
4744 * Hoist just the 'i' from 'for (let x = i in o)' to
4745 * before the loop, glued together via pnseq.
4747 pn3
= NewParseNode(PN_UNARY
, tc
);
4750 pn3
->pn_type
= TOK_SEMI
;
4751 pn3
->pn_op
= JSOP_NOP
;
4752 #if JS_HAS_DESTRUCTURING
4753 if (pn2
->pn_type
== TOK_ASSIGN
) {
4754 pn4
= pn2
->pn_right
;
4755 pn2
= pn1
->pn_head
= pn2
->pn_left
;
4760 pn2
->pn_expr
= NULL
;
4762 if (!RebindLets(pn4
, tc
))
4764 pn3
->pn_pos
= pn4
->pn_pos
;
4766 pnseq
->initList(pn3
);
4768 #endif /* JS_HAS_BLOCK_SCOPE */
4770 dflag
= PND_INITIALIZED
;
4773 * All of 'var x = i' is hoisted above 'for (x in o)',
4774 * so clear PNX_FORINVAR.
4776 * Request JSOP_POP here since the var is for a simple
4777 * name (it is not a destructuring binding's left-hand
4778 * side) and it has an initializer.
4780 pn1
->pn_xflags
&= ~PNX_FORINVAR
;
4781 pn1
->pn_xflags
|= PNX_POPVAR
;
4782 pnseq
->initList(pn1
);
4784 #if JS_HAS_DESTRUCTURING
4785 if (pn2
->pn_type
== TOK_ASSIGN
) {
4786 pn1
= CloneParseTree(pn2
->pn_left
, tc
);
4792 JS_ASSERT(pn2
->pn_type
== TOK_NAME
);
4793 pn1
= NewNameNode(cx
, ts
, pn2
->pn_atom
, tc
);
4796 pn1
->pn_type
= TOK_NAME
;
4797 pn1
->pn_op
= JSOP_NAME
;
4798 pn1
->pn_pos
= pn2
->pn_pos
;
4800 LinkUseToDef(pn1
, (JSDefinition
*) pn2
, tc
);
4809 #if JS_HAS_LVALUE_RETURN
4810 if (pn2
->pn_type
== TOK_LP
&&
4811 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
)) {
4815 #if JS_HAS_XML_SUPPORT
4816 if (pn2
->pn_type
== TOK_UNARYOP
)
4817 pn2
->pn_op
= JSOP_BINDXMLNAME
;
4821 switch (pn2
->pn_type
) {
4823 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4824 NoteLValue(cx
, pn2
, tc
, dflag
);
4827 #if JS_HAS_DESTRUCTURING
4830 JS_ASSERT(pn2
->pn_type
== TOK_RB
|| pn2
->pn_type
== TOK_RC
);
4834 /* Check for valid lvalues in var-less destructuring for-in. */
4835 if (pn1
== pn2
&& !CheckDestructuring(cx
, NULL
, pn2
, NULL
, tc
))
4838 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
4840 * Destructuring for-in requires [key, value] enumeration
4843 JS_ASSERT(pn
->pn_op
== JSOP_ITER
);
4844 if (!(pn
->pn_iflags
& JSITER_FOREACH
))
4845 pn
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
4854 * Parse the object expression as the right operand of 'in', first
4855 * removing the top statement from the statement-stack if this is a
4856 * 'for (let x in y)' loop.
4858 #if JS_HAS_BLOCK_SCOPE
4859 JSStmtInfo
*save
= tc
->topStmt
;
4861 tc
->topStmt
= save
->down
;
4863 pn2
= Expr(cx
, ts
, tc
);
4864 #if JS_HAS_BLOCK_SCOPE
4869 pn2
= NewBinary(TOK_IN
, JSOP_NOP
, pn1
, pn2
, tc
);
4874 if (pn
->pn_iflags
& JSITER_FOREACH
)
4876 pn
->pn_op
= JSOP_NOP
;
4878 /* Parse the loop condition or null into pn2. */
4879 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
4880 ts
->flags
|= TSF_OPERAND
;
4881 tt
= js_PeekToken(cx
, ts
);
4882 ts
->flags
&= ~TSF_OPERAND
;
4883 if (tt
== TOK_SEMI
) {
4886 pn2
= Expr(cx
, ts
, tc
);
4891 /* Parse the update expression or null into pn3. */
4892 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
4893 ts
->flags
|= TSF_OPERAND
;
4894 tt
= js_PeekToken(cx
, ts
);
4895 ts
->flags
&= ~TSF_OPERAND
;
4899 pn3
= Expr(cx
, ts
, tc
);
4904 /* Build the FORHEAD node to use as the left kid of pn. */
4905 pn4
= NewParseNode(PN_TERNARY
, tc
);
4908 pn4
->pn_type
= TOK_FORHEAD
;
4909 pn4
->pn_op
= JSOP_NOP
;
4916 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
4918 /* Parse the loop body into pn->pn_right. */
4919 pn2
= Statement(cx
, ts
, tc
);
4924 /* Record the absolute line number for source note emission. */
4925 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4927 #if JS_HAS_BLOCK_SCOPE
4930 pnlet
->pn_expr
= pn
;
4935 pnseq
->pn_pos
.end
= pn
->pn_pos
.end
;
4943 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
4944 JSMSG_BAD_FOR_EACH_LOOP
);
4949 JSParseNode
*catchList
, *lastCatch
;
4952 * try nodes are ternary.
4953 * kid1 is the try Statement
4954 * kid2 is the catch node list or null
4955 * kid3 is the finally Statement
4957 * catch nodes are ternary.
4958 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
4959 * kid2 is the catch guard or null if no guard
4960 * kid3 is the catch block
4962 * catch lvalue nodes are either:
4963 * TOK_NAME for a single identifier
4964 * TOK_RB or TOK_RC for a destructuring left-hand side
4966 * finally nodes are TOK_LC Statement lists.
4968 pn
= NewParseNode(PN_TERNARY
, tc
);
4971 pn
->pn_op
= JSOP_NOP
;
4973 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_TRY
);
4974 if (!PushBlocklikeStatement(&stmtInfo
, STMT_TRY
, tc
))
4976 pn
->pn_kid1
= Statements(cx
, ts
, tc
);
4979 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_TRY
);
4983 tt
= js_GetToken(cx
, ts
);
4984 if (tt
== TOK_CATCH
) {
4985 catchList
= NewParseNode(PN_LIST
, tc
);
4988 catchList
->pn_type
= TOK_RESERVED
;
4989 catchList
->makeEmpty();
4993 JSParseNode
*pnblock
;
4996 /* Check for another catch after unconditional catch. */
4997 if (lastCatch
&& !lastCatch
->pn_kid2
) {
4998 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4999 JSMSG_CATCH_AFTER_GENERAL
);
5004 * Create a lexical scope node around the whole catch clause,
5005 * including the head.
5007 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
5010 stmtInfo
.type
= STMT_CATCH
;
5013 * Legal catch forms are:
5015 * catch (lhs if <boolean_expression>)
5016 * where lhs is a name or a destructuring left-hand side.
5017 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5019 pn2
= NewParseNode(PN_TERNARY
, tc
);
5022 pnblock
->pn_expr
= pn2
;
5023 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_CATCH
);
5026 * Contrary to ECMA Ed. 3, the catch variable is lexically
5027 * scoped, not a property of a new Object instance. This is
5028 * an intentional change that anticipates ECMA Ed. 4.
5032 data
.binder
= BindLet
;
5033 data
.let
.overflow
= JSMSG_TOO_MANY_CATCH_VARS
;
5035 tt
= js_GetToken(cx
, ts
);
5037 #if JS_HAS_DESTRUCTURING
5040 pn3
= DestructuringExpr(cx
, &data
, tc
, tt
);
5047 label
= CURRENT_TOKEN(ts
).t_atom
;
5048 pn3
= NewBindingNode(ts
, label
, tc
, true);
5052 if (!data
.binder(cx
, &data
, label
, tc
))
5057 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5058 JSMSG_CATCH_IDENTIFIER
);
5063 #if JS_HAS_CATCH_GUARD
5065 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5066 * to avoid conflicting with the JS2/ECMAv4 type annotation
5067 * catchguard syntax.
5069 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
5070 pn2
->pn_kid2
= Expr(cx
, ts
, tc
);
5075 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_CATCH
);
5077 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_CATCH
);
5078 pn2
->pn_kid3
= Statements(cx
, ts
, tc
);
5081 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_CATCH
);
5084 catchList
->append(pnblock
);
5086 ts
->flags
|= TSF_OPERAND
;
5087 tt
= js_GetToken(cx
, ts
);
5088 ts
->flags
&= ~TSF_OPERAND
;
5089 } while (tt
== TOK_CATCH
);
5091 pn
->pn_kid2
= catchList
;
5093 if (tt
== TOK_FINALLY
) {
5094 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_FINALLY
);
5095 if (!PushBlocklikeStatement(&stmtInfo
, STMT_FINALLY
, tc
))
5097 pn
->pn_kid3
= Statements(cx
, ts
, tc
);
5100 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_FINALLY
);
5105 if (!catchList
&& !pn
->pn_kid3
) {
5106 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5107 JSMSG_CATCH_OR_FINALLY
);
5114 pn
= NewParseNode(PN_UNARY
, tc
);
5118 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5119 ts
->flags
|= TSF_OPERAND
;
5120 tt
= js_PeekTokenSameLine(cx
, ts
);
5121 ts
->flags
&= ~TSF_OPERAND
;
5122 if (tt
== TOK_ERROR
)
5124 if (tt
== TOK_EOF
|| tt
== TOK_EOL
|| tt
== TOK_SEMI
|| tt
== TOK_RC
) {
5125 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5126 JSMSG_SYNTAX_ERROR
);
5130 pn2
= Expr(cx
, ts
, tc
);
5133 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5134 pn
->pn_op
= JSOP_THROW
;
5138 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5140 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5141 JSMSG_CATCH_WITHOUT_TRY
);
5145 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5146 JSMSG_FINALLY_WITHOUT_TRY
);
5150 pn
= NewParseNode(PN_NULLARY
, tc
);
5153 if (!MatchLabel(cx
, ts
, pn
))
5156 label
= pn
->pn_atom
;
5158 for (; ; stmt
= stmt
->down
) {
5160 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5161 JSMSG_LABEL_NOT_FOUND
);
5164 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
)
5168 for (; ; stmt
= stmt
->down
) {
5170 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5174 if (STMT_IS_LOOP(stmt
) || stmt
->type
== STMT_SWITCH
)
5179 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5183 pn
= NewParseNode(PN_NULLARY
, tc
);
5186 if (!MatchLabel(cx
, ts
, pn
))
5189 label
= pn
->pn_atom
;
5191 for (stmt2
= NULL
; ; stmt
= stmt
->down
) {
5193 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5194 JSMSG_LABEL_NOT_FOUND
);
5197 if (stmt
->type
== STMT_LABEL
) {
5198 if (stmt
->label
== label
) {
5199 if (!stmt2
|| !STMT_IS_LOOP(stmt2
)) {
5200 js_ReportCompileErrorNumber(cx
, ts
, NULL
,
5202 JSMSG_BAD_CONTINUE
);
5212 for (; ; stmt
= stmt
->down
) {
5214 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5215 JSMSG_BAD_CONTINUE
);
5218 if (STMT_IS_LOOP(stmt
))
5223 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5227 pn
= NewParseNode(PN_BINARY
, tc
);
5230 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_WITH
);
5231 pn2
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
5234 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_WITH
);
5237 js_PushStatement(tc
, &stmtInfo
, STMT_WITH
, -1);
5238 pn2
= Statement(cx
, ts
, tc
);
5243 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5245 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5249 pn
= Variables(cx
, ts
, tc
, false);
5253 /* Tell js_EmitTree to generate a final POP. */
5254 pn
->pn_xflags
|= PNX_POPVAR
;
5257 #if JS_HAS_BLOCK_SCOPE
5261 JSObjectBox
*blockbox
;
5263 /* Check for a let statement or let expression. */
5264 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
5265 pn
= LetBlock(cx
, ts
, tc
, JS_TRUE
);
5266 if (!pn
|| pn
->pn_op
== JSOP_LEAVEBLOCK
)
5269 /* Let expressions require automatic semicolon insertion. */
5270 JS_ASSERT(pn
->pn_type
== TOK_SEMI
||
5271 pn
->pn_op
== JSOP_LEAVEBLOCKEXPR
);
5276 * This is a let declaration. We must be directly under a block per
5277 * the proposed ES4 specs, but not an implicit block created due to
5278 * 'for (let ...)'. If we pass this error test, make the enclosing
5279 * JSStmtInfo be our scope. Further let declarations in this block
5280 * will find this scope statement and use the same block object.
5282 * If we are the first let declaration in this block (i.e., when the
5283 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5284 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5288 (!STMT_MAYBE_SCOPE(stmt
) || (stmt
->flags
& SIF_FOR_BLOCK
))) {
5289 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5290 JSMSG_LET_DECL_NOT_IN_BLOCK
);
5294 if (stmt
&& (stmt
->flags
& SIF_SCOPE
)) {
5295 JS_ASSERT(tc
->blockChain
== stmt
->blockObj
);
5296 obj
= tc
->blockChain
;
5298 if (!stmt
|| (stmt
->flags
& SIF_BODY_BLOCK
)) {
5300 * ES4 specifies that let at top level and at body-block scope
5301 * does not shadow var, so convert back to var.
5303 CURRENT_TOKEN(ts
).type
= TOK_VAR
;
5304 CURRENT_TOKEN(ts
).t_op
= JSOP_DEFVAR
;
5306 pn
= Variables(cx
, ts
, tc
, false);
5309 pn
->pn_xflags
|= PNX_POPVAR
;
5314 * Some obvious assertions here, but they may help clarify the
5315 * situation. This stmt is not yet a scope, so it must not be a
5316 * catch block (catch is a lexical scope by definition).
5318 JS_ASSERT(!(stmt
->flags
& SIF_SCOPE
));
5319 JS_ASSERT(stmt
!= tc
->topScopeStmt
);
5320 JS_ASSERT(stmt
->type
== STMT_BLOCK
||
5321 stmt
->type
== STMT_SWITCH
||
5322 stmt
->type
== STMT_TRY
||
5323 stmt
->type
== STMT_FINALLY
);
5324 JS_ASSERT(!stmt
->downScope
);
5326 /* Convert the block statement into a scope statement. */
5327 JSObject
*obj
= js_NewBlockObject(tc
->compiler
->context
);
5331 blockbox
= tc
->compiler
->newObjectBox(obj
);
5336 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5337 * list stack, if it isn't already there. If it is there, but it
5338 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5341 stmt
->flags
|= SIF_SCOPE
;
5342 stmt
->downScope
= tc
->topScopeStmt
;
5343 tc
->topScopeStmt
= stmt
;
5344 JS_SCOPE_DEPTH_METERING(++tc
->scopeDepth
> tc
->maxScopeDepth
&&
5345 (tc
->maxScopeDepth
= tc
->scopeDepth
));
5347 STOBJ_SET_PARENT(obj
, tc
->blockChain
);
5348 tc
->blockChain
= obj
;
5349 stmt
->blockObj
= obj
;
5352 pn1
= tc
->blockNode
;
5353 JS_ASSERT(!pn1
|| pn1
->pn_type
!= TOK_LEXICALSCOPE
);
5356 /* Create a new lexical scope node for these statements. */
5357 pn1
= NewParseNode(PN_NAME
, tc
);
5361 pn1
->pn_type
= TOK_LEXICALSCOPE
;
5362 pn1
->pn_op
= JSOP_LEAVEBLOCK
;
5363 pn1
->pn_pos
= tc
->blockNode
->pn_pos
;
5364 pn1
->pn_objbox
= blockbox
;
5365 pn1
->pn_expr
= tc
->blockNode
;
5366 pn1
->pn_blockid
= tc
->blockNode
->pn_blockid
;
5367 tc
->blockNode
= pn1
;
5370 pn
= Variables(cx
, ts
, tc
, false);
5373 pn
->pn_xflags
= PNX_POPVAR
;
5376 #endif /* JS_HAS_BLOCK_SCOPE */
5379 pn
= ReturnOrYield(cx
, ts
, tc
, Expr
);
5388 oldflags
= tc
->flags
;
5389 tc
->flags
= oldflags
& ~TCF_HAS_FUNCTION_STMT
;
5390 if (!PushBlocklikeStatement(&stmtInfo
, STMT_BLOCK
, tc
))
5392 pn
= Statements(cx
, ts
, tc
);
5396 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_COMPOUND
);
5400 * If we contain a function statement and our container is top-level
5401 * or another block, flag pn to preserve braces when decompiling.
5403 if ((tc
->flags
& TCF_HAS_FUNCTION_STMT
) &&
5404 (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)) {
5405 pn
->pn_xflags
|= PNX_NEEDBRACES
;
5407 tc
->flags
= oldflags
| (tc
->flags
& (TCF_FUN_FLAGS
| TCF_RETURN_FLAGS
));
5413 pn
= NewParseNode(PN_UNARY
, tc
);
5416 pn
->pn_type
= TOK_SEMI
;
5419 #if JS_HAS_DEBUGGER_KEYWORD
5421 pn
= NewParseNode(PN_NULLARY
, tc
);
5424 pn
->pn_type
= TOK_DEBUGGER
;
5425 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5427 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5429 #if JS_HAS_XML_SUPPORT
5431 pn
= NewParseNode(PN_UNARY
, tc
);
5434 if (!js_MatchToken(cx
, ts
, TOK_NAME
) ||
5435 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.xmlAtom
||
5436 !js_MatchToken(cx
, ts
, TOK_NAME
) ||
5437 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.namespaceAtom
||
5438 !js_MatchToken(cx
, ts
, TOK_ASSIGN
) ||
5439 CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
5440 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5441 JSMSG_BAD_DEFAULT_XML_NAMESPACE
);
5444 pn2
= Expr(cx
, ts
, tc
);
5447 pn
->pn_op
= JSOP_DEFXMLNS
;
5448 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5457 #if JS_HAS_XML_SUPPORT
5461 pn2
= Expr(cx
, ts
, tc
);
5465 if (js_PeekToken(cx
, ts
) == TOK_COLON
) {
5466 if (pn2
->pn_type
!= TOK_NAME
) {
5467 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5471 label
= pn2
->pn_atom
;
5472 for (stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
5473 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
) {
5474 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5475 JSMSG_DUPLICATE_LABEL
);
5481 (void) js_GetToken(cx
, ts
);
5483 /* Push a label struct and parse the statement. */
5484 js_PushStatement(tc
, &stmtInfo
, STMT_LABEL
, -1);
5485 stmtInfo
.label
= label
;
5486 pn
= Statement(cx
, ts
, tc
);
5490 /* Normalize empty statement to empty block for the decompiler. */
5491 if (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
) {
5492 pn
->pn_type
= TOK_LC
;
5493 pn
->pn_arity
= PN_LIST
;
5497 /* Pop the label, set pn_expr, and return early. */
5499 pn2
->pn_type
= TOK_COLON
;
5500 pn2
->pn_pos
.end
= pn
->pn_pos
.end
;
5505 pn
= NewParseNode(PN_UNARY
, tc
);
5508 pn
->pn_type
= TOK_SEMI
;
5509 pn
->pn_pos
= pn2
->pn_pos
;
5514 /* Check termination of this primitive statement. */
5515 return MatchOrInsertSemicolon(cx
, ts
) ? pn
: NULL
;
5519 NoteArgumentsUse(JSTreeContext
*tc
)
5521 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
5522 tc
->flags
|= TCF_FUN_USES_ARGUMENTS
;
5524 tc
->funbox
->node
->pn_dflags
|= PND_FUNARG
;
5527 static JSParseNode
*
5528 Variables(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, bool inLetHead
)
5532 JSStmtInfo
*scopeStmt
;
5534 JSParseNode
*pn
, *pn2
;
5538 * The three options here are:
5539 * - TOK_LET: We are parsing a let declaration.
5540 * - TOK_LP: We are parsing the head of a let block.
5541 * - Otherwise, we're parsing var declarations.
5543 tt
= CURRENT_TOKEN(ts
).type
;
5544 let
= (tt
== TOK_LET
|| tt
== TOK_LP
);
5545 JS_ASSERT(let
|| tt
== TOK_VAR
);
5547 #if JS_HAS_BLOCK_SCOPE
5548 bool popScope
= (inLetHead
|| (let
&& (tc
->flags
& TCF_IN_FOR_INIT
)));
5549 JSStmtInfo
*save
= tc
->topStmt
, *saveScope
= tc
->topScopeStmt
;
5552 /* Make sure that Statement set up the tree context correctly. */
5553 scopeStmt
= tc
->topScopeStmt
;
5555 while (scopeStmt
&& !(scopeStmt
->flags
& SIF_SCOPE
)) {
5556 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt
));
5557 scopeStmt
= scopeStmt
->downScope
;
5559 JS_ASSERT(scopeStmt
);
5562 data
.op
= let
? JSOP_NOP
: CURRENT_TOKEN(ts
).t_op
;
5563 pn
= NewParseNode(PN_LIST
, tc
);
5566 pn
->pn_op
= data
.op
;
5570 * SpiderMonkey const is really "write once per initialization evaluation"
5571 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5572 * this code will change soon.
5575 JS_ASSERT(tc
->blockChain
== scopeStmt
->blockObj
);
5576 data
.binder
= BindLet
;
5577 data
.let
.overflow
= JSMSG_TOO_MANY_LOCALS
;
5579 data
.binder
= BindVarOrConst
;
5583 tt
= js_GetToken(cx
, ts
);
5584 #if JS_HAS_DESTRUCTURING
5585 if (tt
== TOK_LB
|| tt
== TOK_LC
) {
5586 ts
->flags
|= TSF_DESTRUCTURING
;
5587 pn2
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
5588 ts
->flags
&= ~TSF_DESTRUCTURING
;
5592 if (!CheckDestructuring(cx
, &data
, pn2
, NULL
, tc
))
5594 if ((tc
->flags
& TCF_IN_FOR_INIT
) &&
5595 js_PeekToken(cx
, ts
) == TOK_IN
) {
5600 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_BAD_DESTRUCT_DECL
);
5601 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
5604 #if JS_HAS_BLOCK_SCOPE
5606 tc
->topStmt
= save
->down
;
5607 tc
->topScopeStmt
= saveScope
->downScope
;
5610 JSParseNode
*init
= AssignExpr(cx
, ts
, tc
);
5611 #if JS_HAS_BLOCK_SCOPE
5614 tc
->topScopeStmt
= saveScope
;
5618 if (!init
|| !UndominateInitializers(pn2
, init
, tc
))
5621 pn2
= NewBinary(TOK_ASSIGN
, JSOP_NOP
, pn2
, init
, tc
);
5627 #endif /* JS_HAS_DESTRUCTURING */
5629 if (tt
!= TOK_NAME
) {
5630 if (tt
!= TOK_ERROR
) {
5631 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5632 JSMSG_NO_VARIABLE_NAME
);
5637 atom
= CURRENT_TOKEN(ts
).t_atom
;
5638 pn2
= NewBindingNode(ts
, atom
, tc
, let
);
5641 if (data
.op
== JSOP_DEFCONST
)
5642 pn2
->pn_dflags
|= PND_CONST
;
5644 if (!data
.binder(cx
, &data
, atom
, tc
))
5648 if (js_MatchToken(cx
, ts
, TOK_ASSIGN
)) {
5649 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
5652 #if JS_HAS_BLOCK_SCOPE
5654 tc
->topStmt
= save
->down
;
5655 tc
->topScopeStmt
= saveScope
->downScope
;
5658 JSParseNode
*init
= AssignExpr(cx
, ts
, tc
);
5659 #if JS_HAS_BLOCK_SCOPE
5662 tc
->topScopeStmt
= saveScope
;
5669 pn2
= MakeAssignment(pn2
, init
, tc
);
5673 pn2
->pn_expr
= init
;
5676 pn2
->pn_op
= (PN_OP(pn2
) == JSOP_ARGUMENTS
)
5678 : (pn2
->pn_dflags
& PND_GVAR
)
5680 : (pn2
->pn_dflags
& PND_BOUND
)
5682 : (data
.op
== JSOP_DEFCONST
)
5686 NoteLValue(cx
, pn2
, tc
, data
.fresh
? PND_INITIALIZED
: PND_ASSIGNED
);
5688 /* The declarator's position must include the initializer. */
5689 pn2
->pn_pos
.end
= init
->pn_pos
.end
;
5691 if ((tc
->flags
& TCF_IN_FUNCTION
) &&
5692 atom
== cx
->runtime
->atomState
.argumentsAtom
) {
5693 NoteArgumentsUse(tc
);
5695 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5698 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
5700 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5704 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5705 JSMSG_BAD_VAR_INIT
);
5709 static JSParseNode
*
5710 Expr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5712 JSParseNode
*pn
, *pn2
;
5714 pn
= AssignExpr(cx
, ts
, tc
);
5715 if (pn
&& js_MatchToken(cx
, ts
, TOK_COMMA
)) {
5716 pn2
= NewParseNode(PN_LIST
, tc
);
5719 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
5723 #if JS_HAS_GENERATORS
5725 if (pn2
->pn_type
== TOK_YIELD
) {
5726 js_ReportCompileErrorNumber(cx
, ts
, pn2
, JSREPORT_ERROR
,
5727 JSMSG_BAD_GENERATOR_SYNTAX
,
5732 pn2
= AssignExpr(cx
, ts
, tc
);
5736 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
5737 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5742 static JSParseNode
*
5743 AssignExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5745 JSParseNode
*pn
, *pn2
;
5749 JS_CHECK_RECURSION(cx
, return NULL
);
5751 #if JS_HAS_GENERATORS
5752 ts
->flags
|= TSF_OPERAND
;
5753 if (js_MatchToken(cx
, ts
, TOK_YIELD
)) {
5754 ts
->flags
&= ~TSF_OPERAND
;
5755 return ReturnOrYield(cx
, ts
, tc
, AssignExpr
);
5757 ts
->flags
&= ~TSF_OPERAND
;
5760 pn
= CondExpr(cx
, ts
, tc
);
5764 tt
= js_GetToken(cx
, ts
);
5765 #if JS_HAS_GETTER_SETTER
5766 if (tt
== TOK_NAME
) {
5767 tt
= CheckGetterOrSetter(cx
, ts
, TOK_ASSIGN
);
5768 if (tt
== TOK_ERROR
)
5772 if (tt
!= TOK_ASSIGN
) {
5777 op
= CURRENT_TOKEN(ts
).t_op
;
5778 for (pn2
= pn
; pn2
->pn_type
== TOK_RP
; pn2
= pn2
->pn_kid
)
5780 switch (pn2
->pn_type
) {
5782 pn2
->pn_op
= JSOP_SETNAME
;
5783 NoteLValue(cx
, pn2
, tc
);
5786 pn2
->pn_op
= JSOP_SETPROP
;
5789 pn2
->pn_op
= JSOP_SETELEM
;
5791 #if JS_HAS_DESTRUCTURING
5794 if (op
!= JSOP_NOP
) {
5795 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5796 JSMSG_BAD_DESTRUCT_ASS
);
5799 pn
= AssignExpr(cx
, ts
, tc
);
5800 if (!pn
|| !CheckDestructuring(cx
, NULL
, pn2
, pn
, tc
))
5802 return NewBinary(TOK_ASSIGN
, op
, pn2
, pn
, tc
);
5804 #if JS_HAS_LVALUE_RETURN
5806 if (!MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
5810 #if JS_HAS_XML_SUPPORT
5812 if (pn2
->pn_op
== JSOP_XMLNAME
) {
5813 pn2
->pn_op
= JSOP_SETXMLNAME
;
5819 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5820 JSMSG_BAD_LEFTSIDE_OF_ASS
);
5824 JSParseNode
*pn3
= AssignExpr(cx
, ts
, tc
);
5825 if (pn3
&& PN_TYPE(pn2
) == TOK_NAME
&& pn2
->pn_used
) {
5826 JSDefinition
*dn
= pn2
->pn_lexdef
;
5829 * If the definition is not flagged as assigned, we must have imputed
5830 * the initialized flag to it, to optimize for flat closures. But that
5831 * optimization uses source coordinates to check dominance relations,
5832 * so we must extend the end of the definition to cover the right-hand
5833 * side of this assignment, i.e., the initializer.
5835 if (!dn
->isAssigned()) {
5836 JS_ASSERT(dn
->isInitialized());
5837 dn
->pn_pos
.end
= pn3
->pn_pos
.end
;
5841 return NewBinary(TOK_ASSIGN
, op
, pn2
, pn3
, tc
);
5844 static JSParseNode
*
5845 CondExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5847 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
;
5850 pn
= OrExpr(cx
, ts
, tc
);
5851 if (pn
&& js_MatchToken(cx
, ts
, TOK_HOOK
)) {
5853 pn
= NewParseNode(PN_TERNARY
, tc
);
5857 * Always accept the 'in' operator in the middle clause of a ternary,
5858 * where it's unambiguous, even if we might be parsing the init of a
5861 oldflags
= tc
->flags
;
5862 tc
->flags
&= ~TCF_IN_FOR_INIT
;
5863 pn2
= AssignExpr(cx
, ts
, tc
);
5864 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
5868 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_IN_COND
);
5869 pn3
= AssignExpr(cx
, ts
, tc
);
5872 pn
->pn_pos
.begin
= pn1
->pn_pos
.begin
;
5873 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
5881 static JSParseNode
*
5882 OrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5886 pn
= AndExpr(cx
, ts
, tc
);
5887 while (pn
&& js_MatchToken(cx
, ts
, TOK_OR
))
5888 pn
= NewBinary(TOK_OR
, JSOP_OR
, pn
, AndExpr(cx
, ts
, tc
), tc
);
5892 static JSParseNode
*
5893 AndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5897 pn
= BitOrExpr(cx
, ts
, tc
);
5898 while (pn
&& js_MatchToken(cx
, ts
, TOK_AND
))
5899 pn
= NewBinary(TOK_AND
, JSOP_AND
, pn
, BitOrExpr(cx
, ts
, tc
), tc
);
5903 static JSParseNode
*
5904 BitOrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5908 pn
= BitXorExpr(cx
, ts
, tc
);
5909 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITOR
)) {
5910 pn
= NewBinary(TOK_BITOR
, JSOP_BITOR
, pn
, BitXorExpr(cx
, ts
, tc
),
5916 static JSParseNode
*
5917 BitXorExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5921 pn
= BitAndExpr(cx
, ts
, tc
);
5922 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITXOR
)) {
5923 pn
= NewBinary(TOK_BITXOR
, JSOP_BITXOR
, pn
, BitAndExpr(cx
, ts
, tc
),
5929 static JSParseNode
*
5930 BitAndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5934 pn
= EqExpr(cx
, ts
, tc
);
5935 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITAND
))
5936 pn
= NewBinary(TOK_BITAND
, JSOP_BITAND
, pn
, EqExpr(cx
, ts
, tc
), tc
);
5940 static JSParseNode
*
5941 EqExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5946 pn
= RelExpr(cx
, ts
, tc
);
5947 while (pn
&& js_MatchToken(cx
, ts
, TOK_EQOP
)) {
5948 op
= CURRENT_TOKEN(ts
).t_op
;
5949 pn
= NewBinary(TOK_EQOP
, op
, pn
, RelExpr(cx
, ts
, tc
), tc
);
5954 static JSParseNode
*
5955 RelExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5960 uintN inForInitFlag
= tc
->flags
& TCF_IN_FOR_INIT
;
5963 * Uses of the in operator in ShiftExprs are always unambiguous,
5964 * so unset the flag that prohibits recognizing it.
5966 tc
->flags
&= ~TCF_IN_FOR_INIT
;
5968 pn
= ShiftExpr(cx
, ts
, tc
);
5970 (js_MatchToken(cx
, ts
, TOK_RELOP
) ||
5972 * Recognize the 'in' token as an operator only if we're not
5973 * currently in the init expr of a for loop.
5975 (inForInitFlag
== 0 && js_MatchToken(cx
, ts
, TOK_IN
)) ||
5976 js_MatchToken(cx
, ts
, TOK_INSTANCEOF
))) {
5977 tt
= CURRENT_TOKEN(ts
).type
;
5978 op
= CURRENT_TOKEN(ts
).t_op
;
5979 pn
= NewBinary(tt
, op
, pn
, ShiftExpr(cx
, ts
, tc
), tc
);
5981 /* Restore previous state of inForInit flag. */
5982 tc
->flags
|= inForInitFlag
;
5987 static JSParseNode
*
5988 ShiftExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5993 pn
= AddExpr(cx
, ts
, tc
);
5994 while (pn
&& js_MatchToken(cx
, ts
, TOK_SHOP
)) {
5995 op
= CURRENT_TOKEN(ts
).t_op
;
5996 pn
= NewBinary(TOK_SHOP
, op
, pn
, AddExpr(cx
, ts
, tc
), tc
);
6001 static JSParseNode
*
6002 AddExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6008 pn
= MulExpr(cx
, ts
, tc
);
6010 (js_MatchToken(cx
, ts
, TOK_PLUS
) ||
6011 js_MatchToken(cx
, ts
, TOK_MINUS
))) {
6012 tt
= CURRENT_TOKEN(ts
).type
;
6013 op
= (tt
== TOK_PLUS
) ? JSOP_ADD
: JSOP_SUB
;
6014 pn
= NewBinary(tt
, op
, pn
, MulExpr(cx
, ts
, tc
), tc
);
6019 static JSParseNode
*
6020 MulExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6026 pn
= UnaryExpr(cx
, ts
, tc
);
6028 (js_MatchToken(cx
, ts
, TOK_STAR
) ||
6029 js_MatchToken(cx
, ts
, TOK_DIVOP
))) {
6030 tt
= CURRENT_TOKEN(ts
).type
;
6031 op
= CURRENT_TOKEN(ts
).t_op
;
6032 pn
= NewBinary(tt
, op
, pn
, UnaryExpr(cx
, ts
, tc
), tc
);
6037 static JSParseNode
*
6038 SetLvalKid(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
, JSParseNode
*kid
,
6041 while (kid
->pn_type
== TOK_RP
)
6043 if (kid
->pn_type
!= TOK_NAME
&&
6044 kid
->pn_type
!= TOK_DOT
&&
6045 #if JS_HAS_LVALUE_RETURN
6046 (kid
->pn_type
!= TOK_LP
||
6047 (kid
->pn_op
!= JSOP_CALL
&& kid
->pn_op
!= JSOP_EVAL
&& kid
->pn_op
!= JSOP_APPLY
)) &&
6049 #if JS_HAS_XML_SUPPORT
6050 (kid
->pn_type
!= TOK_UNARYOP
|| kid
->pn_op
!= JSOP_XMLNAME
) &&
6052 kid
->pn_type
!= TOK_LB
) {
6053 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6054 JSMSG_BAD_OPERAND
, name
);
6061 static const char incop_name_str
[][10] = {"increment", "decrement"};
6064 SetIncOpKid(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6065 JSParseNode
*pn
, JSParseNode
*kid
,
6066 JSTokenType tt
, JSBool preorder
)
6070 kid
= SetLvalKid(cx
, ts
, pn
, kid
, incop_name_str
[tt
== TOK_DEC
]);
6073 switch (kid
->pn_type
) {
6075 op
= (tt
== TOK_INC
)
6076 ? (preorder
? JSOP_INCNAME
: JSOP_NAMEINC
)
6077 : (preorder
? JSOP_DECNAME
: JSOP_NAMEDEC
);
6078 NoteLValue(cx
, kid
, tc
);
6082 op
= (tt
== TOK_INC
)
6083 ? (preorder
? JSOP_INCPROP
: JSOP_PROPINC
)
6084 : (preorder
? JSOP_DECPROP
: JSOP_PROPDEC
);
6087 #if JS_HAS_LVALUE_RETURN
6089 if (!MakeSetCall(cx
, kid
, tc
, JSMSG_BAD_INCOP_OPERAND
))
6093 #if JS_HAS_XML_SUPPORT
6095 if (kid
->pn_op
== JSOP_XMLNAME
)
6096 kid
->pn_op
= JSOP_SETXMLNAME
;
6100 op
= (tt
== TOK_INC
)
6101 ? (preorder
? JSOP_INCELEM
: JSOP_ELEMINC
)
6102 : (preorder
? JSOP_DECELEM
: JSOP_ELEMDEC
);
6113 static JSParseNode
*
6114 UnaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6117 JSParseNode
*pn
, *pn2
;
6119 JS_CHECK_RECURSION(cx
, return NULL
);
6121 ts
->flags
|= TSF_OPERAND
;
6122 tt
= js_GetToken(cx
, ts
);
6123 ts
->flags
&= ~TSF_OPERAND
;
6129 pn
= NewParseNode(PN_UNARY
, tc
);
6132 pn
->pn_type
= TOK_UNARYOP
; /* PLUS and MINUS are binary */
6133 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
6134 pn2
= UnaryExpr(cx
, ts
, tc
);
6137 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6143 pn
= NewParseNode(PN_UNARY
, tc
);
6146 pn2
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
6149 if (!SetIncOpKid(cx
, ts
, tc
, pn
, pn2
, tt
, JS_TRUE
))
6151 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6155 pn
= NewParseNode(PN_UNARY
, tc
);
6158 pn2
= UnaryExpr(cx
, ts
, tc
);
6161 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6164 * Under ECMA3, deleting any unary expression is valid -- it simply
6165 * returns true. Here we strip off any parentheses and fold constants
6166 * before checking for a call expression, in order to rule out delete
6167 * of a generator expression.
6169 while (pn2
->pn_type
== TOK_RP
)
6171 if (!js_FoldConstants(cx
, pn2
, tc
))
6173 switch (pn2
->pn_type
) {
6175 if (pn2
->pn_op
!= JSOP_SETCALL
&&
6176 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_DELETE_OPERAND
)) {
6181 pn2
->pn_op
= JSOP_DELNAME
;
6193 pn
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
6197 /* Don't look across a newline boundary for a postfix incop. */
6198 if (ON_CURRENT_LINE(ts
, pn
->pn_pos
)) {
6199 ts
->flags
|= TSF_OPERAND
;
6200 tt
= js_PeekTokenSameLine(cx
, ts
);
6201 ts
->flags
&= ~TSF_OPERAND
;
6202 if (tt
== TOK_INC
|| tt
== TOK_DEC
) {
6203 (void) js_GetToken(cx
, ts
);
6204 pn2
= NewParseNode(PN_UNARY
, tc
);
6207 if (!SetIncOpKid(cx
, ts
, tc
, pn2
, pn
, tt
, JS_FALSE
))
6209 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6218 #if JS_HAS_GENERATORS
6221 * A dedicated helper for transplanting the comprehension expression E in
6223 * [E for (V in I)] // array comprehension
6224 * (E for (V in I)) // generator expression
6226 * from its initial location in the AST, on the left of the 'for', to its final
6227 * position on the right. To avoid a separate pass we do this by adjusting the
6228 * blockids and name binding links that were established when E was parsed.
6230 * A generator expression desugars like so:
6232 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6234 * so the transplanter must adjust static level as well as blockid. E's source
6235 * coordinates in root->pn_pos are critical to deciding which binding links to
6236 * preserve and which to cut.
6238 * NB: This is not a general tree transplanter -- it knows in particular that
6239 * the one or more bindings induced by V have not yet been created.
6241 class CompExprTransplanter
{
6249 CompExprTransplanter(JSParseNode
*pn
, JSTreeContext
*tc
, bool ge
, uintN adj
)
6250 : root(pn
), tc(tc
), genexp(ge
), adjust(adj
), funcLevel(0)
6254 bool transplant(JSParseNode
*pn
);
6258 * Any definitions nested within the comprehension expression of a generator
6259 * expression must move "down" one static level, which of course increases the
6260 * upvar-frame-skip count.
6263 BumpStaticLevel(JSParseNode
*pn
, JSTreeContext
*tc
)
6265 if (pn
->pn_cookie
!= FREE_UPVAR_COOKIE
) {
6266 uintN level
= UPVAR_FRAME_SKIP(pn
->pn_cookie
) + 1;
6268 JS_ASSERT(level
>= tc
->staticLevel
);
6269 if (level
>= FREE_STATIC_LEVEL
) {
6270 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
6271 JSMSG_TOO_DEEP
, js_function_str
);
6275 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(level
, UPVAR_FRAME_SLOT(pn
->pn_cookie
));
6281 AdjustBlockId(JSParseNode
*pn
, uintN adjust
, JSTreeContext
*tc
)
6283 JS_ASSERT(pn
->pn_arity
== PN_LIST
|| pn
->pn_arity
== PN_FUNC
|| pn
->pn_arity
== PN_NAME
);
6284 pn
->pn_blockid
+= adjust
;
6285 if (pn
->pn_blockid
>= tc
->blockidGen
)
6286 tc
->blockidGen
= pn
->pn_blockid
+ 1;
6290 CompExprTransplanter::transplant(JSParseNode
*pn
)
6295 switch (pn
->pn_arity
) {
6297 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
6299 if (pn
->pn_pos
>= root
->pn_pos
)
6300 AdjustBlockId(pn
, adjust
, tc
);
6304 transplant(pn
->pn_kid1
);
6305 transplant(pn
->pn_kid2
);
6306 transplant(pn
->pn_kid3
);
6310 transplant(pn
->pn_left
);
6312 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6313 if (pn
->pn_right
!= pn
->pn_left
)
6314 transplant(pn
->pn_right
);
6318 transplant(pn
->pn_kid
);
6324 * Only the first level of transplant recursion through functions needs
6325 * to reparent the funbox, since all descendant functions are correctly
6326 * linked under the top-most funbox. But every visit to this case needs
6327 * to update funbox->level.
6329 * Recall that funbox->level is the static level of the code containing
6330 * the definition or expression of the function and not the static level
6331 * of the function's body.
6333 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6335 funbox
->level
= tc
->staticLevel
+ funcLevel
;
6336 if (++funcLevel
== 1 && genexp
) {
6337 JSFunctionBox
*parent
= tc
->funbox
;
6339 JSFunctionBox
**funboxp
= &tc
->parent
->functionList
;
6340 while (*funboxp
!= funbox
)
6341 funboxp
= &(*funboxp
)->siblings
;
6342 *funboxp
= funbox
->siblings
;
6344 funbox
->parent
= parent
;
6345 funbox
->siblings
= parent
->kids
;
6346 parent
->kids
= funbox
;
6347 funbox
->level
= tc
->staticLevel
;
6353 transplant(pn
->maybeExpr());
6354 if (pn
->pn_arity
== PN_FUNC
)
6358 if (genexp
&& !BumpStaticLevel(pn
, tc
))
6360 } else if (pn
->pn_used
) {
6361 JS_ASSERT(pn
->pn_op
!= JSOP_NOP
);
6362 JS_ASSERT(pn
->pn_cookie
== FREE_UPVAR_COOKIE
);
6364 JSDefinition
*dn
= pn
->pn_lexdef
;
6365 JS_ASSERT(dn
->pn_defn
);
6368 * Adjust the definition's block id only if it is a placeholder not
6369 * to the left of the root node, and if pn is the last use visited
6370 * in the comprehension expression (to avoid adjusting the blockid
6373 * Non-placeholder definitions within the comprehension expression
6374 * will be visited further below.
6376 if (dn
->isPlaceholder() && dn
->pn_pos
>= root
->pn_pos
&& dn
->dn_uses
== pn
) {
6377 if (genexp
&& !BumpStaticLevel(dn
, tc
))
6379 AdjustBlockId(dn
, adjust
, tc
);
6382 JSAtom
*atom
= pn
->pn_atom
;
6384 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
6385 JS_ASSERT(!stmt
|| stmt
!= tc
->topStmt
);
6387 if (genexp
&& PN_OP(dn
) != JSOP_CALLEE
) {
6388 JS_ASSERT(!tc
->decls
.lookup(atom
));
6390 if (dn
->pn_pos
< root
->pn_pos
|| dn
->isPlaceholder()) {
6391 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->compiler
, dn
->pn_atom
);
6395 if (dn
->pn_pos
>= root
->pn_pos
) {
6396 tc
->parent
->lexdeps
.remove(tc
->compiler
, atom
);
6398 JSDefinition
*dn2
= (JSDefinition
*)
6399 NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), dn
->pn_atom
, tc
);
6403 dn2
->pn_type
= dn
->pn_type
;
6404 dn2
->pn_pos
= root
->pn_pos
;
6405 dn2
->pn_defn
= true;
6406 dn2
->pn_dflags
|= PND_PLACEHOLDER
;
6408 JSParseNode
**pnup
= &dn
->dn_uses
;
6410 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_pos
>= root
->pn_pos
) {
6411 pnu
->pn_lexdef
= dn2
;
6412 dn2
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
6413 pnup
= &pnu
->pn_link
;
6415 dn2
->dn_uses
= dn
->dn_uses
;
6416 dn
->dn_uses
= *pnup
;
6422 ALE_SET_DEFN(ale
, dn
);
6427 if (pn
->pn_pos
>= root
->pn_pos
)
6428 AdjustBlockId(pn
, adjust
, tc
);
6432 transplant(pn
->pn_tree
);
6439 * Starting from a |for| keyword after the first array initialiser element or
6440 * an expression in an open parenthesis, parse the tail of the comprehension
6441 * or generator expression signified by this |for| keyword in context.
6443 * Return null on failure, else return the top-most parse node for the array
6444 * comprehension or generator expression, with a unary node as the body of the
6445 * (possibly nested) for-loop, initialized by |type, op, kid|.
6447 static JSParseNode
*
6448 ComprehensionTail(JSParseNode
*kid
, uintN blockid
, JSTreeContext
*tc
,
6449 JSTokenType type
= TOK_SEMI
, JSOp op
= JSOP_NOP
)
6451 JSContext
*cx
= tc
->compiler
->context
;
6452 JSTokenStream
*ts
= TS(tc
->compiler
);
6455 JSParseNode
*pn
, *pn2
, *pn3
, **pnp
;
6456 JSStmtInfo stmtInfo
;
6461 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_FOR
);
6463 if (type
== TOK_SEMI
) {
6465 * Generator expression desugars to an immediately applied lambda that
6466 * yields the next value from a for-in loop (possibly nested, and with
6467 * optional if guard). Make pn be the TOK_LC body node.
6469 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
6472 adjust
= pn
->pn_blockid
- blockid
;
6474 JS_ASSERT(type
== TOK_ARRAYPUSH
);
6477 * Make a parse-node and literal object representing the block scope of
6478 * this array comprehension. Our caller in PrimaryExpr, the TOK_LB case
6479 * aka the array initialiser case, has passed the blockid to claim for
6480 * the comprehension's block scope. We allocate that id or one above it
6481 * here, by calling js_PushLexicalScope.
6483 * In the case of a comprehension expression that has nested blocks
6484 * (e.g., let expressions), we will allocate a higher blockid but then
6485 * slide all blocks "to the right" to make room for the comprehension's
6488 adjust
= tc
->blockid();
6489 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
6493 JS_ASSERT(blockid
<= pn
->pn_blockid
);
6494 JS_ASSERT(blockid
< tc
->blockidGen
);
6495 JS_ASSERT(tc
->bodyid
< blockid
);
6496 pn
->pn_blockid
= stmtInfo
.blockid
= blockid
;
6497 JS_ASSERT(adjust
< blockid
);
6498 adjust
= blockid
- adjust
;
6503 CompExprTransplanter
transplanter(kid
, tc
, type
== TOK_SEMI
, adjust
);
6504 transplanter
.transplant(kid
);
6508 data
.binder
= BindLet
;
6509 data
.let
.overflow
= JSMSG_ARRAY_INIT_TOO_BIG
;
6513 * FOR node is binary, left is loop control and right is body. Use
6514 * index to count each block-local let-variable on the left-hand side
6517 pn2
= NewParseNode(PN_BINARY
, tc
);
6521 pn2
->pn_op
= JSOP_ITER
;
6522 pn2
->pn_iflags
= JSITER_ENUMERATE
;
6523 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
6524 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
6525 pn2
->pn_iflags
|= JSITER_FOREACH
;
6529 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
6532 tt
= js_GetToken(cx
, ts
);
6534 #if JS_HAS_DESTRUCTURING
6537 ts
->flags
|= TSF_DESTRUCTURING
;
6538 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
6539 ts
->flags
&= ~TSF_DESTRUCTURING
;
6546 atom
= CURRENT_TOKEN(ts
).t_atom
;
6549 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6550 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6551 * in the operand stack frame. The code generator computes that,
6552 * and it tries to bind all names to slots, so we must let it do
6555 pn3
= NewBindingNode(ts
, atom
, tc
, true);
6561 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6562 JSMSG_NO_VARIABLE_NAME
);
6568 MUST_MATCH_TOKEN(TOK_IN
, JSMSG_IN_AFTER_FOR_NAME
);
6569 JSParseNode
*pn4
= Expr(cx
, ts
, tc
);
6572 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
6575 #if JS_HAS_DESTRUCTURING
6578 if (!CheckDestructuring(cx
, &data
, pn3
, NULL
, tc
))
6581 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
6582 /* Destructuring requires [key, value] enumeration in JS1.7. */
6583 if (pn3
->pn_type
!= TOK_RB
|| pn3
->pn_count
!= 2) {
6584 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6585 JSMSG_BAD_FOR_LEFTSIDE
);
6589 JS_ASSERT(pn2
->pn_op
== JSOP_ITER
);
6590 JS_ASSERT(pn2
->pn_iflags
& JSITER_ENUMERATE
);
6591 if (!(pn2
->pn_iflags
& JSITER_FOREACH
))
6592 pn2
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
6599 if (!data
.binder(cx
, &data
, atom
, tc
))
6606 pn2
->pn_left
= NewBinary(TOK_IN
, JSOP_NOP
, pn3
, pn4
, tc
);
6610 pnp
= &pn2
->pn_right
;
6611 } while (js_MatchToken(cx
, ts
, TOK_FOR
));
6613 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
6614 pn2
= NewParseNode(PN_TERNARY
, tc
);
6617 pn2
->pn_kid1
= Condition(cx
, ts
, tc
);
6621 pnp
= &pn2
->pn_kid2
;
6624 pn2
= NewParseNode(PN_UNARY
, tc
);
6627 pn2
->pn_type
= type
;
6632 if (type
== TOK_ARRAYPUSH
)
6637 #if JS_HAS_GENERATOR_EXPRS
6640 * Starting from a |for| keyword after an expression, parse the comprehension
6641 * tail completing this generator expression. Wrap the expression at kid in a
6642 * generator function that is immediately called to evaluate to the generator
6643 * iterator that is the value of this generator expression.
6645 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
6646 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6647 * expression-statement node that constitutes the body of the |for| loop(s) in
6648 * the generator function.
6650 * Note how unlike Python, we do not evaluate the expression to the right of
6651 * the first |in| in the chain of |for| heads. Instead, a generator expression
6652 * is merely sugar for a generator function expression and its application.
6654 static JSParseNode
*
6655 GeneratorExpr(JSParseNode
*pn
, JSParseNode
*kid
, JSTreeContext
*tc
)
6657 /* Initialize pn, connecting it to kid. */
6658 JS_ASSERT(pn
->pn_arity
== PN_UNARY
);
6659 pn
->pn_type
= TOK_YIELD
;
6660 pn
->pn_op
= JSOP_YIELD
;
6661 pn
->pn_pos
= kid
->pn_pos
;
6663 pn
->pn_hidden
= JS_TRUE
;
6665 /* Make a new node for the desugared generator function. */
6666 JSParseNode
*genfn
= NewParseNode(PN_FUNC
, tc
);
6669 genfn
->pn_type
= TOK_FUNCTION
;
6670 genfn
->pn_op
= JSOP_LAMBDA
;
6671 JS_ASSERT(!genfn
->pn_body
);
6672 genfn
->pn_dflags
= PND_FUNARG
;
6675 JSTreeContext
gentc(tc
->compiler
);
6677 JSFunctionBox
*funbox
= EnterFunction(genfn
, tc
, &gentc
);
6682 * We assume conservatively that any deoptimization flag in tc->flags
6683 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6684 * propagate these flags into genfn. For code simplicity we also do
6685 * not detect if the flags were only set in the kid and could be
6686 * removed from tc->flags.
6688 gentc
.flags
|= TCF_FUN_IS_GENERATOR
| TCF_GENEXP_LAMBDA
|
6689 (tc
->flags
& (TCF_FUN_FLAGS
& ~TCF_FUN_PARAM_ARGUMENTS
));
6690 funbox
->tcflags
|= gentc
.flags
;
6691 genfn
->pn_funbox
= funbox
;
6692 genfn
->pn_blockid
= gentc
.bodyid
;
6694 JSParseNode
*body
= ComprehensionTail(pn
, tc
->blockid(), &gentc
);
6697 JS_ASSERT(!genfn
->pn_body
);
6698 genfn
->pn_body
= body
;
6699 genfn
->pn_pos
.begin
= body
->pn_pos
.begin
= kid
->pn_pos
.begin
;
6700 genfn
->pn_pos
.end
= body
->pn_pos
.end
= CURRENT_TOKEN(TS(tc
->compiler
)).pos
.end
;
6702 if (!LeaveFunction(genfn
, &gentc
, tc
))
6707 * Our result is a call expression that invokes the anonymous generator
6710 JSParseNode
*result
= NewParseNode(PN_LIST
, tc
);
6713 result
->pn_type
= TOK_LP
;
6714 result
->pn_op
= JSOP_CALL
;
6715 result
->pn_pos
.begin
= genfn
->pn_pos
.begin
;
6716 result
->initList(genfn
);
6720 static const char js_generator_str
[] = "generator";
6722 #endif /* JS_HAS_GENERATOR_EXPRS */
6723 #endif /* JS_HAS_GENERATORS */
6726 ArgumentList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6727 JSParseNode
*listNode
)
6731 ts
->flags
|= TSF_OPERAND
;
6732 matched
= js_MatchToken(cx
, ts
, TOK_RP
);
6733 ts
->flags
&= ~TSF_OPERAND
;
6736 JSParseNode
*argNode
= AssignExpr(cx
, ts
, tc
);
6739 #if JS_HAS_GENERATORS
6740 if (argNode
->pn_type
== TOK_YIELD
&&
6741 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
6742 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
6743 JSMSG_BAD_GENERATOR_SYNTAX
,
6748 #if JS_HAS_GENERATOR_EXPRS
6749 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
6750 JSParseNode
*pn
= NewParseNode(PN_UNARY
, tc
);
6753 argNode
= GeneratorExpr(pn
, argNode
, tc
);
6756 if (listNode
->pn_count
> 1 ||
6757 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
6758 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
6759 JSMSG_BAD_GENERATOR_SYNTAX
,
6765 listNode
->append(argNode
);
6766 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
6768 if (js_GetToken(cx
, ts
) != TOK_RP
) {
6769 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6770 JSMSG_PAREN_AFTER_ARGS
);
6777 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6778 static JSParseNode
*
6779 CheckForImmediatelyAppliedLambda(JSParseNode
*pn
)
6781 while (pn
->pn_type
== TOK_RP
)
6783 if (pn
->pn_type
== TOK_FUNCTION
) {
6784 JS_ASSERT(pn
->pn_arity
== PN_FUNC
);
6786 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6787 JS_ASSERT(((JSFunction
*) funbox
->object
)->flags
& JSFUN_LAMBDA
);
6788 if (!(funbox
->tcflags
& TCF_FUN_USES_ARGUMENTS
))
6789 pn
->pn_dflags
&= ~PND_FUNARG
;
6794 static JSParseNode
*
6795 MemberExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6796 JSBool allowCallSyntax
)
6798 JSParseNode
*pn
, *pn2
, *pn3
;
6801 JS_CHECK_RECURSION(cx
, return NULL
);
6803 /* Check for new expression first. */
6804 ts
->flags
|= TSF_OPERAND
;
6805 tt
= js_GetToken(cx
, ts
);
6806 ts
->flags
&= ~TSF_OPERAND
;
6807 if (tt
== TOK_NEW
) {
6808 pn
= NewParseNode(PN_LIST
, tc
);
6811 pn2
= MemberExpr(cx
, ts
, tc
, JS_FALSE
);
6814 pn2
= CheckForImmediatelyAppliedLambda(pn2
);
6815 pn
->pn_op
= JSOP_NEW
;
6817 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
6819 if (js_MatchToken(cx
, ts
, TOK_LP
) && !ArgumentList(cx
, ts
, tc
, pn
))
6821 if (pn
->pn_count
> ARGC_LIMIT
) {
6822 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6823 JSMSG_TOO_MANY_CON_ARGS
);
6826 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
6828 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
6832 if (pn
->pn_type
== TOK_ANYNAME
||
6833 pn
->pn_type
== TOK_AT
||
6834 pn
->pn_type
== TOK_DBLCOLON
) {
6835 pn2
= NewOrRecycledNode(tc
);
6838 pn2
->pn_type
= TOK_UNARYOP
;
6839 pn2
->pn_pos
= pn
->pn_pos
;
6840 pn2
->pn_op
= JSOP_XMLNAME
;
6841 pn2
->pn_arity
= PN_UNARY
;
6847 while ((tt
= js_GetToken(cx
, ts
)) > TOK_EOF
) {
6848 if (tt
== TOK_DOT
) {
6849 pn2
= NewNameNode(cx
, ts
, NULL
, tc
);
6852 #if JS_HAS_XML_SUPPORT
6853 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
6854 tt
= js_GetToken(cx
, ts
);
6855 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
6856 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
6860 if (tt
== TOK_NAME
) {
6861 pn2
->pn_op
= JSOP_GETPROP
;
6863 pn2
->pn_atom
= pn3
->pn_atom
;
6864 RecycleTree(pn3
, tc
);
6866 if (TOKEN_TYPE_IS_XML(tt
)) {
6867 pn2
->pn_type
= TOK_LB
;
6868 pn2
->pn_op
= JSOP_GETELEM
;
6869 } else if (tt
== TOK_RP
) {
6870 JSParseNode
*group
= pn3
;
6872 /* Recycle the useless TOK_RP node. */
6873 pn3
= group
->pn_kid
;
6874 group
->pn_kid
= NULL
;
6875 RecycleTree(group
, tc
);
6876 pn2
->pn_type
= TOK_FILTER
;
6877 pn2
->pn_op
= JSOP_FILTER
;
6879 /* A filtering predicate is like a with statement. */
6880 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
6882 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6883 JSMSG_NAME_AFTER_DOT
);
6886 pn2
->pn_arity
= PN_BINARY
;
6888 pn2
->pn_right
= pn3
;
6891 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
6892 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NAME_AFTER_DOT
);
6893 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
6894 pn2
->pn_op
= JSOP_GETPROP
;
6896 pn2
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
6898 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6899 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6900 #if JS_HAS_XML_SUPPORT
6901 } else if (tt
== TOK_DBLDOT
) {
6902 pn2
= NewParseNode(PN_BINARY
, tc
);
6905 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
6906 tt
= js_GetToken(cx
, ts
);
6907 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
6908 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
6912 if (tt
== TOK_NAME
) {
6913 pn3
->pn_type
= TOK_STRING
;
6914 pn3
->pn_arity
= PN_NULLARY
;
6915 pn3
->pn_op
= JSOP_QNAMEPART
;
6916 } else if (!TOKEN_TYPE_IS_XML(tt
)) {
6917 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6918 JSMSG_NAME_AFTER_DOT
);
6921 pn2
->pn_op
= JSOP_DESCENDANTS
;
6923 pn2
->pn_right
= pn3
;
6924 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6925 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6927 } else if (tt
== TOK_LB
) {
6928 pn2
= NewParseNode(PN_BINARY
, tc
);
6931 pn3
= Expr(cx
, ts
, tc
);
6935 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
6936 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6937 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6940 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
6941 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
6942 * the interpreter from fast property access. However, if the
6943 * bracketed string is a uint32, we rewrite pn3 to be a number
6944 * instead of a string.
6947 if (pn3
->pn_type
== TOK_STRING
) {
6950 if (!js_IdIsIndex(ATOM_TO_JSID(pn3
->pn_atom
), &index
)) {
6951 pn2
->pn_type
= TOK_DOT
;
6952 pn2
->pn_op
= JSOP_GETPROP
;
6953 pn2
->pn_arity
= PN_NAME
;
6955 pn2
->pn_atom
= pn3
->pn_atom
;
6958 pn3
->pn_type
= TOK_NUMBER
;
6959 pn3
->pn_op
= JSOP_DOUBLE
;
6960 pn3
->pn_dval
= index
;
6962 pn2
->pn_op
= JSOP_GETELEM
;
6964 pn2
->pn_right
= pn3
;
6966 } else if (allowCallSyntax
&& tt
== TOK_LP
) {
6967 pn2
= NewParseNode(PN_LIST
, tc
);
6970 pn2
->pn_op
= JSOP_CALL
;
6972 /* CheckForImmediatelyAppliedLambda skips useless TOK_RP nodes. */
6973 pn
= CheckForImmediatelyAppliedLambda(pn
);
6974 if (pn
->pn_op
== JSOP_NAME
) {
6975 if (pn
->pn_atom
== cx
->runtime
->atomState
.evalAtom
) {
6976 /* Select JSOP_EVAL and flag tc as heavyweight. */
6977 pn2
->pn_op
= JSOP_EVAL
;
6978 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
6980 } else if (pn
->pn_op
== JSOP_GETPROP
) {
6981 if (pn
->pn_atom
== cx
->runtime
->atomState
.applyAtom
||
6982 pn
->pn_atom
== cx
->runtime
->atomState
.callAtom
) {
6983 /* Select JSOP_APPLY given foo.apply(...). */
6984 pn2
->pn_op
= JSOP_APPLY
;
6989 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6991 if (!ArgumentList(cx
, ts
, tc
, pn2
))
6993 if (pn2
->pn_count
> ARGC_LIMIT
) {
6994 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6995 JSMSG_TOO_MANY_FUN_ARGS
);
6998 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7006 if (tt
== TOK_ERROR
)
7011 static JSParseNode
*
7012 BracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7018 * Always accept the 'in' operator in a parenthesized expression,
7019 * where it's unambiguous, even if we might be parsing the init of a
7022 oldflags
= tc
->flags
;
7023 tc
->flags
&= ~TCF_IN_FOR_INIT
;
7024 pn
= Expr(cx
, ts
, tc
);
7025 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
7029 #if JS_HAS_XML_SUPPORT
7031 static JSParseNode
*
7032 EndBracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7036 pn
= BracketedExpr(cx
, ts
, tc
);
7040 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_ATTR_EXPR
);
7045 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7047 * AttributeIdentifier:
7048 * @ PropertySelector
7049 * @ QualifiedIdentifier
7056 * QualifiedIdentifier:
7057 * PropertySelector :: PropertySelector
7058 * PropertySelector :: [ Expression ]
7060 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7062 * AttributeIdentifier:
7063 * @ QualifiedIdentifier
7070 * QualifiedIdentifier:
7071 * PropertySelector :: PropertySelector
7072 * PropertySelector :: [ Expression ]
7075 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7076 * for that rule to result in a name node, but ECMA-357 extends the grammar
7077 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7079 * QualifiedIdentifier:
7080 * PropertySelector QualifiedSuffix
7083 * :: PropertySelector
7087 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7089 * PrimaryExpression:
7090 * Identifier QualifiedSuffix
7092 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7093 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7095 static JSParseNode
*
7096 PropertySelector(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7100 pn
= NewParseNode(PN_NULLARY
, tc
);
7103 if (pn
->pn_type
== TOK_STAR
) {
7104 pn
->pn_type
= TOK_ANYNAME
;
7105 pn
->pn_op
= JSOP_ANYNAME
;
7106 pn
->pn_atom
= cx
->runtime
->atomState
.starAtom
;
7108 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
7109 pn
->pn_op
= JSOP_QNAMEPART
;
7110 pn
->pn_arity
= PN_NAME
;
7111 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
7112 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
7117 static JSParseNode
*
7118 QualifiedSuffix(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
7121 JSParseNode
*pn2
, *pn3
;
7124 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_DBLCOLON
);
7125 pn2
= NewNameNode(cx
, ts
, NULL
, tc
);
7129 /* Left operand of :: must be evaluated if it is an identifier. */
7130 if (pn
->pn_op
== JSOP_QNAMEPART
)
7131 pn
->pn_op
= JSOP_NAME
;
7133 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7134 tt
= js_GetToken(cx
, ts
);
7135 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7136 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
7137 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
7138 pn2
->pn_op
= JSOP_QNAMECONST
;
7139 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7140 pn2
->pn_atom
= (tt
== TOK_STAR
)
7141 ? cx
->runtime
->atomState
.starAtom
7142 : CURRENT_TOKEN(ts
).t_atom
;
7144 pn2
->pn_cookie
= FREE_UPVAR_COOKIE
;
7149 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7150 JSMSG_SYNTAX_ERROR
);
7153 pn3
= EndBracketedExpr(cx
, ts
, tc
);
7157 pn2
->pn_op
= JSOP_QNAME
;
7158 pn2
->pn_arity
= PN_BINARY
;
7159 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7160 pn2
->pn_pos
.end
= pn3
->pn_pos
.end
;
7162 pn2
->pn_right
= pn3
;
7166 static JSParseNode
*
7167 QualifiedIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7171 pn
= PropertySelector(cx
, ts
, tc
);
7174 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
7175 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7176 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
7177 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
7182 static JSParseNode
*
7183 AttributeIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7185 JSParseNode
*pn
, *pn2
;
7188 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_AT
);
7189 pn
= NewParseNode(PN_UNARY
, tc
);
7192 pn
->pn_op
= JSOP_TOATTRNAME
;
7193 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7194 tt
= js_GetToken(cx
, ts
);
7195 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7196 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
7197 pn2
= QualifiedIdentifier(cx
, ts
, tc
);
7198 } else if (tt
== TOK_LB
) {
7199 pn2
= EndBracketedExpr(cx
, ts
, tc
);
7201 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7202 JSMSG_SYNTAX_ERROR
);
7212 * Make a TOK_LC unary node whose pn_kid is an expression.
7214 static JSParseNode
*
7215 XMLExpr(JSContext
*cx
, JSTokenStream
*ts
, JSBool inTag
, JSTreeContext
*tc
)
7217 JSParseNode
*pn
, *pn2
;
7220 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LC
);
7221 pn
= NewParseNode(PN_UNARY
, tc
);
7226 * Turn off XML tag mode, but don't restore it after parsing this braced
7227 * expression. Instead, simply restore ts's old flags. This is required
7228 * because XMLExpr is called both from within a tag, and from within text
7229 * contained in an element, but outside of any start, end, or point tag.
7231 oldflags
= ts
->flags
;
7232 ts
->flags
= oldflags
& ~TSF_XMLTAGMODE
;
7233 pn2
= Expr(cx
, ts
, tc
);
7237 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_XML_EXPR
);
7238 ts
->flags
= oldflags
;
7240 pn
->pn_op
= inTag
? JSOP_XMLTAGEXPR
: JSOP_XMLELTEXPR
;
7245 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7246 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7247 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7248 * child of a container tag.
7250 static JSParseNode
*
7251 XMLAtomNode(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7256 pn
= NewParseNode(PN_NULLARY
, tc
);
7259 tp
= &CURRENT_TOKEN(ts
);
7260 pn
->pn_op
= tp
->t_op
;
7261 pn
->pn_atom
= tp
->t_atom
;
7262 if (tp
->type
== TOK_XMLPI
)
7263 pn
->pn_atom2
= tp
->t_atom2
;
7268 * Parse the productions:
7271 * XMLName XMLNameExpr?
7272 * { Expr } XMLNameExpr?
7274 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7275 * a list of names and/or expressions, a single expression, or a single name.
7276 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7279 static JSParseNode
*
7280 XMLNameExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7282 JSParseNode
*pn
, *pn2
, *list
;
7287 tt
= CURRENT_TOKEN(ts
).type
;
7289 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
7293 JS_ASSERT(tt
== TOK_XMLNAME
);
7294 pn2
= XMLAtomNode(cx
, ts
, tc
);
7303 list
= NewParseNode(PN_LIST
, tc
);
7306 list
->pn_type
= TOK_XMLNAME
;
7307 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7309 list
->pn_xflags
= PNX_CANTFOLD
;
7312 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7315 } while ((tt
= js_GetToken(cx
, ts
)) == TOK_XMLNAME
|| tt
== TOK_LC
);
7322 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7323 * at compile time into a JSXML tree.
7325 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7326 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7327 : (pn)->pn_type != TOK_LC)
7330 * Parse the productions:
7334 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7335 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7337 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7338 * produces a list of name and attribute values and/or braced expressions, a
7339 * single expression, or a single name.
7341 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7342 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7343 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7344 * we parsed exactly one expression.
7346 static JSParseNode
*
7347 XMLTagContent(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7348 JSTokenType tagtype
, JSAtom
**namep
)
7350 JSParseNode
*pn
, *pn2
, *list
;
7353 pn
= XMLNameExpr(cx
, ts
, tc
);
7356 *namep
= (pn
->pn_arity
== PN_NULLARY
) ? pn
->pn_atom
: NULL
;
7359 while (js_MatchToken(cx
, ts
, TOK_XMLSPACE
)) {
7360 tt
= js_GetToken(cx
, ts
);
7361 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7366 pn2
= XMLNameExpr(cx
, ts
, tc
);
7370 list
= NewParseNode(PN_LIST
, tc
);
7373 list
->pn_type
= tagtype
;
7374 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7379 if (!XML_FOLDABLE(pn2
))
7380 pn
->pn_xflags
|= PNX_CANTFOLD
;
7382 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7383 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_NO_ASSIGN_IN_XML_ATTR
);
7384 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7386 tt
= js_GetToken(cx
, ts
);
7387 if (tt
== TOK_XMLATTR
) {
7388 pn2
= XMLAtomNode(cx
, ts
, tc
);
7389 } else if (tt
== TOK_LC
) {
7390 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
7391 pn
->pn_xflags
|= PNX_CANTFOLD
;
7393 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7394 JSMSG_BAD_XML_ATTR_VALUE
);
7399 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7406 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7408 if ((tt) <= TOK_EOF) { \
7409 if ((tt) == TOK_EOF) { \
7410 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
7411 JSMSG_END_OF_XML_SOURCE); \
7417 static JSParseNode
*
7418 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7422 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7423 * that opens the end tag for the container.
7426 XMLElementContent(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
7433 ts
->flags
&= ~TSF_XMLTAGMODE
;
7435 ts
->flags
|= TSF_XMLTEXTMODE
;
7436 tt
= js_GetToken(cx
, ts
);
7437 ts
->flags
&= ~TSF_XMLTEXTMODE
;
7438 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7440 JS_ASSERT(tt
== TOK_XMLSPACE
|| tt
== TOK_XMLTEXT
);
7441 textAtom
= CURRENT_TOKEN(ts
).t_atom
;
7443 /* Non-zero-length XML text scanned. */
7444 pn2
= XMLAtomNode(cx
, ts
, tc
);
7447 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7451 ts
->flags
|= TSF_OPERAND
;
7452 tt
= js_GetToken(cx
, ts
);
7453 ts
->flags
&= ~TSF_OPERAND
;
7454 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7455 if (tt
== TOK_XMLETAGO
)
7459 pn2
= XMLExpr(cx
, ts
, JS_FALSE
, tc
);
7460 pn
->pn_xflags
|= PNX_CANTFOLD
;
7461 } else if (tt
== TOK_XMLSTAGO
) {
7462 pn2
= XMLElementOrList(cx
, ts
, tc
, JS_FALSE
);
7464 pn2
->pn_xflags
&= ~PNX_XMLROOT
;
7465 pn
->pn_xflags
|= pn2
->pn_xflags
;
7468 JS_ASSERT(tt
== TOK_XMLCDATA
|| tt
== TOK_XMLCOMMENT
||
7470 pn2
= XMLAtomNode(cx
, ts
, tc
);
7474 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7478 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLETAGO
);
7479 ts
->flags
|= TSF_XMLTAGMODE
;
7484 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7486 static JSParseNode
*
7487 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7490 JSParseNode
*pn
, *pn2
, *list
;
7492 JSAtom
*startAtom
, *endAtom
;
7494 JS_CHECK_RECURSION(cx
, return NULL
);
7496 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLSTAGO
);
7497 pn
= NewParseNode(PN_LIST
, tc
);
7501 ts
->flags
|= TSF_XMLTAGMODE
;
7502 tt
= js_GetToken(cx
, ts
);
7503 if (tt
== TOK_ERROR
)
7506 if (tt
== TOK_XMLNAME
|| tt
== TOK_LC
) {
7508 * XMLElement. Append the tag and its contents, if any, to pn.
7510 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLSTAGO
, &startAtom
);
7513 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7515 tt
= js_GetToken(cx
, ts
);
7516 if (tt
== TOK_XMLPTAGC
) {
7517 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7518 if (pn2
->pn_type
== TOK_XMLSTAGO
) {
7520 RecycleTree(pn
, tc
);
7523 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
||
7524 pn2
->pn_type
== TOK_LC
);
7526 if (!XML_FOLDABLE(pn2
))
7527 pn
->pn_xflags
|= PNX_CANTFOLD
;
7529 pn
->pn_type
= TOK_XMLPTAGC
;
7530 pn
->pn_xflags
|= PNX_XMLROOT
;
7532 /* We had better have a tag-close (>) at this point. */
7533 if (tt
!= TOK_XMLTAGC
) {
7534 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7535 JSMSG_BAD_XML_TAG_SYNTAX
);
7538 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7540 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7541 if (pn2
->pn_type
!= TOK_XMLSTAGO
) {
7543 if (!XML_FOLDABLE(pn2
))
7544 pn
->pn_xflags
|= PNX_CANTFOLD
;
7546 pn
= NewParseNode(PN_LIST
, tc
);
7551 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7552 pn
->pn_type
= TOK_XMLELEM
;
7553 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
7555 if (!XML_FOLDABLE(pn2
))
7556 pn
->pn_xflags
|= PNX_CANTFOLD
;
7557 pn
->pn_xflags
|= PNX_XMLROOT
;
7559 /* Get element contents and delimiting end-tag-open sequence. */
7560 if (!XMLElementContent(cx
, ts
, pn
, tc
))
7563 tt
= js_GetToken(cx
, ts
);
7564 XML_CHECK_FOR_ERROR_AND_EOF(tt
, NULL
);
7565 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7566 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7567 JSMSG_BAD_XML_TAG_SYNTAX
);
7571 /* Parse end tag; check mismatch at compile-time if we can. */
7572 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLETAGO
, &endAtom
);
7575 if (pn2
->pn_type
== TOK_XMLETAGO
) {
7576 /* Oops, end tag has attributes! */
7577 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7578 JSMSG_BAD_XML_TAG_SYNTAX
);
7581 if (endAtom
&& startAtom
&& endAtom
!= startAtom
) {
7582 JSString
*str
= ATOM_TO_STRING(startAtom
);
7584 /* End vs. start tag name mismatch: point to the tag name. */
7585 js_ReportCompileErrorNumber(cx
, ts
, pn2
,
7586 JSREPORT_UC
| JSREPORT_ERROR
,
7587 JSMSG_XML_TAG_NAME_MISMATCH
,
7592 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7593 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
|| pn2
->pn_type
== TOK_LC
);
7594 list
= NewParseNode(PN_LIST
, tc
);
7597 list
->pn_type
= TOK_XMLETAGO
;
7598 list
->initList(pn2
);
7600 if (!XML_FOLDABLE(pn2
)) {
7601 list
->pn_xflags
|= PNX_CANTFOLD
;
7602 pn
->pn_xflags
|= PNX_CANTFOLD
;
7605 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7606 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_TAG_SYNTAX
);
7609 /* Set pn_op now that pn has been updated to its final value. */
7610 pn
->pn_op
= JSOP_TOXML
;
7611 } else if (allowList
&& tt
== TOK_XMLTAGC
) {
7612 /* XMLList Initialiser. */
7613 pn
->pn_type
= TOK_XMLLIST
;
7614 pn
->pn_op
= JSOP_TOXMLLIST
;
7616 pn
->pn_xflags
|= PNX_XMLROOT
;
7617 if (!XMLElementContent(cx
, ts
, pn
, tc
))
7620 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_LIST_SYNTAX
);
7622 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7623 JSMSG_BAD_XML_NAME_SYNTAX
);
7627 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7628 ts
->flags
&= ~TSF_XMLTAGMODE
;
7632 static JSParseNode
*
7633 XMLElementOrListRoot(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7640 * Force XML support to be enabled so that comments and CDATA literals
7641 * are recognized, instead of <! followed by -- starting an HTML comment
7642 * to end of line (used in script tags to hide content from old browsers
7643 * that don't recognize <script>).
7645 oldopts
= JS_SetOptions(cx
, cx
->options
| JSOPTION_XML
);
7646 pn
= XMLElementOrList(cx
, ts
, tc
, allowList
);
7647 JS_SetOptions(cx
, oldopts
);
7652 JSCompiler::parseXMLText(JSObject
*chain
, bool allowList
)
7655 * Push a compiler frame if we have no frames, or if the top frame is a
7656 * lightweight function activation, or if its scope chain doesn't match
7657 * the one passed to us.
7659 JSTreeContext
tc(this);
7660 tc
.scopeChain
= chain
;
7662 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7663 TS(this)->flags
|= TSF_OPERAND
| TSF_XMLONLYMODE
;
7664 JSTokenType tt
= js_GetToken(context
, TS(this));
7665 TS(this)->flags
&= ~TSF_OPERAND
;
7668 if (tt
!= TOK_XMLSTAGO
) {
7669 js_ReportCompileErrorNumber(context
, TS(this), NULL
, JSREPORT_ERROR
,
7670 JSMSG_BAD_XML_MARKUP
);
7673 pn
= XMLElementOrListRoot(context
, TS(this), &tc
, allowList
);
7676 TS(this)->flags
&= ~TSF_XMLONLYMODE
;
7680 #endif /* JS_HAS_XMLSUPPORT */
7682 #if JS_HAS_BLOCK_SCOPE
7684 * Check whether blockid is an active scoping statement in tc. This code is
7685 * necessary to qualify tc->decls.lookup() hits in PrimaryExpr's TOK_NAME case
7686 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7687 * and let blocks and expressions (not let declarations).
7689 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7690 * due to hoisting, let in a for loop head, let block, or let expression acts
7691 * like Scheme's let: initializers are evaluated without the new let bindings
7694 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7695 * bindings push on the front of the tc->decls JSAtomList (either the singular
7696 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7697 * scope bindings of the same name.
7699 * This simplifies binding lookup code at the price of a linear search here,
7700 * but only if code uses let (var predominates), and even then this function's
7701 * loop iterates more than once only in crazy cases.
7704 BlockIdInScope(uintN blockid
, JSTreeContext
*tc
)
7706 if (blockid
> tc
->blockid())
7708 for (JSStmtInfo
*stmt
= tc
->topScopeStmt
; stmt
; stmt
= stmt
->downScope
) {
7709 if (stmt
->blockid
== blockid
)
7716 static JSParseNode
*
7717 PrimaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7718 JSTokenType tt
, JSBool afterDot
)
7720 JSParseNode
*pn
, *pn2
, *pn3
;
7723 JS_CHECK_RECURSION(cx
, return NULL
);
7725 #if JS_HAS_GETTER_SETTER
7726 if (tt
== TOK_NAME
) {
7727 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
7728 if (tt
== TOK_ERROR
)
7735 #if JS_HAS_XML_SUPPORT
7736 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7737 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
7738 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7739 pn2
= NewParseNode(PN_NULLARY
, tc
);
7742 pn2
->pn_type
= TOK_FUNCTION
;
7743 pn
= QualifiedSuffix(cx
, ts
, pn2
, tc
);
7748 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7750 pn
= FunctionExpr(cx
, ts
, tc
);
7760 pn
= NewParseNode(PN_LIST
, tc
);
7763 pn
->pn_type
= TOK_RB
;
7764 pn
->pn_op
= JSOP_NEWINIT
;
7767 #if JS_HAS_GENERATORS
7768 pn
->pn_blockid
= tc
->blockidGen
;
7771 ts
->flags
|= TSF_OPERAND
;
7772 matched
= js_MatchToken(cx
, ts
, TOK_RB
);
7773 ts
->flags
&= ~TSF_OPERAND
;
7775 for (index
= 0; ; index
++) {
7776 if (index
== ARRAY_INIT_LIMIT
) {
7777 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7778 JSMSG_ARRAY_INIT_TOO_BIG
);
7782 ts
->flags
|= TSF_OPERAND
;
7783 tt
= js_PeekToken(cx
, ts
);
7784 ts
->flags
&= ~TSF_OPERAND
;
7786 pn
->pn_xflags
|= PNX_ENDCOMMA
;
7790 if (tt
== TOK_COMMA
) {
7791 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7792 js_MatchToken(cx
, ts
, TOK_COMMA
);
7793 pn2
= NewParseNode(PN_NULLARY
, tc
);
7794 pn
->pn_xflags
|= PNX_HOLEY
;
7796 pn2
= AssignExpr(cx
, ts
, tc
);
7802 if (tt
!= TOK_COMMA
) {
7803 /* If we didn't already match TOK_COMMA in above case. */
7804 if (!js_MatchToken(cx
, ts
, TOK_COMMA
))
7809 #if JS_HAS_GENERATORS
7811 * At this point, (index == 0 && pn->pn_count != 0) implies one
7812 * element initialiser was parsed.
7814 * An array comprehension of the form:
7816 * [i * j for (i in o) for (j in p) if (i != j)]
7818 * translates to roughly the following let expression:
7820 * let (array = new Array, i, j) {
7821 * for (i in o) let {
7829 * where array is a nameless block-local variable. The "roughly"
7830 * means that an implementation may optimize away the array.push.
7831 * An array comprehension opens exactly one block scope, no matter
7832 * how many for heads it contains.
7834 * Each let () {...} or for (let ...) ... compiles to:
7836 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7838 * where <o> is a literal object representing the block scope,
7839 * with <n> properties, naming each var declared in the block.
7841 * Each var declaration in a let-block binds a name in <o> at
7842 * compile time, and allocates a slot on the operand stack at
7843 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7844 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7845 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7846 * local slot's stack index from fp->spbase.
7848 * The array comprehension iteration step, array.push(i * j) in
7849 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7850 * where <array> is the index of array's stack slot.
7853 pn
->pn_count
!= 0 &&
7854 js_MatchToken(cx
, ts
, TOK_FOR
)) {
7855 JSParseNode
*pnexp
, *pntop
;
7857 /* Relabel pn as an array comprehension node. */
7858 pn
->pn_type
= TOK_ARRAYCOMP
;
7861 * Remove the comprehension expression from pn's linked list
7862 * and save it via pnexp. We'll re-install it underneath the
7863 * ARRAYPUSH node after we parse the rest of the comprehension.
7866 JS_ASSERT(pn
->pn_count
== 1 || pn
->pn_count
== 2);
7867 pn
->pn_tail
= (--pn
->pn_count
== 1)
7868 ? &pn
->pn_head
->pn_next
7870 *pn
->pn_tail
= NULL
;
7872 pntop
= ComprehensionTail(pnexp
, pn
->pn_blockid
, tc
,
7873 TOK_ARRAYPUSH
, JSOP_ARRAYPUSH
);
7878 #endif /* JS_HAS_GENERATORS */
7880 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_LIST
);
7882 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7891 pn
= NewParseNode(PN_LIST
, tc
);
7894 pn
->pn_type
= TOK_RC
;
7895 pn
->pn_op
= JSOP_NEWINIT
;
7898 afterComma
= JS_FALSE
;
7900 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7901 tt
= js_GetToken(cx
, ts
);
7902 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7905 pn3
= NewParseNode(PN_NULLARY
, tc
);
7907 pn3
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
7910 #if JS_HAS_GETTER_SETTER
7914 atom
= CURRENT_TOKEN(ts
).t_atom
;
7915 if (atom
== cx
->runtime
->atomState
.getAtom
)
7917 else if (atom
== cx
->runtime
->atomState
.setAtom
)
7922 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7923 tt
= js_GetToken(cx
, ts
);
7924 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7925 if (tt
!= TOK_NAME
) {
7929 pn3
= NewNameNode(cx
, ts
, CURRENT_TOKEN(ts
).t_atom
, tc
);
7933 /* We have to fake a 'function' token here. */
7934 CURRENT_TOKEN(ts
).t_op
= JSOP_NOP
;
7935 CURRENT_TOKEN(ts
).type
= TOK_FUNCTION
;
7936 pn2
= FunctionExpr(cx
, ts
, tc
);
7937 pn2
= NewBinary(TOK_COLON
, op
, pn3
, pn2
, tc
);
7943 pn3
= NewParseNode(PN_NULLARY
, tc
);
7945 pn3
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
7949 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
7952 JSMSG_TRAILING_COMMA
)) {
7957 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7962 tt
= js_GetToken(cx
, ts
);
7963 #if JS_HAS_GETTER_SETTER
7964 if (tt
== TOK_NAME
) {
7965 tt
= CheckGetterOrSetter(cx
, ts
, TOK_COLON
);
7966 if (tt
== TOK_ERROR
)
7971 if (tt
!= TOK_COLON
) {
7972 #if JS_HAS_DESTRUCTURING_SHORTHAND
7973 if (tt
!= TOK_COMMA
&& tt
!= TOK_RC
) {
7975 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7976 JSMSG_COLON_AFTER_ID
);
7978 #if JS_HAS_DESTRUCTURING_SHORTHAND
7982 * Support, e.g., |var {x, y} = o| as destructuring shorthand
7983 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
7986 pn
->pn_xflags
|= PNX_DESTRUCT
;
7988 if (pnval
->pn_type
== TOK_NAME
) {
7989 pnval
->pn_arity
= PN_NAME
;
7990 InitNameNodeCommon(pnval
, tc
);
7995 op
= CURRENT_TOKEN(ts
).t_op
;
7996 pnval
= AssignExpr(cx
, ts
, tc
);
7999 pn2
= NewBinary(TOK_COLON
, op
, pn3
, pnval
, tc
);
8000 #if JS_HAS_GETTER_SETTER
8007 tt
= js_GetToken(cx
, ts
);
8010 if (tt
!= TOK_COMMA
) {
8011 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8012 JSMSG_CURLY_AFTER_LIST
);
8015 afterComma
= JS_TRUE
;
8019 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
8023 #if JS_HAS_BLOCK_SCOPE
8025 pn
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
8031 #if JS_HAS_SHARP_VARS
8033 pn
= NewParseNode(PN_UNARY
, tc
);
8036 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
8037 ts
->flags
|= TSF_OPERAND
;
8038 tt
= js_GetToken(cx
, ts
);
8039 ts
->flags
&= ~TSF_OPERAND
;
8040 if (tt
== TOK_USESHARP
|| tt
== TOK_DEFSHARP
||
8041 #if JS_HAS_XML_SUPPORT
8042 tt
== TOK_STAR
|| tt
== TOK_AT
||
8043 tt
== TOK_XMLSTAGO
/* XXXbe could be sharp? */ ||
8045 tt
== TOK_STRING
|| tt
== TOK_NUMBER
|| tt
== TOK_PRIMARY
) {
8046 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8047 JSMSG_BAD_SHARP_VAR_DEF
);
8050 pn
->pn_kid
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
8053 tc
->flags
|= TCF_HAS_SHARPS
;
8057 /* Check for forward/dangling references at runtime, to allow eval. */
8058 pn
= NewParseNode(PN_NULLARY
, tc
);
8061 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
8062 tc
->flags
|= TCF_HAS_SHARPS
;
8064 #endif /* JS_HAS_SHARP_VARS */
8070 pn
= NewParseNode(PN_UNARY
, tc
);
8073 pn2
= ParenExpr(cx
, ts
, tc
, pn
, &genexp
);
8078 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
8080 /* Check if parentheses were unnecessary. */
8081 if (pn2
->pn_type
== TOK_RP
||
8082 (js_CodeSpec
[pn2
->pn_op
].prec
>= js_CodeSpec
[JSOP_GETPROP
].prec
&&
8084 RecycleTree(pn
, tc
);
8087 pn
->pn_type
= TOK_RP
;
8090 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
8094 #if JS_HAS_XML_SUPPORT
8096 pn
= QualifiedIdentifier(cx
, ts
, tc
);
8102 pn
= AttributeIdentifier(cx
, ts
, tc
);
8108 pn
= XMLElementOrListRoot(cx
, ts
, tc
, JS_TRUE
);
8112 #endif /* JS_HAS_XML_SUPPORT */
8115 #if JS_HAS_SHARP_VARS
8119 #if JS_HAS_XML_SUPPORT
8121 case TOK_XMLCOMMENT
:
8124 pn
= NewParseNode(PN_NULLARY
, tc
);
8127 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
8128 #if JS_HAS_XML_SUPPORT
8129 if (tt
== TOK_XMLPI
)
8130 pn
->pn_atom2
= CURRENT_TOKEN(ts
).t_atom2
;
8133 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
8137 pn
= NewNameNode(cx
, ts
, CURRENT_TOKEN(ts
).t_atom
, tc
);
8140 JS_ASSERT(CURRENT_TOKEN(ts
).t_op
== JSOP_NAME
);
8141 pn
->pn_op
= JSOP_NAME
;
8143 if ((tc
->flags
& (TCF_IN_FUNCTION
| TCF_FUN_PARAM_ARGUMENTS
)) == TCF_IN_FUNCTION
&&
8144 pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
) {
8146 * Flag arguments usage so we can avoid unsafe optimizations such
8147 * as formal parameter assignment analysis (because of the hated
8148 * feature whereby arguments alias formals). We do this even for
8149 * a reference of the form foo.arguments, which ancient code may
8150 * still use instead of arguments (more hate).
8152 NoteArgumentsUse(tc
);
8155 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8156 * to do this work (new rule for the emitter to count on).
8158 if (!afterDot
&& !(ts
->flags
& TSF_DESTRUCTURING
) && !tc
->inStatement(STMT_WITH
)) {
8159 pn
->pn_op
= JSOP_ARGUMENTS
;
8160 pn
->pn_dflags
|= PND_BOUND
;
8162 } else if ((!afterDot
8163 #if JS_HAS_XML_SUPPORT
8164 || js_PeekToken(cx
, ts
) == TOK_DBLCOLON
8166 ) && !(ts
->flags
& TSF_DESTRUCTURING
)) {
8167 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, pn
->pn_atom
, NULL
);
8168 if (!stmt
|| stmt
->type
!= STMT_WITH
) {
8171 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
8174 #if JS_HAS_BLOCK_SCOPE
8176 * Skip out-of-scope let bindings along an ALE list or hash
8177 * chain. These can happen due to |let (x = x) x| block and
8178 * expression bindings, where the x on the right of = comes
8179 * from an outer scope. See bug 496532.
8181 while (dn
->isLet() && !BlockIdInScope(dn
->pn_blockid
, tc
)) {
8183 ale
= ALE_NEXT(ale
);
8184 } while (ale
&& ALE_ATOM(ale
) != pn
->pn_atom
);
8195 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
8200 * No definition before this use in any lexical scope.
8201 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8202 * new node for the forward-referenced definition. This
8203 * placeholder definition node will be adopted when we
8204 * parse the real defining declaration form, or left as
8205 * a free variable definition if we never see the real
8208 ale
= MakePlaceholder(pn
, tc
);
8214 * In case this is a forward reference to a function,
8215 * we pessimistically set PND_FUNARG if the next token
8216 * is not a left parenthesis.
8218 * If the definition eventually parsed into dn is not a
8219 * function, this flag won't hurt, and if we do parse a
8220 * function with pn's name, then the PND_FUNARG flag is
8221 * necessary for safe cx->display-based optimization of
8222 * the closure's static link.
8224 JS_ASSERT(PN_TYPE(dn
) == TOK_NAME
);
8225 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
8226 if (js_PeekToken(cx
, ts
) != TOK_LP
)
8227 dn
->pn_dflags
|= PND_FUNARG
;
8231 JS_ASSERT(dn
->pn_defn
);
8232 LinkUseToDef(pn
, dn
, tc
);
8234 /* Here we handle the backward function reference case. */
8235 if (js_PeekToken(cx
, ts
) != TOK_LP
)
8236 dn
->pn_dflags
|= PND_FUNARG
;
8238 pn
->pn_dflags
|= (dn
->pn_dflags
& PND_FUNARG
);
8242 #if JS_HAS_XML_SUPPORT
8243 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
8248 * Here PrimaryExpr is called after . or .. followed by a name
8249 * followed by ::. This is the only case where a keyword after
8250 * . or .. is not treated as a property name.
8252 str
= ATOM_TO_STRING(pn
->pn_atom
);
8253 tt
= js_CheckKeyword(str
->chars(), str
->length());
8254 if (tt
== TOK_FUNCTION
) {
8255 pn
->pn_arity
= PN_NULLARY
;
8256 pn
->pn_type
= TOK_FUNCTION
;
8257 } else if (tt
!= TOK_EOF
) {
8258 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8259 JSMSG_KEYWORD_NOT_NS
);
8263 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
8274 pn
= NewParseNode(PN_NULLARY
, tc
);
8278 /* Token stream ensures that tokenbuf is NUL-terminated. */
8279 JS_ASSERT(*ts
->tokenbuf
.ptr
== (jschar
) 0);
8280 obj
= js_NewRegExpObject(cx
, ts
,
8282 ts
->tokenbuf
.ptr
- ts
->tokenbuf
.base
,
8283 CURRENT_TOKEN(ts
).t_reflags
);
8286 if (!(tc
->flags
& TCF_COMPILE_N_GO
)) {
8287 STOBJ_CLEAR_PARENT(obj
);
8288 STOBJ_CLEAR_PROTO(obj
);
8291 pn
->pn_objbox
= tc
->compiler
->newObjectBox(obj
);
8295 pn
->pn_op
= JSOP_REGEXP
;
8300 pn
= NewParseNode(PN_NULLARY
, tc
);
8303 pn
->pn_op
= JSOP_DOUBLE
;
8304 pn
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
8308 pn
= NewParseNode(PN_NULLARY
, tc
);
8311 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
8315 /* The scanner or one of its subroutines reported the error. */
8319 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8320 JSMSG_SYNTAX_ERROR
);
8326 static JSParseNode
*
8327 ParenExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
8328 JSParseNode
*pn1
, JSBool
*genexp
)
8333 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LP
);
8334 begin
= CURRENT_TOKEN(ts
).pos
.begin
;
8338 pn
= BracketedExpr(cx
, ts
, tc
);
8342 #if JS_HAS_GENERATOR_EXPRS
8343 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
8344 if (pn
->pn_type
== TOK_YIELD
) {
8345 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
8346 JSMSG_BAD_GENERATOR_SYNTAX
,
8350 if (pn
->pn_type
== TOK_COMMA
) {
8351 js_ReportCompileErrorNumber(cx
, ts
, pn
->last(), JSREPORT_ERROR
,
8352 JSMSG_BAD_GENERATOR_SYNTAX
,
8357 pn1
= NewParseNode(PN_UNARY
, tc
);
8361 pn
= GeneratorExpr(pn1
, pn
, tc
);
8364 pn
->pn_pos
.begin
= begin
;
8366 if (js_GetToken(cx
, ts
) != TOK_RP
) {
8367 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8368 JSMSG_BAD_GENERATOR_SYNTAX
,
8372 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
8376 #endif /* JS_HAS_GENERATOR_EXPRS */
8382 * Fold from one constant type to another.
8383 * XXX handles only strings and numbers for now
8386 FoldType(JSContext
*cx
, JSParseNode
*pn
, JSTokenType type
)
8388 if (PN_TYPE(pn
) != type
) {
8391 if (pn
->pn_type
== TOK_STRING
) {
8393 if (!JS_ValueToNumber(cx
, ATOM_KEY(pn
->pn_atom
), &d
))
8396 pn
->pn_type
= TOK_NUMBER
;
8397 pn
->pn_op
= JSOP_DOUBLE
;
8402 if (pn
->pn_type
== TOK_NUMBER
) {
8403 JSString
*str
= js_NumberToString(cx
, pn
->pn_dval
);
8406 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8409 pn
->pn_type
= TOK_STRING
;
8410 pn
->pn_op
= JSOP_STRING
;
8421 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8422 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8423 * a successful call to this function.
8426 FoldBinaryNumeric(JSContext
*cx
, JSOp op
, JSParseNode
*pn1
, JSParseNode
*pn2
,
8427 JSParseNode
*pn
, JSTreeContext
*tc
)
8432 JS_ASSERT(pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
);
8438 i
= js_DoubleToECMAInt32(d
);
8439 j
= js_DoubleToECMAInt32(d2
);
8441 d
= (op
== JSOP_LSH
) ? i
<< j
: i
>> j
;
8445 j
= js_DoubleToECMAInt32(d2
);
8447 d
= js_DoubleToECMAUint32(d
) >> j
;
8465 /* XXX MSVC miscompiles such that (NaN == 0) */
8466 if (JSDOUBLE_IS_NaN(d2
))
8467 d
= *cx
->runtime
->jsNaN
;
8470 if (d
== 0 || JSDOUBLE_IS_NaN(d
))
8471 d
= *cx
->runtime
->jsNaN
;
8472 else if ((JSDOUBLE_HI32(d
) ^ JSDOUBLE_HI32(d2
)) >> 31)
8473 d
= *cx
->runtime
->jsNegativeInfinity
;
8475 d
= *cx
->runtime
->jsPositiveInfinity
;
8483 d
= *cx
->runtime
->jsNaN
;
8492 /* Take care to allow pn1 or pn2 to alias pn. */
8494 RecycleTree(pn1
, tc
);
8496 RecycleTree(pn2
, tc
);
8497 pn
->pn_type
= TOK_NUMBER
;
8498 pn
->pn_op
= JSOP_DOUBLE
;
8499 pn
->pn_arity
= PN_NULLARY
;
8504 #if JS_HAS_XML_SUPPORT
8507 FoldXMLConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
8510 JSParseNode
**pnp
, *pn1
, *pn2
;
8511 JSString
*accum
, *str
;
8513 JSTempValueRooter tvr
;
8515 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
8520 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8521 if (tt
== TOK_XMLETAGO
)
8522 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.etagoAtom
);
8523 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
)
8524 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.stagoAtom
);
8528 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8529 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8530 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8531 * Therefore, we have to add additonal protection from GC nesting under
8534 for (pn2
= pn1
, i
= j
= 0; pn2
; pn2
= pn2
->pn_next
, i
++) {
8535 /* The parser already rejected end-tags with attributes. */
8536 JS_ASSERT(tt
!= TOK_XMLETAGO
|| i
== 0);
8537 switch (pn2
->pn_type
) {
8546 if (pn2
->pn_arity
== PN_LIST
)
8548 str
= ATOM_TO_STRING(pn2
->pn_atom
);
8552 str
= js_MakeXMLCDATAString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8557 case TOK_XMLCOMMENT
:
8558 str
= js_MakeXMLCommentString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8564 str
= js_MakeXMLPIString(cx
, ATOM_TO_STRING(pn2
->pn_atom
),
8565 ATOM_TO_STRING(pn2
->pn_atom2
));
8572 JS_ASSERT(*pnp
== pn1
);
8573 if ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) &&
8574 (i
& 1) ^ (j
& 1)) {
8575 #ifdef DEBUG_brendanXXX
8576 printf("1: %d, %d => ", i
, j
);
8578 js_FileEscapedString(stdout
, accum
, 0);
8580 fputs("NULL", stdout
);
8581 fputc('\n', stdout
);
8583 } else if (accum
&& pn1
!= pn2
) {
8584 while (pn1
->pn_next
!= pn2
) {
8585 pn1
= RecycleTree(pn1
, tc
);
8588 pn1
->pn_type
= TOK_XMLTEXT
;
8589 pn1
->pn_op
= JSOP_STRING
;
8590 pn1
->pn_arity
= PN_NULLARY
;
8591 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8594 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8597 pnp
= &pn2
->pn_next
;
8604 JS_PUSH_TEMP_ROOT_STRING(cx
, accum
, &tvr
);
8605 str
= ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) && i
!= 0)
8606 ? js_AddAttributePart(cx
, i
& 1, accum
, str
)
8607 : js_ConcatStrings(cx
, accum
, str
);
8608 JS_POP_TEMP_ROOT(cx
, &tvr
);
8611 #ifdef DEBUG_brendanXXX
8612 printf("2: %d, %d => ", i
, j
);
8613 js_FileEscapedString(stdout
, str
, 0);
8614 printf(" (%u)\n", str
->length());
8623 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8624 if (tt
== TOK_XMLPTAGC
)
8625 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.ptagcAtom
);
8626 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLETAGO
)
8627 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.tagcAtom
);
8630 accum
= js_ConcatStrings(cx
, accum
, str
);
8635 JS_ASSERT(*pnp
== pn1
);
8636 while (pn1
->pn_next
) {
8637 pn1
= RecycleTree(pn1
, tc
);
8640 pn1
->pn_type
= TOK_XMLTEXT
;
8641 pn1
->pn_op
= JSOP_STRING
;
8642 pn1
->pn_arity
= PN_NULLARY
;
8643 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8646 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8650 if (pn1
&& pn
->pn_count
== 1) {
8652 * Only one node under pn, and it has been folded: move pn1 onto pn
8653 * unless pn is an XML root (in which case we need it to tell the code
8654 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8655 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8656 * extra "<" and "/>" bracketing at runtime.
8658 if (!(pn
->pn_xflags
& PNX_XMLROOT
)) {
8660 } else if (tt
== TOK_XMLPTAGC
) {
8661 pn
->pn_type
= TOK_XMLELEM
;
8662 pn
->pn_op
= JSOP_TOXML
;
8668 #endif /* JS_HAS_XML_SUPPORT */
8671 StartsWith(JSParseNode
*pn
, JSTokenType tt
)
8673 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
8676 if (PN_TYPE(pn
) == tt
)
8678 switch (pn
->pn_arity
) {
8680 return tt
== TOK_FUNCTION
;
8683 TAIL_RECURSE(pn
->pn_head
);
8687 TAIL_RECURSE(pn
->pn_kid1
);
8691 TAIL_RECURSE(pn
->pn_left
);
8694 /* A parenthesized expression starts with a left parenthesis. */
8695 if (pn
->pn_type
== TOK_RP
)
8696 return tt
== TOK_LP
;
8698 TAIL_RECURSE(pn
->pn_kid
);
8701 if (pn
->pn_type
== TOK_DOT
|| pn
->pn_type
== TOK_DBLDOT
)
8702 TAIL_RECURSE(pn
->expr());
8705 TAIL_RECURSE(pn
->pn_tree
);
8713 Boolish(JSParseNode
*pn
)
8715 switch (pn
->pn_op
) {
8717 return pn
->pn_dval
!= 0 && !JSDOUBLE_IS_NaN(pn
->pn_dval
);
8720 return ATOM_TO_STRING(pn
->pn_atom
)->length() != 0;
8722 #if JS_HAS_GENERATOR_EXPRS
8726 * A generator expression as an if or loop condition has no effects, it
8727 * simply results in a truthy object reference. This condition folding
8728 * is needed for the decompiler. See bug 442342 and bug 443074.
8730 if (pn
->pn_count
!= 1)
8732 JSParseNode
*pn2
= pn
->pn_head
;
8733 if (pn2
->pn_type
!= TOK_FUNCTION
)
8735 if (!(pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
))
8757 js_FoldConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, bool inCond
)
8759 JSParseNode
*pn1
= NULL
, *pn2
= NULL
, *pn3
= NULL
;
8761 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
8763 switch (pn
->pn_arity
) {
8766 uint16 oldflags
= tc
->flags
;
8767 JSFunctionBox
*oldlist
= tc
->functionList
;
8769 tc
->flags
= (uint16
) pn
->pn_funbox
->tcflags
;
8770 tc
->functionList
= pn
->pn_funbox
->kids
;
8771 if (!js_FoldConstants(cx
, pn
->pn_body
, tc
))
8773 pn
->pn_funbox
->kids
= tc
->functionList
;
8774 tc
->flags
= oldflags
;
8775 tc
->functionList
= oldlist
;
8781 /* Propagate inCond through logical connectives. */
8782 bool cond
= inCond
&& (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
);
8784 /* Save the list head in pn1 for later use. */
8785 for (pn1
= pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
8786 if (!js_FoldConstants(cx
, pn2
, tc
, cond
))
8793 /* Any kid may be null (e.g. for (;;)). */
8797 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_IF
))
8800 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_FORHEAD
))
8802 if (pn
->pn_type
== TOK_FORHEAD
&& pn2
->pn_op
== JSOP_TRUE
) {
8803 RecycleTree(pn2
, tc
);
8807 if (pn3
&& !js_FoldConstants(cx
, pn3
, tc
))
8815 /* Propagate inCond through logical connectives. */
8816 if (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
) {
8817 if (!js_FoldConstants(cx
, pn1
, tc
, inCond
))
8819 if (!js_FoldConstants(cx
, pn2
, tc
, inCond
))
8824 /* First kid may be null (for default case in switch). */
8825 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_WHILE
))
8827 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_DO
))
8832 /* Our kid may be null (e.g. return; vs. return e;). */
8835 !js_FoldConstants(cx
, pn1
, tc
,
8836 (inCond
&& pn
->pn_type
== TOK_RP
) ||
8837 pn
->pn_op
== JSOP_NOT
)) {
8844 * Skip pn1 down along a chain of dotted member expressions to avoid
8845 * excessive recursion. Our only goal here is to fold constants (if
8846 * any) in the primary expression operand to the left of the first
8851 while (pn1
&& pn1
->pn_arity
== PN_NAME
&& !pn1
->pn_used
)
8853 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
8860 if (!js_FoldConstants(cx
, pn1
, tc
))
8868 switch (pn
->pn_type
) {
8870 if (ContainsStmt(pn2
, TOK_VAR
) || ContainsStmt(pn3
, TOK_VAR
))
8875 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8876 while (pn1
->pn_type
== TOK_RP
)
8878 switch (pn1
->pn_type
) {
8880 if (pn1
->pn_dval
== 0 || JSDOUBLE_IS_NaN(pn1
->pn_dval
))
8884 if (ATOM_TO_STRING(pn1
->pn_atom
)->length() == 0)
8888 if (pn1
->pn_op
== JSOP_TRUE
)
8890 if (pn1
->pn_op
== JSOP_FALSE
|| pn1
->pn_op
== JSOP_NULL
) {
8896 /* Early return to dodge common code that copies pn2 to pn. */
8900 #if JS_HAS_GENERATOR_EXPRS
8901 /* Don't fold a trailing |if (0)| in a generator expression. */
8902 if (!pn2
&& (tc
->flags
& TCF_GENEXP_LAMBDA
))
8908 * pn2 is the then- or else-statement subtree to compile. Take
8909 * care not to expose an object initialiser, which would be parsed
8910 * as a block, to the Statement parser via eval(uneval(e)) where e
8911 * is '1 ? {p:2, q:3}[i] : r;' or the like.
8913 if (pn
->pn_type
== TOK_HOOK
&& StartsWith(pn2
, TOK_RC
)) {
8914 pn
->pn_type
= TOK_RP
;
8915 pn
->pn_arity
= PN_UNARY
;
8917 if (pn3
&& pn3
!= pn2
)
8918 RecycleTree(pn3
, tc
);
8924 if (!pn2
|| (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
)) {
8926 * False condition and no else, or an empty then-statement was
8927 * moved up over pn. Either way, make pn an empty block (not an
8928 * empty statement, which does not decompile, even when labeled).
8929 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8930 * or an empty statement for a child.
8932 pn
->pn_type
= TOK_LC
;
8933 pn
->pn_arity
= PN_LIST
;
8936 RecycleTree(pn2
, tc
);
8937 if (pn3
&& pn3
!= pn2
)
8938 RecycleTree(pn3
, tc
);
8944 if (pn
->pn_arity
== PN_LIST
) {
8945 JSParseNode
**pnp
= &pn
->pn_head
;
8946 JS_ASSERT(*pnp
== pn1
);
8948 int cond
= Boolish(pn1
);
8949 if (cond
== (pn
->pn_type
== TOK_OR
)) {
8950 for (pn2
= pn1
->pn_next
; pn2
; pn2
= pn3
) {
8952 RecycleTree(pn2
, tc
);
8955 pn1
->pn_next
= NULL
;
8959 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
8960 if (pn
->pn_count
== 1)
8962 *pnp
= pn1
->pn_next
;
8963 RecycleTree(pn1
, tc
);
8966 pnp
= &pn1
->pn_next
;
8968 } while ((pn1
= *pnp
) != NULL
);
8970 // We may have to change arity from LIST to BINARY.
8972 if (pn
->pn_count
== 2) {
8974 pn1
->pn_next
= NULL
;
8975 JS_ASSERT(!pn2
->pn_next
);
8976 pn
->pn_arity
= PN_BINARY
;
8979 } else if (pn
->pn_count
== 1) {
8981 RecycleTree(pn1
, tc
);
8984 int cond
= Boolish(pn1
);
8985 if (cond
== (pn
->pn_type
== TOK_OR
)) {
8986 RecycleTree(pn2
, tc
);
8988 } else if (cond
!= -1) {
8989 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
8990 RecycleTree(pn1
, tc
);
8999 * Compound operators such as *= should be subject to folding, in case
9000 * the left-hand side is constant, and so that the decompiler produces
9001 * the same string that you get from decompiling a script or function
9002 * compiled from that same string. As with +, += is special.
9004 if (pn
->pn_op
== JSOP_NOP
)
9006 if (pn
->pn_op
!= JSOP_ADD
)
9011 if (pn
->pn_arity
== PN_LIST
) {
9012 size_t length
, length2
;
9014 JSString
*str
, *str2
;
9017 * Any string literal term with all others number or string means
9018 * this is a concatenation. If any term is not a string or number
9019 * literal, we can't fold.
9021 JS_ASSERT(pn
->pn_count
> 2);
9022 if (pn
->pn_xflags
& PNX_CANTFOLD
)
9024 if (pn
->pn_xflags
!= PNX_STRCAT
)
9027 /* Ok, we're concatenating: convert non-string constant operands. */
9029 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9030 if (!FoldType(cx
, pn2
, TOK_STRING
))
9032 /* XXX fold only if all operands convert to string */
9033 if (pn2
->pn_type
!= TOK_STRING
)
9035 length
+= ATOM_TO_STRING(pn2
->pn_atom
)->flatLength();
9038 /* Allocate a new buffer and string descriptor for the result. */
9039 chars
= (jschar
*) cx
->malloc((length
+ 1) * sizeof(jschar
));
9042 str
= js_NewString(cx
, chars
, length
);
9048 /* Fill the buffer, advancing chars and recycling kids as we go. */
9049 for (pn2
= pn1
; pn2
; pn2
= RecycleTree(pn2
, tc
)) {
9050 str2
= ATOM_TO_STRING(pn2
->pn_atom
);
9051 length2
= str2
->flatLength();
9052 js_strncpy(chars
, str2
->flatChars(), length2
);
9057 /* Atomize the result string and mutate pn to refer to it. */
9058 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
9061 pn
->pn_type
= TOK_STRING
;
9062 pn
->pn_op
= JSOP_STRING
;
9063 pn
->pn_arity
= PN_NULLARY
;
9067 /* Handle a binary string concatenation. */
9068 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
9069 if (pn1
->pn_type
== TOK_STRING
|| pn2
->pn_type
== TOK_STRING
) {
9070 JSString
*left
, *right
, *str
;
9072 if (!FoldType(cx
, (pn1
->pn_type
!= TOK_STRING
) ? pn1
: pn2
,
9076 if (pn1
->pn_type
!= TOK_STRING
|| pn2
->pn_type
!= TOK_STRING
)
9078 left
= ATOM_TO_STRING(pn1
->pn_atom
);
9079 right
= ATOM_TO_STRING(pn2
->pn_atom
);
9080 str
= js_ConcatStrings(cx
, left
, right
);
9083 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
9086 pn
->pn_type
= TOK_STRING
;
9087 pn
->pn_op
= JSOP_STRING
;
9088 pn
->pn_arity
= PN_NULLARY
;
9089 RecycleTree(pn1
, tc
);
9090 RecycleTree(pn2
, tc
);
9094 /* Can't concatenate string literals, let's try numbers. */
9102 if (pn
->pn_arity
== PN_LIST
) {
9103 JS_ASSERT(pn
->pn_count
> 2);
9104 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9105 if (!FoldType(cx
, pn2
, TOK_NUMBER
))
9108 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9109 /* XXX fold only if all operands convert to number */
9110 if (pn2
->pn_type
!= TOK_NUMBER
)
9114 JSOp op
= PN_OP(pn
);
9118 if (!FoldBinaryNumeric(cx
, op
, pn1
, pn2
, pn
, tc
))
9120 while ((pn2
= pn3
) != NULL
) {
9122 if (!FoldBinaryNumeric(cx
, op
, pn
, pn2
, pn
, tc
))
9127 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
9128 if (!FoldType(cx
, pn1
, TOK_NUMBER
) ||
9129 !FoldType(cx
, pn2
, TOK_NUMBER
)) {
9132 if (pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
) {
9133 if (!FoldBinaryNumeric(cx
, PN_OP(pn
), pn1
, pn2
, pn
, tc
))
9140 while (pn1
->pn_type
== TOK_RP
)
9142 if (pn1
->pn_type
== TOK_NUMBER
) {
9145 /* Operate on one numeric constant. */
9147 switch (pn
->pn_op
) {
9149 d
= ~js_DoubleToECMAInt32(d
);
9155 * Negation of a zero doesn't produce a negative
9156 * zero on HPUX. Perform the operation by bit
9159 JSDOUBLE_HI32(d
) ^= JSDOUBLE_HI32_SIGNBIT
;
9169 pn
->pn_type
= TOK_PRIMARY
;
9170 pn
->pn_op
= (d
== 0 || JSDOUBLE_IS_NaN(d
)) ? JSOP_TRUE
: JSOP_FALSE
;
9171 pn
->pn_arity
= PN_NULLARY
;
9175 /* Return early to dodge the common TOK_NUMBER code. */
9178 pn
->pn_type
= TOK_NUMBER
;
9179 pn
->pn_op
= JSOP_DOUBLE
;
9180 pn
->pn_arity
= PN_NULLARY
;
9182 RecycleTree(pn1
, tc
);
9183 } else if (pn1
->pn_type
== TOK_PRIMARY
) {
9184 if (pn
->pn_op
== JSOP_NOT
&&
9185 (pn1
->pn_op
== JSOP_TRUE
||
9186 pn1
->pn_op
== JSOP_FALSE
)) {
9188 pn
->pn_op
= (pn
->pn_op
== JSOP_TRUE
) ? JSOP_FALSE
: JSOP_TRUE
;
9189 RecycleTree(pn1
, tc
);
9194 #if JS_HAS_XML_SUPPORT
9201 if (pn
->pn_arity
== PN_LIST
) {
9202 JS_ASSERT(pn
->pn_type
== TOK_XMLLIST
|| pn
->pn_count
!= 0);
9203 if (!FoldXMLConstants(cx
, pn
, tc
))
9209 if (pn1
->pn_type
== TOK_XMLNAME
) {
9211 JSObjectBox
*xmlbox
;
9213 v
= ATOM_KEY(pn1
->pn_atom
);
9214 if (!js_ToAttributeName(cx
, &v
))
9216 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
9218 xmlbox
= tc
->compiler
->newObjectBox(JSVAL_TO_OBJECT(v
));
9222 pn
->pn_type
= TOK_XMLNAME
;
9223 pn
->pn_op
= JSOP_OBJECT
;
9224 pn
->pn_arity
= PN_NULLARY
;
9225 pn
->pn_objbox
= xmlbox
;
9226 RecycleTree(pn1
, tc
);
9229 #endif /* JS_HAS_XML_SUPPORT */
9235 int cond
= Boolish(pn
);
9237 switch (pn
->pn_arity
) {
9242 RecycleTree(pn2
, tc
);
9243 } while ((pn2
= pn3
) != NULL
);
9246 RecycleFuncNameKids(pn
, tc
);
9251 JS_NOT_REACHED("unhandled arity");
9253 pn
->pn_type
= TOK_PRIMARY
;
9254 pn
->pn_op
= cond
? JSOP_TRUE
: JSOP_FALSE
;
9255 pn
->pn_arity
= PN_NULLARY
;