1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla Communicator client code, released
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
43 * This is a recursive-descent parser for the JavaScript language specified by
44 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
45 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
46 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
47 * After tree construction, it rewrites trees to fold constants and evaluate
48 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
51 * This parser attempts no error recovery. The dense JSTokenType enumeration
52 * was designed with error recovery built on 64-bit first and follow bitsets
60 #include "jsarena.h" /* Added by JSIFY */
61 #include "jsutil.h" /* Added by JSIFY */
80 * JS parsers, from lowest to highest precedence.
82 * Each parser takes a context and a token stream, and emits bytecode using
87 JSParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
);
90 JSMemberParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
91 JSBool allowCallSyntax
);
93 static JSParser FunctionStmt
;
94 #if JS_HAS_LEXICAL_CLOSURE
95 static JSParser FunctionExpr
;
97 static JSParser Statements
;
98 static JSParser Statement
;
99 static JSParser Variables
;
100 static JSParser Expr
;
101 static JSParser AssignExpr
;
102 static JSParser CondExpr
;
103 static JSParser OrExpr
;
104 static JSParser AndExpr
;
105 static JSParser BitOrExpr
;
106 static JSParser BitXorExpr
;
107 static JSParser BitAndExpr
;
108 static JSParser EqExpr
;
109 static JSParser RelExpr
;
110 static JSParser ShiftExpr
;
111 static JSParser AddExpr
;
112 static JSParser MulExpr
;
113 static JSParser UnaryExpr
;
114 static JSMemberParser MemberExpr
;
115 static JSParser PrimaryExpr
;
118 * Insist that the next token be of type tt, or report errno and return null.
119 * NB: this macro uses cx and ts from its lexical environment.
121 #define MUST_MATCH_TOKEN(tt, errno) \
123 if (js_GetToken(cx, ts) != tt) { \
124 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
129 #define CHECK_RECURSION() \
132 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \
133 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
134 JSMSG_OVER_RECURSED); \
139 #ifdef METER_PARSENODES
140 static uint32 parsenodes
= 0;
141 static uint32 maxparsenodes
= 0;
142 static uint32 recyclednodes
= 0;
146 RecycleTree(JSParseNode
*pn
, JSTreeContext
*tc
)
150 JS_ASSERT(pn
!= tc
->nodeList
); /* catch back-to-back dup recycles */
151 pn
->pn_next
= tc
->nodeList
;
153 #ifdef METER_PARSENODES
159 NewOrRecycledNode(JSContext
*cx
, JSTreeContext
*tc
)
165 JS_ARENA_ALLOCATE_TYPE(pn
, JSParseNode
, &cx
->tempPool
);
167 JS_ReportOutOfMemory(cx
);
169 tc
->nodeList
= pn
->pn_next
;
171 /* Recycle immediate descendents only, to save work and working set. */
172 switch (pn
->pn_arity
) {
174 RecycleTree(pn
->pn_body
, tc
);
178 /* XXX check for dup recycles in the list */
179 *pn
->pn_tail
= tc
->nodeList
;
180 tc
->nodeList
= pn
->pn_head
;
181 #ifdef METER_PARSENODES
182 recyclednodes
+= pn
->pn_count
;
187 RecycleTree(pn
->pn_kid1
, tc
);
188 RecycleTree(pn
->pn_kid2
, tc
);
189 RecycleTree(pn
->pn_kid3
, tc
);
192 RecycleTree(pn
->pn_left
, tc
);
193 RecycleTree(pn
->pn_right
, tc
);
196 RecycleTree(pn
->pn_kid
, tc
);
199 RecycleTree(pn
->pn_expr
, tc
);
209 * Allocate a JSParseNode from cx's temporary arena.
212 NewParseNode(JSContext
*cx
, JSToken
*tok
, JSParseNodeArity arity
,
217 pn
= NewOrRecycledNode(cx
, tc
);
220 pn
->pn_type
= tok
->type
;
221 pn
->pn_pos
= tok
->pos
;
222 pn
->pn_op
= JSOP_NOP
;
223 pn
->pn_arity
= arity
;
225 #ifdef METER_PARSENODES
227 if (parsenodes
- recyclednodes
> maxparsenodes
)
228 maxparsenodes
= parsenodes
- recyclednodes
;
234 NewBinary(JSContext
*cx
, JSTokenType tt
,
235 JSOp op
, JSParseNode
*left
, JSParseNode
*right
,
238 JSParseNode
*pn
, *pn1
, *pn2
;
244 * Flatten a left-associative (left-heavy) tree of a given operator into
245 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
247 if (left
->pn_type
== tt
&&
249 (js_CodeSpec
[op
].format
& JOF_LEFTASSOC
)) {
250 if (left
->pn_arity
!= PN_LIST
) {
251 pn1
= left
->pn_left
, pn2
= left
->pn_right
;
252 left
->pn_arity
= PN_LIST
;
253 PN_INIT_LIST_1(left
, pn1
);
254 PN_APPEND(left
, pn2
);
256 if (tt
== TOK_PLUS
) {
257 if (pn1
->pn_type
== TOK_STRING
)
258 left
->pn_extra
|= PNX_STRCAT
;
259 else if (pn1
->pn_type
!= TOK_NUMBER
)
260 left
->pn_extra
|= PNX_CANTFOLD
;
261 if (pn2
->pn_type
== TOK_STRING
)
262 left
->pn_extra
|= PNX_STRCAT
;
263 else if (pn2
->pn_type
!= TOK_NUMBER
)
264 left
->pn_extra
|= PNX_CANTFOLD
;
267 PN_APPEND(left
, right
);
268 left
->pn_pos
.end
= right
->pn_pos
.end
;
269 if (tt
== TOK_PLUS
) {
270 if (right
->pn_type
== TOK_STRING
)
271 left
->pn_extra
|= PNX_STRCAT
;
272 else if (right
->pn_type
!= TOK_NUMBER
)
273 left
->pn_extra
|= PNX_CANTFOLD
;
279 * Fold constant addition immediately, to conserve node space and, what's
280 * more, so js_FoldConstants never sees mixed addition and concatenation
281 * operations with more than one leading non-string operand in a PN_LIST
282 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
283 * to "3pt", not "12pt").
285 if (tt
== TOK_PLUS
&&
286 left
->pn_type
== TOK_NUMBER
&&
287 right
->pn_type
== TOK_NUMBER
) {
288 left
->pn_dval
+= right
->pn_dval
;
289 RecycleTree(right
, tc
);
293 pn
= NewOrRecycledNode(cx
, tc
);
297 pn
->pn_pos
.begin
= left
->pn_pos
.begin
;
298 pn
->pn_pos
.end
= right
->pn_pos
.end
;
300 pn
->pn_arity
= PN_BINARY
;
302 pn
->pn_right
= right
;
304 #ifdef METER_PARSENODES
306 if (parsenodes
- recyclednodes
> maxparsenodes
)
307 maxparsenodes
= parsenodes
- recyclednodes
;
312 #if JS_HAS_GETTER_SETTER
314 CheckGetterOrSetter(JSContext
*cx
, JSTokenStream
*ts
, JSTokenType tt
)
321 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_NAME
);
322 atom
= CURRENT_TOKEN(ts
).t_atom
;
324 if (atom
== rt
->atomState
.getterAtom
)
326 else if (atom
== rt
->atomState
.setterAtom
)
330 if (js_PeekTokenSameLine(cx
, ts
) != tt
)
332 (void) js_GetToken(cx
, ts
);
333 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
334 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
335 JSMSG_BAD_GETTER_OR_SETTER
,
341 CURRENT_TOKEN(ts
).t_op
= op
;
342 name
= js_AtomToPrintableString(cx
, atom
);
344 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
347 JSMSG_DEPRECATED_USAGE
,
356 * Parse a top-level JS script.
358 JS_FRIEND_API(JSParseNode
*)
359 js_ParseTokenStream(JSContext
*cx
, JSObject
*chain
, JSTokenStream
*ts
)
361 JSStackFrame
*fp
, frame
;
366 * Push a compiler frame if we have no frames, or if the top frame is a
367 * lightweight function activation, or if its scope chain doesn't match
368 * the one passed to us.
371 if (!fp
|| !fp
->varobj
|| fp
->scopeChain
!= chain
) {
372 memset(&frame
, 0, sizeof frame
);
373 frame
.varobj
= frame
.scopeChain
= chain
;
374 if (cx
->options
& JSOPTION_VAROBJFIX
) {
375 while ((chain
= JS_GetParent(cx
, chain
)) != NULL
)
376 frame
.varobj
= chain
;
383 * Protect atoms from being collected by a GC activation, which might
384 * - nest on this thread due to out of memory (the so-called "last ditch"
385 * GC attempted within js_AllocGCThing), or
386 * - run for any reason on another thread if this thread is suspended on
387 * an object lock before it finishes generating bytecode into a script
388 * protected from the GC by a root or a stack frame reference.
390 JS_KEEP_ATOMS(cx
->runtime
);
391 TREE_CONTEXT_INIT(&tc
);
392 pn
= Statements(cx
, ts
, &tc
);
394 if (!js_MatchToken(cx
, ts
, TOK_EOF
)) {
395 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
399 pn
->pn_type
= TOK_LC
;
400 if (!js_FoldConstants(cx
, pn
, &tc
))
405 TREE_CONTEXT_FINISH(&tc
);
406 JS_UNKEEP_ATOMS(cx
->runtime
);
412 * Compile a top-level script.
414 JS_FRIEND_API(JSBool
)
415 js_CompileTokenStream(JSContext
*cx
, JSObject
*chain
, JSTokenStream
*ts
,
418 JSStackFrame
*fp
, frame
;
422 #ifdef METER_PARSENODES
423 void *sbrk(ptrdiff_t), *before
= sbrk(0);
427 * Push a compiler frame if we have no frames, or if the top frame is a
428 * lightweight function activation, or if its scope chain doesn't match
429 * the one passed to us.
432 if (!fp
|| !fp
->varobj
|| fp
->scopeChain
!= chain
) {
433 memset(&frame
, 0, sizeof frame
);
434 frame
.varobj
= frame
.scopeChain
= chain
;
435 if (cx
->options
& JSOPTION_VAROBJFIX
) {
436 while ((chain
= JS_GetParent(cx
, chain
)) != NULL
)
437 frame
.varobj
= chain
;
442 flags
= cx
->fp
->flags
;
443 cx
->fp
->flags
= flags
|
444 (JS_HAS_COMPILE_N_GO_OPTION(cx
)
445 ? JSFRAME_COMPILING
| JSFRAME_COMPILE_N_GO
446 : JSFRAME_COMPILING
);
448 /* Prevent GC activation while compiling. */
449 JS_KEEP_ATOMS(cx
->runtime
);
451 pn
= Statements(cx
, ts
, &cg
->treeContext
);
454 } else if (!js_MatchToken(cx
, ts
, TOK_EOF
)) {
455 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
459 #ifdef METER_PARSENODES
460 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
461 (char *)sbrk(0) - (char *)before
,
464 parsenodes
- recyclednodes
);
469 * No need to emit code here -- Statements already has, for each
470 * statement in turn. Search for TCF_COMPILING in Statements, below.
471 * That flag is set for every tc == &cg->treeContext, and it implies
472 * that the tc can be downcast to a cg and used to emit code during
473 * parsing, rather than at the end of the parse phase.
475 JS_ASSERT(cg
->treeContext
.flags
& TCF_COMPILING
);
479 #ifdef METER_PARSENODES
480 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
481 (char *)sbrk(0) - (char *)before
, CG_OFFSET(cg
), cg
->noteCount
);
484 JS_DumpArenaStats(stdout
);
486 JS_UNKEEP_ATOMS(cx
->runtime
);
487 cx
->fp
->flags
= flags
;
493 * Insist on a final return before control flows out of pn, but don't be too
494 * smart about loops (do {...; return e2;} while(0) at the end of a function
495 * that contains an early return e1 will get a strict-option-only warning).
497 #define ENDS_IN_OTHER 0
498 #define ENDS_IN_RETURN 1
499 #define ENDS_IN_BREAK 2
502 HasFinalReturn(JSParseNode
*pn
)
504 uintN rv
, rv2
, hasDefault
;
505 JSParseNode
*pn2
, *pn3
;
507 switch (pn
->pn_type
) {
510 return ENDS_IN_OTHER
;
511 return HasFinalReturn(PN_LAST(pn
));
514 rv
= HasFinalReturn(pn
->pn_kid2
);
516 rv
&= HasFinalReturn(pn
->pn_kid3
);
519 #if JS_HAS_SWITCH_STATEMENT
522 hasDefault
= ENDS_IN_OTHER
;
523 for (pn2
= pn
->pn_kid2
->pn_head
; rv
&& pn2
; pn2
= pn2
->pn_next
) {
524 if (pn2
->pn_type
== TOK_DEFAULT
)
525 hasDefault
= ENDS_IN_RETURN
;
527 JS_ASSERT(pn3
->pn_type
== TOK_LC
);
529 rv2
= HasFinalReturn(PN_LAST(pn3
));
530 if (rv2
== ENDS_IN_OTHER
&& pn2
->pn_next
)
531 /* Falling through to next case or default. */;
536 /* If a final switch has no default case, we judge it harshly. */
539 #endif /* JS_HAS_SWITCH_STATEMENT */
542 return ENDS_IN_BREAK
;
545 return HasFinalReturn(pn
->pn_right
);
548 return ENDS_IN_RETURN
;
551 return HasFinalReturn(pn
->pn_expr
);
553 #if JS_HAS_EXCEPTIONS
555 return ENDS_IN_RETURN
;
558 /* If we have a finally block that returns, we are done. */
560 rv
= HasFinalReturn(pn
->pn_kid3
);
561 if (rv
== ENDS_IN_RETURN
)
565 /* Else check the try block and any and all catch statements. */
566 rv
= HasFinalReturn(pn
->pn_kid1
);
568 rv
&= HasFinalReturn(pn
->pn_kid2
);
572 /* Check this block's code and iterate over further catch blocks. */
573 rv
= HasFinalReturn(pn
->pn_kid3
);
574 for (pn2
= pn
->pn_kid2
; pn2
; pn2
= pn2
->pn_kid2
)
575 rv
&= HasFinalReturn(pn2
->pn_kid3
);
580 return ENDS_IN_OTHER
;
585 ReportNoReturnValue(JSContext
*cx
, JSTokenStream
*ts
)
592 char *name
= js_GetStringBytes(ATOM_TO_STRING(fun
->atom
));
593 ok
= js_ReportCompileErrorNumber(cx
, ts
, NULL
,
596 JSMSG_NO_RETURN_VALUE
, name
);
598 ok
= js_ReportCompileErrorNumber(cx
, ts
, NULL
,
601 JSMSG_ANON_NO_RETURN_VALUE
);
607 CheckFinalReturn(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
)
609 return HasFinalReturn(pn
) == ENDS_IN_RETURN
|| ReportNoReturnValue(cx
, ts
);
613 FunctionBody(JSContext
*cx
, JSTokenStream
*ts
, JSFunction
*fun
,
616 JSStackFrame
*fp
, frame
;
622 funobj
= fun
->object
;
623 if (!fp
|| fp
->fun
!= fun
|| fp
->varobj
!= funobj
||
624 fp
->scopeChain
!= funobj
) {
625 memset(&frame
, 0, sizeof frame
);
627 frame
.varobj
= frame
.scopeChain
= funobj
;
632 oldflags
= tc
->flags
;
633 tc
->flags
&= ~(TCF_RETURN_EXPR
| TCF_RETURN_VOID
);
634 tc
->flags
|= TCF_IN_FUNCTION
;
635 pn
= Statements(cx
, ts
, tc
);
637 /* Check for falling off the end of a function that returns a value. */
638 if (pn
&& JS_HAS_STRICT_OPTION(cx
) && (tc
->flags
& TCF_RETURN_EXPR
)) {
639 if (!CheckFinalReturn(cx
, ts
, pn
))
644 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
649 * Compile a JS function body, which might appear as the value of an event
650 * handler attribute in an HTML <INPUT> tag.
653 js_CompileFunctionBody(JSContext
*cx
, JSTokenStream
*ts
, JSFunction
*fun
)
655 JSArenaPool codePool
, notePool
;
656 JSCodeGenerator funcg
;
657 JSStackFrame
*fp
, frame
;
662 JS_InitArenaPool(&codePool
, "code", 1024, sizeof(jsbytecode
));
663 JS_InitArenaPool(¬ePool
, "note", 1024, sizeof(jssrcnote
));
664 if (!js_InitCodeGenerator(cx
, &funcg
, &codePool
, ¬ePool
,
665 ts
->filename
, ts
->lineno
,
670 /* Prevent GC activation while compiling. */
671 JS_KEEP_ATOMS(cx
->runtime
);
673 /* Push a JSStackFrame for use by FunctionBody. */
675 funobj
= fun
->object
;
676 JS_ASSERT(!fp
|| (fp
->fun
!= fun
&& fp
->varobj
!= funobj
&&
677 fp
->scopeChain
!= funobj
));
678 memset(&frame
, 0, sizeof frame
);
680 frame
.varobj
= frame
.scopeChain
= funobj
;
682 frame
.flags
= JS_HAS_COMPILE_N_GO_OPTION(cx
)
683 ? JSFRAME_COMPILING
| JSFRAME_COMPILE_N_GO
687 /* Ensure that the body looks like a block statement to js_EmitTree. */
688 CURRENT_TOKEN(ts
).type
= TOK_LC
;
689 pn
= FunctionBody(cx
, ts
, fun
, &funcg
.treeContext
);
694 * No need to emit code here -- Statements (via FunctionBody) already
695 * has. See similar comment in js_CompileTokenStream, and bug 108257.
697 fun
->script
= js_NewScriptFromCG(cx
, &funcg
, fun
);
701 if (funcg
.treeContext
.flags
& TCF_FUN_HEAVYWEIGHT
)
702 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
707 /* Restore saved state and release code generation arenas. */
709 JS_UNKEEP_ATOMS(cx
->runtime
);
710 js_FinishCodeGenerator(cx
, &funcg
);
711 JS_FinishArenaPool(&codePool
);
712 JS_FinishArenaPool(¬ePool
);
717 FunctionDef(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
720 JSParseNode
*pn
, *body
;
722 JSAtom
*funAtom
, *argAtom
;
730 JSAtomListElement
*ale
;
732 /* Make a TOK_FUNCTION node. */
733 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_FUNC
, tc
);
736 #if JS_HAS_GETTER_SETTER
737 op
= CURRENT_TOKEN(ts
).t_op
;
740 /* Scan the optional function name into funAtom. */
741 if (js_MatchToken(cx
, ts
, TOK_NAME
))
742 funAtom
= CURRENT_TOKEN(ts
).t_atom
;
746 /* Find the nearest variable-declaring scope and use it as our parent. */
747 parent
= cx
->fp
->varobj
;
748 fun
= js_NewFunction(cx
, NULL
, NULL
, 0, lambda
? JSFUN_LAMBDA
: 0, parent
,
753 #if JS_HAS_GETTER_SETTER
755 fun
->flags
|= (op
== JSOP_GETTER
) ? JSPROP_GETTER
: JSPROP_SETTER
;
758 /* Now parse formal argument list and compute fun->nargs. */
759 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_FORMAL
);
760 if (!js_MatchToken(cx
, ts
, TOK_RP
)) {
762 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_MISSING_FORMAL
);
763 argAtom
= CURRENT_TOKEN(ts
).t_atom
;
765 if (!js_LookupProperty(cx
, fun
->object
, (jsid
)argAtom
, &pobj
,
772 if (pobj
== fun
->object
&&
773 ((JSScopeProperty
*) prop
)->getter
== js_GetArgument
) {
774 const char *name
= js_AtomToPrintableString(cx
, argAtom
);
777 * A duplicate parameter name. We force a duplicate node
778 * on the SCOPE_LAST_PROP(scope) list with the same id,
779 * distinguished by the SPROP_IS_DUPLICATE flag, and not
780 * mapped by an entry in scope.
783 js_ReportCompileErrorNumber(cx
, ts
, NULL
,
786 JSMSG_DUPLICATE_FORMAL
,
789 dupflag
= SPROP_IS_DUPLICATE
;
791 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
796 if (!js_AddNativeProperty(cx
, fun
->object
, (jsid
)argAtom
,
797 js_GetArgument
, js_SetArgument
,
799 JSPROP_ENUMERATE
| JSPROP_PERMANENT
|
801 SPROP_HAS_SHORTID
| dupflag
,
806 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
808 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FORMAL
);
811 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_BODY
);
812 pn
->pn_pos
.begin
= CURRENT_TOKEN(ts
).pos
.begin
;
814 TREE_CONTEXT_INIT(&funtc
);
815 body
= FunctionBody(cx
, ts
, fun
, &funtc
);
819 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
820 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
822 #if JS_HAS_LEXICAL_CLOSURE
824 * If we collected flags that indicate nested heavyweight functions, or
825 * this function contains heavyweight-making statements (references to
826 * __parent__ or __proto__; use of with, eval, import, or export; and
827 * assignment to arguments), flag the function as heavyweight (requiring
828 * a call object per invocation).
830 if (funtc
.flags
& TCF_FUN_HEAVYWEIGHT
) {
831 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
832 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
835 * If this function is a named statement function not at top-level
836 * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
837 * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
838 * enclosing function, if any, must be heavyweight.
840 if ((!lambda
&& funAtom
&& tc
->topStmt
) ||
841 (funtc
.flags
& TCF_FUN_USES_NONLOCALS
)) {
842 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
848 * Record names for function statements in tc->decls so we know when to
849 * avoid optimizing variable references that might name a function.
851 if (!lambda
&& funAtom
) {
852 ATOM_LIST_SEARCH(ale
, &tc
->decls
, funAtom
);
854 prevop
= ALE_JSOP(ale
);
855 if (JS_HAS_STRICT_OPTION(cx
) || prevop
== JSOP_DEFCONST
) {
856 const char *name
= js_AtomToPrintableString(cx
, funAtom
);
858 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
859 (prevop
!= JSOP_DEFCONST
)
863 JSMSG_REDECLARED_VAR
,
864 (prevop
== JSOP_DEFFUN
||
865 prevop
== JSOP_CLOSURE
)
867 : (prevop
== JSOP_DEFCONST
)
874 if (tc
->topStmt
&& prevop
== JSOP_DEFVAR
)
875 tc
->flags
|= TCF_FUN_CLOSURE_VS_VAR
;
877 ale
= js_IndexAtom(cx
, funAtom
, &tc
->decls
);
881 ALE_SET_JSOP(ale
, tc
->topStmt
? JSOP_CLOSURE
: JSOP_DEFFUN
);
883 #if JS_HAS_LEXICAL_CLOSURE
885 * A function nested at top level inside another's body needs only a
886 * local variable to bind its name to its value, and not an activation
887 * object property (it might also need the activation property, if the
888 * outer function contains with statements, e.g., but the stack slot
889 * wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a
890 * JSOP_GETVAR bytecode).
892 if (!tc
->topStmt
&& (tc
->flags
& TCF_IN_FUNCTION
)) {
897 * Define a property on the outer function so that LookupArgOrVar
898 * can properly optimize accesses.
900 * XXX Here and in Variables, we use the function object's scope,
901 * XXX arguably polluting it, when we could use a compiler-private
902 * XXX scope structure. Tradition!
906 JS_ASSERT(OBJ_GET_CLASS(cx
, varobj
) == &js_FunctionClass
);
907 JS_ASSERT(fp
->fun
== (JSFunction
*) JS_GetPrivate(cx
, varobj
));
908 if (!js_LookupProperty(cx
, varobj
, (jsid
)funAtom
, &pobj
, &prop
))
911 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
912 if (!prop
|| pobj
!= varobj
) {
913 if (!js_DefineNativeProperty(cx
, varobj
, (jsid
)funAtom
,
914 OBJECT_TO_JSVAL(fun
->object
),
918 SPROP_HAS_SHORTID
, fp
->fun
->nvars
,
928 #if JS_HAS_LEXICAL_CLOSURE
929 if (lambda
|| !funAtom
) {
931 * ECMA ed. 3 standard: function expression, possibly anonymous (even
932 * if at top-level, an unnamed function is an expression statement, not
933 * a function declaration).
935 op
= fun
->atom
? JSOP_NAMEDFUNOBJ
: JSOP_ANONFUNOBJ
;
936 } else if (tc
->topStmt
) {
938 * ECMA ed. 3 extension: a function expression statement not at the
939 * top level, e.g., in a compound statement such as the "then" part
940 * of an "if" statement, binds a closure only if control reaches that
949 * Pending a better automatic GC root management scheme (see Mozilla bug
950 * 40757, http://bugzilla.mozilla.org/show_bug.cgi?id=40757), we need to
951 * atomize here to protect against a GC activation.
953 pn
->pn_funAtom
= js_AtomizeObject(cx
, fun
->object
, 0);
959 pn
->pn_flags
= funtc
.flags
& TCF_FUN_FLAGS
;
960 pn
->pn_tryCount
= funtc
.tryCount
;
961 TREE_CONTEXT_FINISH(&funtc
);
966 FunctionStmt(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
968 return FunctionDef(cx
, ts
, tc
, JS_FALSE
);
971 #if JS_HAS_LEXICAL_CLOSURE
973 FunctionExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
975 return FunctionDef(cx
, ts
, tc
, JS_TRUE
);
980 * Parse the statements in a block, creating a TOK_LC node that lists the
981 * statements' trees. If called from block-parsing code, the caller must
982 * match { before and } after.
985 Statements(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
987 JSParseNode
*pn
, *pn2
;
992 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
997 ts
->flags
|= TSF_REGEXP
;
998 while ((tt
= js_PeekToken(cx
, ts
)) > TOK_EOF
&& tt
!= TOK_RC
) {
999 ts
->flags
&= ~TSF_REGEXP
;
1000 pn2
= Statement(cx
, ts
, tc
);
1003 ts
->flags
|= TSF_REGEXP
;
1005 /* If compiling top-level statements, emit as we go to save space. */
1006 if (!tc
->topStmt
&& (tc
->flags
& TCF_COMPILING
)) {
1008 JS_HAS_STRICT_OPTION(cx
) &&
1009 (tc
->flags
& TCF_RETURN_EXPR
)) {
1011 * Check pn2 for lack of a final return statement if it is the
1012 * last statement in the block.
1014 tt
= js_PeekToken(cx
, ts
);
1015 if ((tt
== TOK_EOF
|| tt
== TOK_RC
) &&
1016 !CheckFinalReturn(cx
, ts
, pn2
)) {
1022 * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1023 * CheckFinalReturn again.
1025 tc
->flags
&= ~TCF_RETURN_EXPR
;
1027 if (!js_FoldConstants(cx
, pn2
, tc
) ||
1028 !js_AllocTryNotes(cx
, (JSCodeGenerator
*)tc
) ||
1029 !js_EmitTree(cx
, (JSCodeGenerator
*)tc
, pn2
)) {
1033 RecycleTree(pn2
, tc
);
1038 ts
->flags
&= ~TSF_REGEXP
;
1039 if (tt
== TOK_ERROR
)
1042 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
1046 static JSParseNode
*
1047 Condition(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1049 JSParseNode
*pn
, *pn2
;
1051 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
1052 pn
= Expr(cx
, ts
, tc
);
1055 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
1058 * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1059 * greater precedence than ==.
1060 * XXX not ECMA, but documented in several books -- now a strict warning.
1062 if (pn
->pn_type
== TOK_ASSIGN
&&
1063 pn
->pn_op
== JSOP_NOP
&&
1064 pn
->pn_right
->pn_type
> TOK_EQOP
)
1066 JSBool rewrite
= !JSVERSION_IS_ECMA(cx
->version
);
1067 if (!js_ReportCompileErrorNumber(cx
, ts
, NULL
,
1068 JSREPORT_WARNING
| JSREPORT_STRICT
,
1069 JSMSG_EQUAL_AS_ASSIGN
,
1071 ? "\nAssuming equality test"
1076 pn
->pn_type
= TOK_EQOP
;
1077 pn
->pn_op
= (JSOp
)cx
->jsop_eq
;
1079 switch (pn2
->pn_op
) {
1081 pn2
->pn_op
= JSOP_NAME
;
1084 pn2
->pn_op
= JSOP_GETPROP
;
1087 pn2
->pn_op
= JSOP_GETELEM
;
1098 MatchLabel(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
)
1101 #if JS_HAS_LABEL_STATEMENT
1104 tt
= js_PeekTokenSameLine(cx
, ts
);
1105 if (tt
== TOK_ERROR
)
1107 if (tt
== TOK_NAME
) {
1108 (void) js_GetToken(cx
, ts
);
1109 label
= CURRENT_TOKEN(ts
).t_atom
;
1116 pn
->pn_atom
= label
;
1120 #if JS_HAS_EXPORT_IMPORT
1121 static JSParseNode
*
1122 ImportExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1124 JSParseNode
*pn
, *pn2
, *pn3
;
1127 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NO_IMPORT_NAME
);
1128 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NAME
, tc
);
1131 pn
->pn_op
= JSOP_NAME
;
1132 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
1137 ts
->flags
|= TSF_REGEXP
;
1138 while ((tt
= js_GetToken(cx
, ts
)) == TOK_DOT
|| tt
== TOK_LB
) {
1139 ts
->flags
&= ~TSF_REGEXP
;
1140 if (pn
->pn_op
== JSOP_IMPORTALL
)
1143 if (tt
== TOK_DOT
) {
1144 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NAME
, tc
);
1147 if (js_MatchToken(cx
, ts
, TOK_STAR
)) {
1148 pn2
->pn_op
= JSOP_IMPORTALL
;
1149 pn2
->pn_atom
= NULL
;
1151 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NAME_AFTER_DOT
);
1152 pn2
->pn_op
= JSOP_GETPROP
;
1153 pn2
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
1158 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
1159 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
1161 /* Make a TOK_LB node. */
1162 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_BINARY
, tc
);
1165 pn3
= Expr(cx
, ts
, tc
);
1169 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
1170 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
1171 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
1173 pn2
->pn_op
= JSOP_GETELEM
;
1175 pn2
->pn_right
= pn3
;
1179 ts
->flags
|= TSF_REGEXP
;
1181 ts
->flags
&= ~TSF_REGEXP
;
1182 if (tt
== TOK_ERROR
)
1186 switch (pn
->pn_op
) {
1188 pn
->pn_op
= JSOP_IMPORTPROP
;
1191 pn
->pn_op
= JSOP_IMPORTELEM
;
1193 case JSOP_IMPORTALL
:
1201 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
, JSMSG_BAD_IMPORT
);
1204 #endif /* JS_HAS_EXPORT_IMPORT */
1206 extern const char js_with_statement_str
[];
1208 static JSParseNode
*
1209 Statement(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1212 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
, *pn4
;
1213 JSStmtInfo stmtInfo
, *stmt
, *stmt2
;
1218 ts
->flags
|= TSF_REGEXP
;
1219 tt
= js_GetToken(cx
, ts
);
1220 ts
->flags
&= ~TSF_REGEXP
;
1222 #if JS_HAS_GETTER_SETTER
1223 if (tt
== TOK_NAME
) {
1224 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
1225 if (tt
== TOK_ERROR
)
1231 #if JS_HAS_EXPORT_IMPORT
1233 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
1237 if (js_MatchToken(cx
, ts
, TOK_STAR
)) {
1238 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
1244 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NO_EXPORT_NAME
);
1245 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NAME
, tc
);
1248 pn2
->pn_op
= JSOP_NAME
;
1249 pn2
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
1250 pn2
->pn_expr
= NULL
;
1254 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
1256 pn
->pn_pos
.end
= PN_LAST(pn
)->pn_pos
.end
;
1257 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1261 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
1266 pn2
= ImportExpr(cx
, ts
, tc
);
1270 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
1271 pn
->pn_pos
.end
= PN_LAST(pn
)->pn_pos
.end
;
1272 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1274 #endif /* JS_HAS_EXPORT_IMPORT */
1277 return FunctionStmt(cx
, ts
, tc
);
1280 /* An IF node has three kids: condition, then, and optional else. */
1281 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_TERNARY
, tc
);
1284 pn1
= Condition(cx
, ts
, tc
);
1287 js_PushStatement(tc
, &stmtInfo
, STMT_IF
, -1);
1288 pn2
= Statement(cx
, ts
, tc
);
1291 if (js_MatchToken(cx
, ts
, TOK_ELSE
)) {
1292 stmtInfo
.type
= STMT_ELSE
;
1293 pn3
= Statement(cx
, ts
, tc
);
1296 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
1299 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
1301 js_PopStatement(tc
);
1307 #if JS_HAS_SWITCH_STATEMENT
1311 JSBool seenDefault
= JS_FALSE
;
1313 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_BINARY
, tc
);
1316 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_SWITCH
);
1318 /* pn1 points to the switch's discriminant. */
1319 pn1
= Expr(cx
, ts
, tc
);
1323 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_SWITCH
);
1324 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_SWITCH
);
1326 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
1327 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
1332 js_PushStatement(tc
, &stmtInfo
, STMT_SWITCH
, -1);
1334 while ((tt
= js_GetToken(cx
, ts
)) != TOK_RC
) {
1338 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1339 JSMSG_TOO_MANY_DEFAULTS
);
1342 seenDefault
= JS_TRUE
;
1346 pn3
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_BINARY
, tc
);
1349 if (tt
== TOK_DEFAULT
) {
1350 pn3
->pn_left
= NULL
;
1352 pn3
->pn_left
= Expr(cx
, ts
, tc
);
1356 PN_APPEND(pn2
, pn3
);
1357 if (pn2
->pn_count
== JS_BIT(16)) {
1358 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1359 JSMSG_TOO_MANY_CASES
);
1368 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1372 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_AFTER_CASE
);
1374 pn4
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
1377 pn4
->pn_type
= TOK_LC
;
1379 while ((tt
= js_PeekToken(cx
, ts
)) != TOK_RC
&&
1380 tt
!= TOK_CASE
&& tt
!= TOK_DEFAULT
) {
1381 if (tt
== TOK_ERROR
)
1383 pn5
= Statement(cx
, ts
, tc
);
1386 pn4
->pn_pos
.end
= pn5
->pn_pos
.end
;
1387 PN_APPEND(pn4
, pn5
);
1390 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1392 pn4
->pn_pos
.begin
= pn4
->pn_head
->pn_pos
.begin
;
1393 pn3
->pn_pos
.end
= pn4
->pn_pos
.end
;
1394 pn3
->pn_right
= pn4
;
1397 js_PopStatement(tc
);
1399 pn
->pn_pos
.end
= pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
1404 #endif /* JS_HAS_SWITCH_STATEMENT */
1407 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_BINARY
, tc
);
1410 js_PushStatement(tc
, &stmtInfo
, STMT_WHILE_LOOP
, -1);
1411 pn2
= Condition(cx
, ts
, tc
);
1415 pn2
= Statement(cx
, ts
, tc
);
1418 js_PopStatement(tc
);
1419 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
1423 #if JS_HAS_DO_WHILE_LOOP
1425 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_BINARY
, tc
);
1428 js_PushStatement(tc
, &stmtInfo
, STMT_DO_LOOP
, -1);
1429 pn2
= Statement(cx
, ts
, tc
);
1433 MUST_MATCH_TOKEN(TOK_WHILE
, JSMSG_WHILE_AFTER_DO
);
1434 pn2
= Condition(cx
, ts
, tc
);
1437 js_PopStatement(tc
);
1438 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
1440 if (cx
->version
!= JSVERSION_ECMA_3
) {
1442 * All legacy and extended versions must do automatic semicolon
1443 * insertion after do-while. See the testcase and discussion in
1444 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
1446 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
1450 #endif /* JS_HAS_DO_WHILE_LOOP */
1453 /* A FOR node is binary, left is loop control and right is the body. */
1454 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_BINARY
, tc
);
1457 js_PushStatement(tc
, &stmtInfo
, STMT_FOR_LOOP
, -1);
1459 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
1460 ts
->flags
|= TSF_REGEXP
;
1461 tt
= js_PeekToken(cx
, ts
);
1462 ts
->flags
&= ~TSF_REGEXP
;
1463 if (tt
== TOK_SEMI
) {
1464 /* No initializer -- set first kid of left sub-node to null. */
1467 /* Set pn1 to a var list or an initializing expression. */
1468 #if JS_HAS_IN_OPERATOR
1470 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
1471 * of the for statement. This flag will be used by the RelExpr
1472 * production; if it is set, then the 'in' keyword will not be
1473 * recognized as an operator, leaving it available to be parsed as
1474 * part of a for/in loop. A side effect of this restriction is
1475 * that (unparenthesized) expressions involving an 'in' operator
1476 * are illegal in the init clause of an ordinary for loop.
1478 tc
->flags
|= TCF_IN_FOR_INIT
;
1479 #endif /* JS_HAS_IN_OPERATOR */
1480 if (tt
== TOK_VAR
) {
1481 (void) js_GetToken(cx
, ts
);
1482 pn1
= Variables(cx
, ts
, tc
);
1484 pn1
= Expr(cx
, ts
, tc
);
1486 #if JS_HAS_IN_OPERATOR
1487 tc
->flags
&= ~TCF_IN_FOR_INIT
;
1488 #endif /* JS_HAS_IN_OPERATOR */
1494 * We can be sure that it's a for/in loop if there's still an 'in'
1495 * keyword here, even if JavaScript recognizes 'in' as an operator,
1496 * as we've excluded 'in' from being parsed in RelExpr by setting
1497 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
1499 if (pn1
&& js_MatchToken(cx
, ts
, TOK_IN
)) {
1500 stmtInfo
.type
= STMT_FOR_IN_LOOP
;
1502 /* Check that the left side of the 'in' is valid. */
1503 if ((pn1
->pn_type
== TOK_VAR
)
1504 ? (pn1
->pn_count
> 1 || pn1
->pn_op
== JSOP_DEFCONST
)
1505 : (pn1
->pn_type
!= TOK_NAME
&&
1506 pn1
->pn_type
!= TOK_DOT
&&
1507 pn1
->pn_type
!= TOK_LB
)) {
1508 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1509 JSMSG_BAD_FOR_LEFTSIDE
);
1513 if (pn1
->pn_type
== TOK_VAR
) {
1514 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
1515 pn1
->pn_extra
|= PNX_FORINVAR
;
1517 /* Generate a final POP only if the var has an initializer. */
1520 pn1
->pn_extra
|= PNX_POPVAR
;
1525 /* Beware 'for (arguments in ...)' with or without a 'var'. */
1526 if (pn2
->pn_type
== TOK_NAME
&&
1527 pn2
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
) {
1528 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1531 /* Parse the object expression as the right operand of 'in'. */
1532 pn2
= NewBinary(cx
, TOK_IN
, JSOP_NOP
, pn1
, Expr(cx
, ts
, tc
), tc
);
1537 /* Parse the loop condition or null into pn2. */
1538 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
1539 ts
->flags
|= TSF_REGEXP
;
1540 tt
= js_PeekToken(cx
, ts
);
1541 ts
->flags
&= ~TSF_REGEXP
;
1542 if (tt
== TOK_SEMI
) {
1545 pn2
= Expr(cx
, ts
, tc
);
1550 /* Parse the update expression or null into pn3. */
1551 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
1552 ts
->flags
|= TSF_REGEXP
;
1553 tt
= js_PeekToken(cx
, ts
);
1554 ts
->flags
&= ~TSF_REGEXP
;
1558 pn3
= Expr(cx
, ts
, tc
);
1563 /* Build the RESERVED node to use as the left kid of pn. */
1564 pn4
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_TERNARY
, tc
);
1567 pn4
->pn_type
= TOK_RESERVED
;
1568 pn4
->pn_op
= JSOP_NOP
;
1575 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
1577 /* Parse the loop body into pn->pn_right. */
1578 pn2
= Statement(cx
, ts
, tc
);
1582 js_PopStatement(tc
);
1584 /* Record the absolute line number for source note emission. */
1585 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
1588 #if JS_HAS_EXCEPTIONS
1590 JSParseNode
*catchtail
= NULL
;
1592 * try nodes are ternary.
1593 * kid1 is the try Statement
1594 * kid2 is the catch node
1595 * kid3 is the finally Statement
1597 * catch nodes are ternary.
1598 * kid1 is the discriminant
1599 * kid2 is the next catch node, or NULL
1600 * kid3 is the catch block (on kid3 so that we can always append a
1601 * new catch pn on catchtail->kid2)
1603 * catch discriminant nodes are binary
1604 * atom is the receptacle
1605 * expr is the discriminant code
1607 * finally nodes are unary (just the finally expression)
1609 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_TERNARY
, tc
);
1610 pn
->pn_op
= JSOP_NOP
;
1612 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_TRY
);
1613 js_PushStatement(tc
, &stmtInfo
, STMT_TRY
, -1);
1614 pn
->pn_kid1
= Statements(cx
, ts
, tc
);
1617 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_TRY
);
1618 js_PopStatement(tc
);
1621 while (js_PeekToken(cx
, ts
) == TOK_CATCH
) {
1622 /* check for another catch after unconditional catch */
1623 if (catchtail
!= pn
&& !catchtail
->pn_kid1
->pn_expr
) {
1624 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1625 JSMSG_CATCH_AFTER_GENERAL
);
1630 * legal catch forms are:
1632 * catch (v if <boolean_expression>)
1633 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
1635 (void) js_GetToken(cx
, ts
); /* eat `catch' */
1636 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_TERNARY
, tc
);
1641 * We use a PN_NAME for the discriminant (catchguard) node
1642 * with the actual discriminant code in the initializer spot
1644 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_CATCH
);
1645 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_CATCH_IDENTIFIER
);
1646 pn3
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NAME
, tc
);
1650 pn3
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
1651 pn3
->pn_expr
= NULL
;
1652 #if JS_HAS_CATCH_GUARD
1654 * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to
1655 * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.
1657 if (js_PeekToken(cx
, ts
) == TOK_IF
) {
1658 (void)js_GetToken(cx
, ts
); /* eat `if' */
1659 pn3
->pn_expr
= Expr(cx
, ts
, tc
);
1666 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_CATCH
);
1668 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_CATCH
);
1669 js_PushStatement(tc
, &stmtInfo
, STMT_CATCH
, -1);
1670 stmtInfo
.label
= pn3
->pn_atom
;
1671 pn2
->pn_kid3
= Statements(cx
, ts
, tc
);
1674 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_CATCH
);
1675 js_PopStatement(tc
);
1677 catchtail
= catchtail
->pn_kid2
= pn2
;
1679 catchtail
->pn_kid2
= NULL
;
1681 if (js_MatchToken(cx
, ts
, TOK_FINALLY
)) {
1683 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_FINALLY
);
1684 js_PushStatement(tc
, &stmtInfo
, STMT_FINALLY
, -1);
1685 pn
->pn_kid3
= Statements(cx
, ts
, tc
);
1688 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_FINALLY
);
1689 js_PopStatement(tc
);
1693 if (!pn
->pn_kid2
&& !pn
->pn_kid3
) {
1694 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1695 JSMSG_CATCH_OR_FINALLY
);
1703 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
1706 pn2
= Expr(cx
, ts
, tc
);
1709 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
1710 pn
->pn_op
= JSOP_THROW
;
1714 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
1716 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1717 JSMSG_CATCH_WITHOUT_TRY
);
1721 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1722 JSMSG_FINALLY_WITHOUT_TRY
);
1725 #endif /* JS_HAS_EXCEPTIONS */
1728 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
1731 if (!MatchLabel(cx
, ts
, pn
))
1734 label
= pn
->pn_atom
;
1736 for (; ; stmt
= stmt
->down
) {
1738 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1739 JSMSG_LABEL_NOT_FOUND
);
1742 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
)
1746 for (; ; stmt
= stmt
->down
) {
1748 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1752 if (STMT_IS_LOOP(stmt
) || stmt
->type
== STMT_SWITCH
)
1757 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
1761 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
1764 if (!MatchLabel(cx
, ts
, pn
))
1767 label
= pn
->pn_atom
;
1769 for (stmt2
= NULL
; ; stmt
= stmt
->down
) {
1771 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1772 JSMSG_LABEL_NOT_FOUND
);
1775 if (stmt
->type
== STMT_LABEL
) {
1776 if (stmt
->label
== label
) {
1777 if (!stmt2
|| !STMT_IS_LOOP(stmt2
)) {
1778 js_ReportCompileErrorNumber(cx
, ts
, NULL
,
1780 JSMSG_BAD_CONTINUE
);
1790 for (; ; stmt
= stmt
->down
) {
1792 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1793 JSMSG_BAD_CONTINUE
);
1796 if (STMT_IS_LOOP(stmt
))
1801 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
1805 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_BINARY
, tc
);
1808 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_WITH
);
1809 pn2
= Expr(cx
, ts
, tc
);
1812 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_WITH
);
1815 js_PushStatement(tc
, &stmtInfo
, STMT_WITH
, -1);
1816 pn2
= Statement(cx
, ts
, tc
);
1819 js_PopStatement(tc
);
1821 /* Deprecate after parsing, in case of WERROR option. */
1822 if (!js_ReportCompileErrorNumber(cx
, ts
, NULL
,
1823 JSREPORT_WARNING
| JSREPORT_STRICT
,
1824 JSMSG_DEPRECATED_USAGE
,
1825 js_with_statement_str
)) {
1829 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
1831 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1835 pn
= Variables(cx
, ts
, tc
);
1839 /* Tell js_EmitTree to generate a final POP. */
1840 pn
->pn_extra
|= PNX_POPVAR
;
1844 if (!(tc
->flags
& TCF_IN_FUNCTION
)) {
1845 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1849 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
1853 /* This is ugly, but we don't want to require a semicolon. */
1854 ts
->flags
|= TSF_REGEXP
;
1855 tt
= js_PeekTokenSameLine(cx
, ts
);
1856 ts
->flags
&= ~TSF_REGEXP
;
1857 if (tt
== TOK_ERROR
)
1860 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
1861 pn2
= Expr(cx
, ts
, tc
);
1864 tc
->flags
|= TCF_RETURN_EXPR
;
1865 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
1868 tc
->flags
|= TCF_RETURN_VOID
;
1872 if (JS_HAS_STRICT_OPTION(cx
) &&
1873 (~tc
->flags
& (TCF_RETURN_EXPR
| TCF_RETURN_VOID
)) == 0) {
1875 * We must be in a frame with a non-native function, because
1876 * we're compiling one.
1878 if (!ReportNoReturnValue(cx
, ts
))
1884 js_PushStatement(tc
, &stmtInfo
, STMT_BLOCK
, -1);
1885 pn
= Statements(cx
, ts
, tc
);
1889 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_COMPOUND
);
1890 js_PopStatement(tc
);
1895 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
1898 pn
->pn_type
= TOK_SEMI
;
1902 #if JS_HAS_DEBUGGER_KEYWORD
1904 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
1907 pn
->pn_type
= TOK_DEBUGGER
;
1908 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1910 #endif /* JS_HAS_DEBUGGER_KEYWORD */
1917 pn2
= Expr(cx
, ts
, tc
);
1921 if (js_PeekToken(cx
, ts
) == TOK_COLON
) {
1922 if (pn2
->pn_type
!= TOK_NAME
) {
1923 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1927 label
= pn2
->pn_atom
;
1928 for (stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
1929 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
) {
1930 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1931 JSMSG_DUPLICATE_LABEL
);
1935 (void) js_GetToken(cx
, ts
);
1937 /* Push a label struct and parse the statement. */
1938 js_PushStatement(tc
, &stmtInfo
, STMT_LABEL
, -1);
1939 stmtInfo
.label
= label
;
1940 pn
= Statement(cx
, ts
, tc
);
1944 /* Pop the label, set pn_expr, and return early. */
1945 js_PopStatement(tc
);
1946 pn2
->pn_type
= TOK_COLON
;
1947 pn2
->pn_pos
.end
= pn
->pn_pos
.end
;
1952 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
1955 pn
->pn_type
= TOK_SEMI
;
1956 pn
->pn_pos
= pn2
->pn_pos
;
1961 /* Check termination of this primitive statement. */
1962 if (ON_CURRENT_LINE(ts
, pn
->pn_pos
)) {
1963 tt
= js_PeekTokenSameLine(cx
, ts
);
1964 if (tt
== TOK_ERROR
)
1966 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
1967 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1968 JSMSG_SEMI_BEFORE_STMNT
);
1973 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
1977 static JSParseNode
*
1978 Variables(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1980 JSParseNode
*pn
, *pn2
;
1981 JSObject
*obj
, *pobj
;
1985 JSPropertyOp getter
, setter
, currentGetter
, currentSetter
;
1987 JSAtomListElement
*ale
;
1990 JSScopeProperty
*sprop
;
1994 * The tricky part of this code is to create special parsenode opcodes for
1995 * getting and setting variables (which will be stored as special slots in
1996 * the frame). The complex special case is an eval() inside a function.
1997 * If the evaluated string references variables in the enclosing function,
1998 * then we need to generate the special variable opcodes. We determine
1999 * this by looking up the variable id in the current variable scope.
2001 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_VAR
);
2002 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
2005 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
2006 pn
->pn_extra
= 0; /* assume no JSOP_POP needed */
2010 * Skip eval and debugger frames when looking for the function whose code
2011 * is being compiled. If we are called from FunctionBody, TCF_IN_FUNCTION
2012 * will be set in tc->flags, and we can be sure fp->fun is the function to
2013 * use. But if a function calls eval, the string argument is treated as a
2014 * Program (per ECMA), so TCF_IN_FUNCTION won't be set.
2016 * What's more, when the following code is reached from eval, cx->fp->fun
2017 * is eval's JSFunction (a native function), so we need to skip its frame.
2018 * We should find the scripted caller's function frame just below it, but
2019 * we code a loop out of paranoia.
2021 for (fp
= cx
->fp
; (fp
->flags
& JSFRAME_SPECIAL
) && fp
->down
; fp
= fp
->down
)
2025 clasp
= OBJ_GET_CLASS(cx
, obj
);
2026 if (fun
&& clasp
== &js_FunctionClass
) {
2027 /* We are compiling code inside a function */
2028 getter
= js_GetLocalVariable
;
2029 setter
= js_SetLocalVariable
;
2030 } else if (fun
&& clasp
== &js_CallClass
) {
2031 /* We are compiling code from an eval inside a function */
2032 getter
= js_GetCallVariable
;
2033 setter
= js_SetCallVariable
;
2035 getter
= clasp
->getProperty
;
2036 setter
= clasp
->setProperty
;
2041 currentGetter
= getter
;
2042 currentSetter
= setter
;
2043 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NO_VARIABLE_NAME
);
2044 atom
= CURRENT_TOKEN(ts
).t_atom
;
2046 ATOM_LIST_SEARCH(ale
, &tc
->decls
, atom
);
2048 prevop
= ALE_JSOP(ale
);
2049 if (JS_HAS_STRICT_OPTION(cx
) ||
2050 pn
->pn_op
== JSOP_DEFCONST
||
2051 prevop
== JSOP_DEFCONST
) {
2052 const char *name
= js_AtomToPrintableString(cx
, atom
);
2054 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2055 (pn
->pn_op
!= JSOP_DEFCONST
&&
2056 prevop
!= JSOP_DEFCONST
)
2057 ? JSREPORT_WARNING
|
2060 JSMSG_REDECLARED_VAR
,
2061 (prevop
== JSOP_DEFFUN
||
2062 prevop
== JSOP_CLOSURE
)
2064 : (prevop
== JSOP_DEFCONST
)
2071 if (pn
->pn_op
== JSOP_DEFVAR
&& prevop
== JSOP_CLOSURE
)
2072 tc
->flags
|= TCF_FUN_CLOSURE_VS_VAR
;
2074 ale
= js_IndexAtom(cx
, atom
, &tc
->decls
);
2078 ALE_SET_JSOP(ale
, pn
->pn_op
);
2080 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NAME
, tc
);
2083 pn2
->pn_op
= JSOP_NAME
;
2084 pn2
->pn_atom
= atom
;
2085 pn2
->pn_expr
= NULL
;
2087 pn2
->pn_attrs
= (pn
->pn_op
== JSOP_DEFCONST
)
2088 ? JSPROP_ENUMERATE
| JSPROP_PERMANENT
|
2090 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
2094 prop
= NULL
; /* don't lookup global variables at compile time */
2096 if (!OBJ_LOOKUP_PROPERTY(cx
, obj
, (jsid
)atom
, &pobj
, &prop
))
2099 if (prop
&& pobj
== obj
&& OBJ_IS_NATIVE(pobj
)) {
2100 sprop
= (JSScopeProperty
*)prop
;
2101 if (sprop
->getter
== js_GetArgument
) {
2102 const char *name
= js_AtomToPrintableString(cx
, atom
);
2105 } else if (pn
->pn_op
== JSOP_DEFCONST
) {
2106 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2107 JSMSG_REDECLARED_PARAM
,
2111 currentGetter
= js_GetArgument
;
2112 currentSetter
= js_SetArgument
;
2113 ok
= js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2116 JSMSG_VAR_HIDES_ARG
,
2121 /* Not an argument, must be a redeclared local var. */
2122 if (clasp
== &js_FunctionClass
) {
2123 JS_ASSERT(sprop
->getter
== js_GetLocalVariable
);
2124 JS_ASSERT((sprop
->flags
& SPROP_HAS_SHORTID
) &&
2125 sprop
->shortid
< fun
->nvars
);
2126 } else if (clasp
== &js_CallClass
) {
2127 if (sprop
->getter
== js_GetCallVariable
) {
2129 * Referencing a variable introduced by a var
2130 * statement in the enclosing function. Check
2131 * that the slot number we have is in range.
2133 JS_ASSERT((sprop
->flags
& SPROP_HAS_SHORTID
) &&
2134 sprop
->shortid
< fun
->nvars
);
2137 * A variable introduced through another eval:
2138 * don't use the special getters and setters
2139 * since we can't allocate a slot in the frame.
2141 currentGetter
= sprop
->getter
;
2142 currentSetter
= sprop
->setter
;
2146 /* Override the old getter and setter, to handle eval. */
2147 sprop
= js_ChangeNativePropertyAttrs(cx
, obj
, sprop
,
2157 * Property not found in current variable scope: we have not seen
2158 * this variable before. Define a new local variable by adding a
2159 * property to the function's scope, allocating one slot in the
2160 * function's frame. Global variables and any locals declared in
2161 * with statement bodies are handled at runtime, by script prolog
2162 * JSOP_DEFVAR bytecodes generated for slot-less vars.
2166 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
2169 if (currentGetter
== js_GetCallVariable
) {
2170 /* Can't increase fun->nvars in an active frame! */
2171 currentGetter
= clasp
->getProperty
;
2172 currentSetter
= clasp
->setProperty
;
2174 if (currentGetter
== js_GetLocalVariable
&&
2175 atom
!= cx
->runtime
->atomState
.argumentsAtom
&&
2176 fp
->scopeChain
== obj
&&
2177 !js_InWithStatement(tc
)) {
2178 if (!js_AddNativeProperty(cx
, obj
, (jsid
)atom
,
2179 currentGetter
, currentSetter
,
2181 pn2
->pn_attrs
| JSPROP_SHARED
,
2182 SPROP_HAS_SHORTID
, fun
->nvars
)) {
2189 if (js_MatchToken(cx
, ts
, TOK_ASSIGN
)) {
2190 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
2191 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2192 JSMSG_BAD_VAR_INIT
);
2195 pn2
->pn_expr
= AssignExpr(cx
, ts
, tc
);
2196 if (!pn2
->pn_expr
) {
2199 pn2
->pn_op
= (pn
->pn_op
== JSOP_DEFCONST
)
2202 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
2203 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2209 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
2212 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
2214 pn
->pn_pos
.end
= PN_LAST(pn
)->pn_pos
.end
;
2218 static JSParseNode
*
2219 Expr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2221 JSParseNode
*pn
, *pn2
;
2223 pn
= AssignExpr(cx
, ts
, tc
);
2224 if (pn
&& js_MatchToken(cx
, ts
, TOK_COMMA
)) {
2225 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
2228 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2229 PN_INIT_LIST_1(pn2
, pn
);
2232 pn2
= AssignExpr(cx
, ts
, tc
);
2236 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
2237 pn
->pn_pos
.end
= PN_LAST(pn
)->pn_pos
.end
;
2242 static JSParseNode
*
2243 AssignExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2245 JSParseNode
*pn
, *pn2
;
2251 pn
= CondExpr(cx
, ts
, tc
);
2255 tt
= js_GetToken(cx
, ts
);
2256 #if JS_HAS_GETTER_SETTER
2257 if (tt
== TOK_NAME
) {
2258 tt
= CheckGetterOrSetter(cx
, ts
, TOK_ASSIGN
);
2259 if (tt
== TOK_ERROR
)
2263 if (tt
!= TOK_ASSIGN
) {
2268 op
= CURRENT_TOKEN(ts
).t_op
;
2269 for (pn2
= pn
; pn2
->pn_type
== TOK_RP
; pn2
= pn2
->pn_kid
)
2271 switch (pn2
->pn_type
) {
2273 pn2
->pn_op
= JSOP_SETNAME
;
2274 if (pn2
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
2275 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2278 pn2
->pn_op
= JSOP_SETPROP
;
2281 pn2
->pn_op
= JSOP_SETELEM
;
2283 #if JS_HAS_LVALUE_RETURN
2285 pn2
->pn_op
= JSOP_SETCALL
;
2289 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2290 JSMSG_BAD_LEFTSIDE_OF_ASS
);
2293 pn
= NewBinary(cx
, TOK_ASSIGN
, op
, pn2
, AssignExpr(cx
, ts
, tc
), tc
);
2297 static JSParseNode
*
2298 CondExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2300 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
;
2301 #if JS_HAS_IN_OPERATOR
2303 #endif /* JS_HAS_IN_OPERATOR */
2305 pn
= OrExpr(cx
, ts
, tc
);
2306 if (pn
&& js_MatchToken(cx
, ts
, TOK_HOOK
)) {
2308 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_TERNARY
, tc
);
2311 #if JS_HAS_IN_OPERATOR
2313 * Always accept the 'in' operator in the middle clause of a ternary,
2314 * where it's unambiguous, even if we might be parsing the init of a
2317 oldflags
= tc
->flags
;
2318 tc
->flags
&= ~TCF_IN_FOR_INIT
;
2319 #endif /* JS_HAS_IN_OPERATOR */
2320 pn2
= AssignExpr(cx
, ts
, tc
);
2321 #if JS_HAS_IN_OPERATOR
2322 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
2323 #endif /* JS_HAS_IN_OPERATOR */
2327 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_IN_COND
);
2328 pn3
= AssignExpr(cx
, ts
, tc
);
2331 pn
->pn_pos
.begin
= pn1
->pn_pos
.begin
;
2332 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
2340 static JSParseNode
*
2341 OrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2345 pn
= AndExpr(cx
, ts
, tc
);
2346 if (pn
&& js_MatchToken(cx
, ts
, TOK_OR
))
2347 pn
= NewBinary(cx
, TOK_OR
, JSOP_OR
, pn
, OrExpr(cx
, ts
, tc
), tc
);
2351 static JSParseNode
*
2352 AndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2356 pn
= BitOrExpr(cx
, ts
, tc
);
2357 if (pn
&& js_MatchToken(cx
, ts
, TOK_AND
))
2358 pn
= NewBinary(cx
, TOK_AND
, JSOP_AND
, pn
, AndExpr(cx
, ts
, tc
), tc
);
2362 static JSParseNode
*
2363 BitOrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2367 pn
= BitXorExpr(cx
, ts
, tc
);
2368 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITOR
)) {
2369 pn
= NewBinary(cx
, TOK_BITOR
, JSOP_BITOR
, pn
, BitXorExpr(cx
, ts
, tc
),
2375 static JSParseNode
*
2376 BitXorExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2380 pn
= BitAndExpr(cx
, ts
, tc
);
2381 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITXOR
)) {
2382 pn
= NewBinary(cx
, TOK_BITXOR
, JSOP_BITXOR
, pn
, BitAndExpr(cx
, ts
, tc
),
2388 static JSParseNode
*
2389 BitAndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2393 pn
= EqExpr(cx
, ts
, tc
);
2394 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITAND
))
2395 pn
= NewBinary(cx
, TOK_BITAND
, JSOP_BITAND
, pn
, EqExpr(cx
, ts
, tc
), tc
);
2399 static JSParseNode
*
2400 EqExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2405 pn
= RelExpr(cx
, ts
, tc
);
2406 while (pn
&& js_MatchToken(cx
, ts
, TOK_EQOP
)) {
2407 op
= CURRENT_TOKEN(ts
).t_op
;
2408 pn
= NewBinary(cx
, TOK_EQOP
, op
, pn
, RelExpr(cx
, ts
, tc
), tc
);
2413 static JSParseNode
*
2414 RelExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2419 #if JS_HAS_IN_OPERATOR
2420 uintN inForInitFlag
= tc
->flags
& TCF_IN_FOR_INIT
;
2423 * Uses of the in operator in ShiftExprs are always unambiguous,
2424 * so unset the flag that prohibits recognizing it.
2426 tc
->flags
&= ~TCF_IN_FOR_INIT
;
2427 #endif /* JS_HAS_IN_OPERATOR */
2429 pn
= ShiftExpr(cx
, ts
, tc
);
2431 (js_MatchToken(cx
, ts
, TOK_RELOP
)
2432 #if JS_HAS_IN_OPERATOR
2434 * Recognize the 'in' token as an operator only if we're not
2435 * currently in the init expr of a for loop.
2437 || (inForInitFlag
== 0 && js_MatchToken(cx
, ts
, TOK_IN
))
2438 #endif /* JS_HAS_IN_OPERATOR */
2439 #if JS_HAS_INSTANCEOF
2440 || js_MatchToken(cx
, ts
, TOK_INSTANCEOF
)
2441 #endif /* JS_HAS_INSTANCEOF */
2443 tt
= CURRENT_TOKEN(ts
).type
;
2444 op
= CURRENT_TOKEN(ts
).t_op
;
2445 pn
= NewBinary(cx
, tt
, op
, pn
, ShiftExpr(cx
, ts
, tc
), tc
);
2447 #if JS_HAS_IN_OPERATOR
2448 /* Restore previous state of inForInit flag. */
2449 tc
->flags
|= inForInitFlag
;
2450 #endif /* JS_HAS_IN_OPERATOR */
2455 static JSParseNode
*
2456 ShiftExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2461 pn
= AddExpr(cx
, ts
, tc
);
2462 while (pn
&& js_MatchToken(cx
, ts
, TOK_SHOP
)) {
2463 op
= CURRENT_TOKEN(ts
).t_op
;
2464 pn
= NewBinary(cx
, TOK_SHOP
, op
, pn
, AddExpr(cx
, ts
, tc
), tc
);
2469 static JSParseNode
*
2470 AddExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2476 pn
= MulExpr(cx
, ts
, tc
);
2478 (js_MatchToken(cx
, ts
, TOK_PLUS
) ||
2479 js_MatchToken(cx
, ts
, TOK_MINUS
))) {
2480 tt
= CURRENT_TOKEN(ts
).type
;
2481 op
= (tt
== TOK_PLUS
) ? JSOP_ADD
: JSOP_SUB
;
2482 pn
= NewBinary(cx
, tt
, op
, pn
, MulExpr(cx
, ts
, tc
), tc
);
2487 static JSParseNode
*
2488 MulExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2494 pn
= UnaryExpr(cx
, ts
, tc
);
2496 (js_MatchToken(cx
, ts
, TOK_STAR
) ||
2497 js_MatchToken(cx
, ts
, TOK_DIVOP
))) {
2498 tt
= CURRENT_TOKEN(ts
).type
;
2499 op
= CURRENT_TOKEN(ts
).t_op
;
2500 pn
= NewBinary(cx
, tt
, op
, pn
, UnaryExpr(cx
, ts
, tc
), tc
);
2505 static JSParseNode
*
2506 SetLvalKid(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
, JSParseNode
*kid
,
2509 while (kid
->pn_type
== TOK_RP
)
2511 if (kid
->pn_type
!= TOK_NAME
&&
2512 kid
->pn_type
!= TOK_DOT
&&
2513 #if JS_HAS_LVALUE_RETURN
2514 (kid
->pn_type
!= TOK_LP
|| kid
->pn_op
!= JSOP_CALL
) &&
2516 kid
->pn_type
!= TOK_LB
) {
2517 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2518 JSMSG_BAD_OPERAND
, name
);
2525 static const char *incop_name_str
[] = {"increment", "decrement"};
2528 SetIncOpKid(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2529 JSParseNode
*pn
, JSParseNode
*kid
,
2530 JSTokenType tt
, JSBool preorder
)
2534 kid
= SetLvalKid(cx
, ts
, pn
, kid
, incop_name_str
[tt
== TOK_DEC
]);
2537 switch (kid
->pn_type
) {
2539 op
= (tt
== TOK_INC
)
2540 ? (preorder
? JSOP_INCNAME
: JSOP_NAMEINC
)
2541 : (preorder
? JSOP_DECNAME
: JSOP_NAMEDEC
);
2542 if (kid
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
2543 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2547 op
= (tt
== TOK_INC
)
2548 ? (preorder
? JSOP_INCPROP
: JSOP_PROPINC
)
2549 : (preorder
? JSOP_DECPROP
: JSOP_PROPDEC
);
2552 #if JS_HAS_LVALUE_RETURN
2554 kid
->pn_op
= JSOP_SETCALL
;
2557 op
= (tt
== TOK_INC
)
2558 ? (preorder
? JSOP_INCELEM
: JSOP_ELEMINC
)
2559 : (preorder
? JSOP_DECELEM
: JSOP_ELEMDEC
);
2570 static JSParseNode
*
2571 UnaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2574 JSParseNode
*pn
, *pn2
;
2576 ts
->flags
|= TSF_REGEXP
;
2577 tt
= js_GetToken(cx
, ts
);
2578 ts
->flags
&= ~TSF_REGEXP
;
2584 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
2587 pn
->pn_type
= TOK_UNARYOP
; /* PLUS and MINUS are binary */
2588 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
2589 pn2
= UnaryExpr(cx
, ts
, tc
);
2592 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
2598 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
2601 pn2
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
2604 if (!SetIncOpKid(cx
, ts
, tc
, pn
, pn2
, tt
, JS_TRUE
))
2606 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
2610 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
2613 pn2
= UnaryExpr(cx
, ts
, tc
);
2616 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
2619 * Under ECMA3, deleting any unary expression is valid -- it simply
2620 * returns true. Here we strip off any parentheses.
2622 while (pn2
->pn_type
== TOK_RP
)
2632 pn
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
2636 /* Don't look across a newline boundary for a postfix incop. */
2637 if (ON_CURRENT_LINE(ts
, pn
->pn_pos
)) {
2638 tt
= js_PeekTokenSameLine(cx
, ts
);
2639 if (tt
== TOK_INC
|| tt
== TOK_DEC
) {
2640 (void) js_GetToken(cx
, ts
);
2641 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
2644 if (!SetIncOpKid(cx
, ts
, tc
, pn2
, pn
, tt
, JS_FALSE
))
2646 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2656 ArgumentList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2657 JSParseNode
*listNode
)
2661 ts
->flags
|= TSF_REGEXP
;
2662 matched
= js_MatchToken(cx
, ts
, TOK_RP
);
2663 ts
->flags
&= ~TSF_REGEXP
;
2666 JSParseNode
*argNode
= AssignExpr(cx
, ts
, tc
);
2669 PN_APPEND(listNode
, argNode
);
2670 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
2672 if (js_GetToken(cx
, ts
) != TOK_RP
) {
2673 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2674 JSMSG_PAREN_AFTER_ARGS
);
2681 static JSParseNode
*
2682 MemberExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2683 JSBool allowCallSyntax
)
2685 JSParseNode
*pn
, *pn2
, *pn3
;
2690 /* Check for new expression first. */
2691 ts
->flags
|= TSF_REGEXP
;
2692 tt
= js_PeekToken(cx
, ts
);
2693 ts
->flags
&= ~TSF_REGEXP
;
2694 if (tt
== TOK_NEW
) {
2695 (void) js_GetToken(cx
, ts
);
2697 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
2700 pn2
= MemberExpr(cx
, ts
, tc
, JS_FALSE
);
2703 pn
->pn_op
= JSOP_NEW
;
2704 PN_INIT_LIST_1(pn
, pn2
);
2705 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
2707 if (js_MatchToken(cx
, ts
, TOK_LP
) && !ArgumentList(cx
, ts
, tc
, pn
))
2709 if (pn
->pn_count
> ARGC_LIMIT
) {
2710 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2711 JSMSG_TOO_MANY_CON_ARGS
);
2714 pn
->pn_pos
.end
= PN_LAST(pn
)->pn_pos
.end
;
2716 pn
= PrimaryExpr(cx
, ts
, tc
);
2721 while ((tt
= js_GetToken(cx
, ts
)) > TOK_EOF
) {
2722 if (tt
== TOK_DOT
) {
2723 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NAME
, tc
);
2726 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NAME_AFTER_DOT
);
2727 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2728 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2729 pn2
->pn_op
= JSOP_GETPROP
;
2731 pn2
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
2732 } else if (tt
== TOK_LB
) {
2733 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_BINARY
, tc
);
2736 pn3
= Expr(cx
, ts
, tc
);
2740 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
2741 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2742 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2744 /* Optimize o['p'] to o.p by rewriting pn2. */
2745 if (pn3
->pn_type
== TOK_STRING
) {
2746 pn2
->pn_type
= TOK_DOT
;
2747 pn2
->pn_op
= JSOP_GETPROP
;
2748 pn2
->pn_arity
= PN_NAME
;
2750 pn2
->pn_atom
= pn3
->pn_atom
;
2752 pn2
->pn_op
= JSOP_GETELEM
;
2754 pn2
->pn_right
= pn3
;
2756 } else if (allowCallSyntax
&& tt
== TOK_LP
) {
2757 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
2761 /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
2762 pn2
->pn_op
= JSOP_CALL
;
2763 if (pn
->pn_op
== JSOP_NAME
&&
2764 pn
->pn_atom
== cx
->runtime
->atomState
.evalAtom
) {
2765 pn2
->pn_op
= JSOP_EVAL
;
2766 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2769 PN_INIT_LIST_1(pn2
, pn
);
2770 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2772 if (!ArgumentList(cx
, ts
, tc
, pn2
))
2774 if (pn2
->pn_count
> ARGC_LIMIT
) {
2775 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2776 JSMSG_TOO_MANY_FUN_ARGS
);
2779 pn2
->pn_pos
.end
= PN_LAST(pn2
)->pn_pos
.end
;
2787 if (tt
== TOK_ERROR
)
2792 static JSParseNode
*
2793 PrimaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2796 JSParseNode
*pn
, *pn2
, *pn3
;
2798 #if JS_HAS_GETTER_SETTER
2803 #if JS_HAS_SHARP_VARS
2804 JSParseNode
*defsharp
;
2808 notsharp
= JS_FALSE
;
2811 * Control flows here after #n= is scanned. If the following primary is
2812 * not valid after such a "sharp variable" definition, the token type case
2813 * should set notsharp.
2819 ts
->flags
|= TSF_REGEXP
;
2820 tt
= js_GetToken(cx
, ts
);
2821 ts
->flags
&= ~TSF_REGEXP
;
2823 #if JS_HAS_GETTER_SETTER
2824 if (tt
== TOK_NAME
) {
2825 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
2826 if (tt
== TOK_ERROR
)
2832 #if JS_HAS_LEXICAL_CLOSURE
2834 pn
= FunctionExpr(cx
, ts
, tc
);
2840 #if JS_HAS_INITIALIZERS
2846 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
2849 pn
->pn_type
= TOK_RB
;
2852 #if JS_HAS_SHARP_VARS
2854 PN_INIT_LIST_1(pn
, defsharp
);
2860 ts
->flags
|= TSF_REGEXP
;
2861 matched
= js_MatchToken(cx
, ts
, TOK_RB
);
2862 ts
->flags
&= ~TSF_REGEXP
;
2864 for (atomIndex
= 0; atomIndex
< ATOM_INDEX_LIMIT
; atomIndex
++) {
2865 ts
->flags
|= TSF_REGEXP
;
2866 tt
= js_PeekToken(cx
, ts
);
2867 ts
->flags
&= ~TSF_REGEXP
;
2869 pn
->pn_extra
|= PNX_ENDCOMMA
;
2873 if (tt
== TOK_COMMA
) {
2874 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
2875 js_MatchToken(cx
, ts
, TOK_COMMA
);
2876 pn2
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
2878 pn2
= AssignExpr(cx
, ts
, tc
);
2884 if (tt
!= TOK_COMMA
) {
2885 /* If we didn't already match TOK_COMMA in above case. */
2886 if (!js_MatchToken(cx
, ts
, TOK_COMMA
))
2891 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_LIST
);
2893 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2898 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_LIST
, tc
);
2901 pn
->pn_type
= TOK_RC
;
2903 #if JS_HAS_SHARP_VARS
2905 PN_INIT_LIST_1(pn
, defsharp
);
2911 if (!js_MatchToken(cx
, ts
, TOK_RC
)) {
2915 tt
= js_GetToken(cx
, ts
);
2918 pn3
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
2920 pn3
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
2923 #if JS_HAS_GETTER_SETTER
2924 atom
= CURRENT_TOKEN(ts
).t_atom
;
2926 if (atom
== rt
->atomState
.getAtom
||
2927 atom
== rt
->atomState
.setAtom
) {
2928 op
= (atom
== rt
->atomState
.getAtom
)
2931 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
2932 pn3
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NAME
,
2936 pn3
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
2937 pn3
->pn_expr
= NULL
;
2939 /* We have to fake a 'function' token here. */
2940 CURRENT_TOKEN(ts
).t_op
= JSOP_NOP
;
2941 CURRENT_TOKEN(ts
).type
= TOK_FUNCTION
;
2942 pn2
= FunctionExpr(cx
, ts
, tc
);
2943 pn2
= NewBinary(cx
, TOK_COLON
, op
, pn3
, pn2
, tc
);
2947 /* else fall thru ... */
2950 pn3
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
2952 pn3
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
2955 if (!js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2958 JSMSG_TRAILING_COMMA
)) {
2963 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2968 tt
= js_GetToken(cx
, ts
);
2969 #if JS_HAS_GETTER_SETTER
2970 if (tt
== TOK_NAME
) {
2971 tt
= CheckGetterOrSetter(cx
, ts
, TOK_COLON
);
2972 if (tt
== TOK_ERROR
)
2976 if (tt
!= TOK_COLON
) {
2977 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2978 JSMSG_COLON_AFTER_ID
);
2981 op
= CURRENT_TOKEN(ts
).t_op
;
2982 pn2
= NewBinary(cx
, TOK_COLON
, op
, pn3
, AssignExpr(cx
, ts
, tc
),
2984 #if JS_HAS_GETTER_SETTER
2990 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
2992 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_LIST
);
2995 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2998 #if JS_HAS_SHARP_VARS
3002 defsharp
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
3005 defsharp
->pn_kid
= NULL
;
3006 defsharp
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
3010 /* Check for forward/dangling references at runtime, to allow eval. */
3011 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
3014 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
3017 #endif /* JS_HAS_SHARP_VARS */
3018 #endif /* JS_HAS_INITIALIZERS */
3022 #if JS_HAS_IN_OPERATOR
3025 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_UNARY
, tc
);
3028 #if JS_HAS_IN_OPERATOR
3030 * Always accept the 'in' operator in a parenthesized expression,
3031 * where it's unambiguous, even if we might be parsing the init of a
3034 oldflags
= tc
->flags
;
3035 tc
->flags
&= ~TCF_IN_FOR_INIT
;
3037 pn2
= Expr(cx
, ts
, tc
);
3038 #if JS_HAS_IN_OPERATOR
3039 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
3044 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
3045 pn
->pn_type
= TOK_RP
;
3046 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
3052 #if JS_HAS_SHARP_VARS
3058 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
3061 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
3062 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
3063 if (tt
== TOK_NAME
) {
3064 pn
->pn_arity
= PN_NAME
;
3069 /* Unqualified __parent__ and __proto__ uses require activations. */
3070 if (pn
->pn_atom
== cx
->runtime
->atomState
.parentAtom
||
3071 pn
->pn_atom
== cx
->runtime
->atomState
.protoAtom
) {
3072 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3078 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
3081 pn
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
3082 #if JS_HAS_SHARP_VARS
3088 pn
= NewParseNode(cx
, &CURRENT_TOKEN(ts
), PN_NULLARY
, tc
);
3091 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
3092 #if JS_HAS_SHARP_VARS
3097 #if !JS_HAS_EXPORT_IMPORT
3102 badWord
= js_DeflateString(cx
, CURRENT_TOKEN(ts
).ptr
,
3103 (size_t) CURRENT_TOKEN(ts
).pos
.end
.index
3104 - CURRENT_TOKEN(ts
).pos
.begin
.index
);
3105 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3106 JSMSG_RESERVED_ID
, badWord
);
3107 JS_free(cx
, badWord
);
3111 /* The scanner or one of its subroutines reported the error. */
3115 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3116 JSMSG_SYNTAX_ERROR
);
3120 #if JS_HAS_SHARP_VARS
3124 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3125 JSMSG_BAD_SHARP_VAR_DEF
);
3128 defsharp
->pn_kid
= pn
;
3136 ContainsVarStmt(JSParseNode
*pn
)
3142 switch (pn
->pn_arity
) {
3144 if (pn
->pn_type
== TOK_VAR
)
3146 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
3147 if (ContainsVarStmt(pn2
))
3152 return ContainsVarStmt(pn
->pn_kid1
) ||
3153 ContainsVarStmt(pn
->pn_kid2
) ||
3154 ContainsVarStmt(pn
->pn_kid3
);
3157 * Limit recursion if pn is a binary expression, which can't contain a
3160 if (pn
->pn_op
!= JSOP_NOP
)
3162 return ContainsVarStmt(pn
->pn_left
) || ContainsVarStmt(pn
->pn_right
);
3164 if (pn
->pn_op
!= JSOP_NOP
)
3166 return ContainsVarStmt(pn
->pn_kid
);
3173 * Fold from one constant type to another.
3174 * XXX handles only strings and numbers for now
3177 FoldType(JSContext
*cx
, JSParseNode
*pn
, JSTokenType type
)
3179 if (pn
->pn_type
!= type
) {
3182 if (pn
->pn_type
== TOK_STRING
) {
3184 if (!js_ValueToNumber(cx
, ATOM_KEY(pn
->pn_atom
), &d
))
3187 pn
->pn_type
= TOK_NUMBER
;
3188 pn
->pn_op
= JSOP_NUMBER
;
3193 if (pn
->pn_type
== TOK_NUMBER
) {
3194 JSString
*str
= js_NumberToString(cx
, pn
->pn_dval
);
3197 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
3200 pn
->pn_type
= TOK_STRING
;
3201 pn
->pn_op
= JSOP_STRING
;
3212 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
3213 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
3214 * a successful call to this function.
3217 FoldBinaryNumeric(JSContext
*cx
, JSOp op
, JSParseNode
*pn1
, JSParseNode
*pn2
,
3218 JSParseNode
*pn
, JSTreeContext
*tc
)
3224 JS_ASSERT(pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
);
3230 if (!js_DoubleToECMAInt32(cx
, d
, &i
))
3232 if (!js_DoubleToECMAInt32(cx
, d2
, &j
))
3235 d
= (op
== JSOP_LSH
) ? i
<< j
: i
>> j
;
3239 if (!js_DoubleToECMAUint32(cx
, d
, &u
))
3241 if (!js_DoubleToECMAInt32(cx
, d2
, &j
))
3262 /* XXX MSVC miscompiles such that (NaN == 0) */
3263 if (JSDOUBLE_IS_NaN(d2
))
3264 d
= *cx
->runtime
->jsNaN
;
3267 if (d
== 0 || JSDOUBLE_IS_NaN(d
))
3268 d
= *cx
->runtime
->jsNaN
;
3269 else if ((JSDOUBLE_HI32(d
) ^ JSDOUBLE_HI32(d2
)) >> 31)
3270 d
= *cx
->runtime
->jsNegativeInfinity
;
3272 d
= *cx
->runtime
->jsPositiveInfinity
;
3280 d
= *cx
->runtime
->jsNaN
;
3283 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
3284 if (!(JSDOUBLE_IS_FINITE(d
) && JSDOUBLE_IS_INFINITE(d2
)))
3293 /* Take care to allow pn1 or pn2 to alias pn. */
3295 RecycleTree(pn1
, tc
);
3297 RecycleTree(pn2
, tc
);
3298 pn
->pn_type
= TOK_NUMBER
;
3299 pn
->pn_op
= JSOP_NUMBER
;
3300 pn
->pn_arity
= PN_NULLARY
;
3306 js_FoldConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
3308 JSParseNode
*pn1
= NULL
, *pn2
= NULL
, *pn3
= NULL
;
3311 if (!JS_CHECK_STACK_SIZE(cx
, stackDummy
)) {
3312 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_OVER_RECURSED
);
3316 switch (pn
->pn_arity
) {
3318 if (!js_FoldConstants(cx
, pn
->pn_body
, tc
))
3323 /* Save the list head in pn1 for later use. */
3324 for (pn1
= pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
3325 if (!js_FoldConstants(cx
, pn2
, tc
))
3331 /* Any kid may be null (e.g. for (;;)). */
3335 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
3337 if (pn2
&& !js_FoldConstants(cx
, pn2
, tc
))
3339 if (pn3
&& !js_FoldConstants(cx
, pn3
, tc
))
3344 /* First kid may be null (for default case in switch). */
3347 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
3349 if (!js_FoldConstants(cx
, pn2
, tc
))
3354 /* Our kid may be null (e.g. return; vs. return e;). */
3356 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
3362 * Skip pn1 down along a chain of dotted member expressions to avoid
3363 * excessive recursion. Our only goal here is to fold constants (if
3364 * any) in the primary expression operand to the left of the first
3368 while (pn1
&& pn1
->pn_arity
== PN_NAME
)
3370 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
3378 switch (pn
->pn_type
) {
3380 if (ContainsVarStmt(pn2
) || ContainsVarStmt(pn3
))
3385 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
3386 switch (pn1
->pn_type
) {
3388 if (pn1
->pn_dval
== 0)
3392 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1
->pn_atom
)) == 0)
3396 if (pn1
->pn_op
== JSOP_TRUE
)
3398 if (pn1
->pn_op
== JSOP_FALSE
|| pn1
->pn_op
== JSOP_NULL
) {
3404 /* Early return to dodge common code that copies pn2 to pn. */
3409 /* pn2 is the then- or else-statement subtree to compile. */
3410 PN_MOVE_NODE(pn
, pn2
);
3412 /* False condition and no else: make pn an empty statement. */
3413 pn
->pn_type
= TOK_SEMI
;
3414 pn
->pn_arity
= PN_UNARY
;
3417 RecycleTree(pn2
, tc
);
3418 if (pn3
&& pn3
!= pn2
)
3419 RecycleTree(pn3
, tc
);
3423 if (pn
->pn_arity
== PN_LIST
) {
3424 size_t length
, length2
;
3426 JSString
*str
, *str2
;
3429 * Any string literal term with all others number or string means
3430 * this is a concatenation. If any term is not a string or number
3431 * literal, we can't fold.
3433 JS_ASSERT(pn
->pn_count
> 2);
3434 if (pn
->pn_extra
& PNX_CANTFOLD
)
3436 if (pn
->pn_extra
!= PNX_STRCAT
)
3439 /* Ok, we're concatenating: convert non-string constant operands. */
3441 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
3442 if (!FoldType(cx
, pn2
, TOK_STRING
))
3444 /* XXX fold only if all operands convert to string */
3445 if (pn2
->pn_type
!= TOK_STRING
)
3447 length
+= ATOM_TO_STRING(pn2
->pn_atom
)->length
;
3450 /* Allocate a new buffer and string descriptor for the result. */
3451 chars
= (jschar
*) JS_malloc(cx
, (length
+ 1) * sizeof(jschar
));
3454 str
= js_NewString(cx
, chars
, length
, 0);
3460 /* Fill the buffer, advancing chars and recycling kids as we go. */
3461 for (pn2
= pn1
; pn2
; pn2
= pn3
) {
3462 str2
= ATOM_TO_STRING(pn2
->pn_atom
);
3463 length2
= str2
->length
;
3464 js_strncpy(chars
, str2
->chars
, length2
);
3467 RecycleTree(pn2
, tc
);
3471 /* Atomize the result string and mutate pn to refer to it. */
3472 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
3475 pn
->pn_type
= TOK_STRING
;
3476 pn
->pn_op
= JSOP_STRING
;
3477 pn
->pn_arity
= PN_NULLARY
;
3481 /* Handle a binary string concatenation. */
3482 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
3483 if (pn1
->pn_type
== TOK_STRING
|| pn2
->pn_type
== TOK_STRING
) {
3484 JSString
*left
, *right
, *str
;
3486 if (!FoldType(cx
, (pn1
->pn_type
!= TOK_STRING
) ? pn1
: pn2
,
3490 if (pn1
->pn_type
!= TOK_STRING
|| pn2
->pn_type
!= TOK_STRING
)
3492 left
= ATOM_TO_STRING(pn1
->pn_atom
);
3493 right
= ATOM_TO_STRING(pn2
->pn_atom
);
3494 str
= js_ConcatStrings(cx
, left
, right
);
3497 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
3500 pn
->pn_type
= TOK_STRING
;
3501 pn
->pn_op
= JSOP_STRING
;
3502 pn
->pn_arity
= PN_NULLARY
;
3503 RecycleTree(pn1
, tc
);
3504 RecycleTree(pn2
, tc
);
3508 /* Can't concatenate string literals, let's try numbers. */
3512 /* The * in 'import *;' parses as a nullary star node. */
3513 if (pn
->pn_arity
== PN_NULLARY
)
3521 if (pn
->pn_arity
== PN_LIST
) {
3522 JS_ASSERT(pn
->pn_count
> 2);
3523 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
3524 if (!FoldType(cx
, pn2
, TOK_NUMBER
))
3526 /* XXX fold only if all operands convert to number */
3527 if (pn2
->pn_type
!= TOK_NUMBER
)
3531 JSOp op
= pn
->pn_op
;
3535 if (!FoldBinaryNumeric(cx
, op
, pn1
, pn2
, pn
, tc
))
3537 while ((pn2
= pn3
) != NULL
) {
3539 if (!FoldBinaryNumeric(cx
, op
, pn
, pn2
, pn
, tc
))
3544 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
3545 if (!FoldType(cx
, pn1
, TOK_NUMBER
) ||
3546 !FoldType(cx
, pn2
, TOK_NUMBER
)) {
3549 if (pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
) {
3550 if (!FoldBinaryNumeric(cx
, pn
->pn_op
, pn1
, pn2
, pn
, tc
))
3557 if (pn1
->pn_type
== TOK_NUMBER
) {
3561 /* Operate on one numeric constant. */
3563 switch (pn
->pn_op
) {
3565 if (!js_DoubleToECMAInt32(cx
, d
, &i
))
3573 * Negation of a zero doesn't produce a negative
3574 * zero on HPUX. Perform the operation by bit
3577 JSDOUBLE_HI32(d
) ^= JSDOUBLE_HI32_SIGNBIT
;
3587 pn
->pn_type
= TOK_PRIMARY
;
3588 pn
->pn_op
= (d
== 0) ? JSOP_TRUE
: JSOP_FALSE
;
3589 pn
->pn_arity
= PN_NULLARY
;
3593 /* Return early to dodge the common TOK_NUMBER code. */
3596 pn
->pn_type
= TOK_NUMBER
;
3597 pn
->pn_op
= JSOP_NUMBER
;
3598 pn
->pn_arity
= PN_NULLARY
;
3600 RecycleTree(pn1
, tc
);