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;
421
#ifdef METER_PARSENODES
422
void *sbrk(ptrdiff_t), *before = sbrk(0);
426
* Push a compiler frame if we have no frames, or if the top frame is a
427
* lightweight function activation, or if its scope chain doesn't match
428
* the one passed to us.
431
if (!fp || !fp->varobj || fp->scopeChain != chain) {
432
memset(&frame, 0, sizeof frame);
433
frame.varobj = frame.scopeChain = chain;
434
if (cx->options & JSOPTION_VAROBJFIX) {
435
while ((chain = JS_GetParent(cx, chain)) != NULL)
436
frame.varobj = chain;
442
/* Prevent GC activation while compiling. */
443
JS_KEEP_ATOMS(cx->runtime);
445
pn = Statements(cx, ts, &cg->treeContext);
448
} else if (!js_MatchToken(cx, ts, TOK_EOF)) {
449
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
453
#ifdef METER_PARSENODES
454
printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
455
(char *)sbrk(0) - (char *)before,
458
parsenodes - recyclednodes);
463
* No need to emit code here -- Statements already has, for each
464
* statement in turn. Search for TCF_COMPILING in Statements, below.
465
* That flag is set for every tc == &cg->treeContext, and it implies
466
* that the tc can be downcast to a cg and used to emit code during
467
* parsing, rather than at the end of the parse phase.
469
JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
473
#ifdef METER_PARSENODES
474
printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
475
(char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
478
JS_DumpArenaStats(stdout);
480
JS_UNKEEP_ATOMS(cx->runtime);
486
* Insist on a final return before control flows out of pn, but don't be too
487
* smart about loops (do {...; return e2;} while(0) at the end of a function
488
* that contains an early return e1 will get a strict-option-only warning).
491
HasFinalReturn(JSParseNode *pn)
493
JSBool ok, hasDefault;
494
JSParseNode *pn2, *pn3;
496
switch (pn->pn_type) {
500
return HasFinalReturn(PN_LAST(pn));
503
ok = HasFinalReturn(pn->pn_kid2);
504
ok &= pn->pn_kid3 && HasFinalReturn(pn->pn_kid3);
507
#if JS_HAS_SWITCH_STATEMENT
510
hasDefault = JS_FALSE;
511
for (pn2 = pn->pn_kid2->pn_head; ok && pn2; pn2 = pn2->pn_next) {
512
if (pn2->pn_type == TOK_DEFAULT)
513
hasDefault = JS_TRUE;
515
JS_ASSERT(pn3->pn_type == TOK_LC);
517
ok &= HasFinalReturn(PN_LAST(pn3));
519
/* If a final switch has no default case, we judge it harshly. */
522
#endif /* JS_HAS_SWITCH_STATEMENT */
525
return HasFinalReturn(pn->pn_right);
530
#if JS_HAS_EXCEPTIONS
535
/* If we have a finally block that returns, we are done. */
536
if (pn->pn_kid3 && HasFinalReturn(pn->pn_kid3))
539
/* Else check the try block and any and all catch statements. */
540
ok = HasFinalReturn(pn->pn_kid1);
542
ok &= HasFinalReturn(pn->pn_kid2);
546
/* Check this block's code and iterate over further catch blocks. */
547
ok = HasFinalReturn(pn->pn_kid3);
548
for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
549
ok &= HasFinalReturn(pn2->pn_kid3);
559
ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
566
char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
567
ok = js_ReportCompileErrorNumber(cx, ts, NULL,
570
JSMSG_NO_RETURN_VALUE, name);
572
ok = js_ReportCompileErrorNumber(cx, ts, NULL,
575
JSMSG_ANON_NO_RETURN_VALUE);
581
CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
583
return HasFinalReturn(pn) || ReportNoReturnValue(cx, ts);
587
FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
590
JSStackFrame *fp, frame;
596
funobj = fun->object;
597
if (!fp || fp->fun != fun || fp->varobj != funobj ||
598
fp->scopeChain != funobj) {
599
memset(&frame, 0, sizeof frame);
601
frame.varobj = frame.scopeChain = funobj;
606
oldflags = tc->flags;
607
tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
608
tc->flags |= TCF_IN_FUNCTION;
609
pn = Statements(cx, ts, tc);
611
/* Check for falling off the end of a function that returns a value. */
612
if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
613
if (!CheckFinalReturn(cx, ts, pn))
618
tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
623
* Compile a JS function body, which might appear as the value of an event
624
* handler attribute in an HTML <INPUT> tag.
627
js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
629
JSArenaPool codePool, notePool;
630
JSCodeGenerator funcg;
631
JSStackFrame *fp, frame;
636
JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
637
JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote));
638
if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool,
639
ts->filename, ts->lineno,
644
/* Prevent GC activation while compiling. */
645
JS_KEEP_ATOMS(cx->runtime);
647
/* Push a JSStackFrame for use by FunctionBody and js_EmitFunctionBody. */
649
funobj = fun->object;
650
JS_ASSERT(!fp || fp->fun != fun || fp->varobj != funobj ||
651
fp->scopeChain != funobj);
652
memset(&frame, 0, sizeof frame);
654
frame.varobj = frame.scopeChain = funobj;
658
/* Ensure that the body looks like a block statement to js_EmitTree. */
659
CURRENT_TOKEN(ts).type = TOK_LC;
660
pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
665
* No need to emit code here -- Statements (via FunctionBody) already
666
* has. See similar comment in js_CompileTokenStream, and bug 108257.
668
fun->script = js_NewScriptFromCG(cx, &funcg, fun);
672
if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
673
fun->flags |= JSFUN_HEAVYWEIGHT;
678
/* Restore saved state and release code generation arenas. */
680
JS_UNKEEP_ATOMS(cx->runtime);
681
js_FinishCodeGenerator(cx, &funcg);
682
JS_FinishArenaPool(&codePool);
683
JS_FinishArenaPool(¬ePool);
688
FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
691
JSParseNode *pn, *body;
693
JSAtom *funAtom, *argAtom;
697
JSScopeProperty *sprop;
701
JSAtomListElement *ale;
703
/* Make a TOK_FUNCTION node. */
704
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc);
707
#if JS_HAS_GETTER_SETTER
708
op = CURRENT_TOKEN(ts).t_op;
711
/* Scan the optional function name into funAtom. */
712
if (js_MatchToken(cx, ts, TOK_NAME))
713
funAtom = CURRENT_TOKEN(ts).t_atom;
717
/* Find the nearest variable-declaring scope and use it as our parent. */
718
parent = cx->fp->varobj;
719
fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, parent,
724
#if JS_HAS_GETTER_SETTER
726
fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
729
/* Now parse formal argument list and compute fun->nargs. */
730
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
731
if (!js_MatchToken(cx, ts, TOK_RP)) {
733
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);
734
argAtom = CURRENT_TOKEN(ts).t_atom;
736
if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj,
737
(JSProperty **)&sprop)) {
743
if (pobj == fun->object &&
744
sprop->getter == js_GetArgument) {
745
const char *name = js_AtomToPrintableString(cx, argAtom);
748
* A duplicate parameter name. We force a duplicate node
749
* on the SCOPE_LAST_PROP(scope) list with the same id,
750
* distinguished by the SPROP_IS_DUPLICATE flag, and not
751
* mapped by an entry in scope.
754
js_ReportCompileErrorNumber(cx, ts, NULL,
757
JSMSG_DUPLICATE_FORMAL,
760
dupflag = SPROP_IS_DUPLICATE;
762
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
767
if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom,
768
js_GetArgument, js_SetArgument,
770
JSPROP_ENUMERATE | JSPROP_PERMANENT |
772
SPROP_HAS_SHORTID | dupflag,
777
} while (js_MatchToken(cx, ts, TOK_COMMA));
779
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
782
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
783
pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
785
TREE_CONTEXT_INIT(&funtc);
786
body = FunctionBody(cx, ts, fun, &funtc);
790
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
791
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
793
#if JS_HAS_LEXICAL_CLOSURE
795
* If we collected flags that indicate nested heavyweight functions, or
796
* this function contains heavyweight-making statements (references to
797
* __parent__ or __proto__; use of with, eval, import, or export; and
798
* assignment to arguments), flag the function as heavyweight (requiring
799
* a call object per invocation).
801
if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
802
fun->flags |= JSFUN_HEAVYWEIGHT;
803
tc->flags |= TCF_FUN_HEAVYWEIGHT;
806
* If this function is a named statement function not at top-level
807
* (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
808
* are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
809
* enclosing function, if any, must be heavyweight.
811
if ((!lambda && funAtom && tc->topStmt) ||
812
(funtc.flags & TCF_FUN_USES_NONLOCALS)) {
813
tc->flags |= TCF_FUN_HEAVYWEIGHT;
819
* Record names for function statements in tc->decls so we know when to
820
* avoid optimizing variable references that might name a function.
822
if (!lambda && funAtom) {
823
ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
825
prevop = ALE_JSOP(ale);
826
if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
827
const char *name = js_AtomToPrintableString(cx, funAtom);
829
!js_ReportCompileErrorNumber(cx, ts, NULL,
830
(prevop != JSOP_DEFCONST)
834
JSMSG_REDECLARED_VAR,
835
(prevop == JSOP_DEFFUN ||
836
prevop == JSOP_CLOSURE)
838
: (prevop == JSOP_DEFCONST)
845
if (tc->topStmt && prevop == JSOP_DEFVAR)
846
tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
848
ale = js_IndexAtom(cx, funAtom, &tc->decls);
852
ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
854
#if JS_HAS_LEXICAL_CLOSURE
856
* A function nested at top level inside another's body needs only a
857
* local variable to bind its name to its value, and not an activation
858
* object property (it might also need the activation property, if the
859
* outer function contains with statements, e.g., but the stack slot
860
* wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a
861
* JSOP_GETVAR bytecode).
863
if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
868
* Define a property on the outer function so that LookupArgOrVar
869
* can properly optimize accesses.
871
* XXX Here and in Variables, we use the function object's scope,
872
* XXX arguably polluting it, when we could use a compiler-private
873
* XXX scope structure. Tradition!
877
JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
878
JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
879
if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom,
880
OBJECT_TO_JSVAL(fun->object),
884
SPROP_HAS_SHORTID, fp->fun->nvars,
893
#if JS_HAS_LEXICAL_CLOSURE
894
if (lambda || !funAtom) {
896
* ECMA ed. 3 standard: function expression, possibly anonymous (even
897
* if at top-level, an unnamed function is an expression statement, not
898
* a function declaration).
900
op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
901
} else if (tc->topStmt) {
903
* ECMA ed. 3 extension: a function expression statement not at the
904
* top level, e.g., in a compound statement such as the "then" part
905
* of an "if" statement, binds a closure only if control reaches that
914
* Pending a better automatic GC root management scheme (see Mozilla bug
915
* 40757, http://bugzilla.mozilla.org/show_bug.cgi?id=40757), we need to
916
* atomize here to protect against a GC activation.
918
pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0);
924
pn->pn_flags = funtc.flags & TCF_FUN_FLAGS;
925
pn->pn_tryCount = funtc.tryCount;
926
TREE_CONTEXT_FINISH(&funtc);
931
FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
933
return FunctionDef(cx, ts, tc, JS_FALSE);
936
#if JS_HAS_LEXICAL_CLOSURE
938
FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
940
return FunctionDef(cx, ts, tc, JS_TRUE);
945
* Parse the statements in a block, creating a TOK_LC node that lists the
946
* statements' trees. If called from block-parsing code, the caller must
947
* match { before and } after.
950
Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
952
JSParseNode *pn, *pn2;
957
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
962
ts->flags |= TSF_REGEXP;
963
while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
964
ts->flags &= ~TSF_REGEXP;
965
pn2 = Statement(cx, ts, tc);
968
ts->flags |= TSF_REGEXP;
970
/* If compiling top-level statements, emit as we go to save space. */
971
if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
973
JS_HAS_STRICT_OPTION(cx) &&
974
(tc->flags & TCF_RETURN_EXPR)) {
976
* Check pn2 for lack of a final return statement if it is the
977
* last statement in the block.
979
tt = js_PeekToken(cx, ts);
980
if ((tt == TOK_EOF || tt == TOK_RC) &&
981
!CheckFinalReturn(cx, ts, pn2)) {
987
* Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
988
* CheckFinalReturn again.
990
tc->flags &= ~TCF_RETURN_EXPR;
992
if (!js_FoldConstants(cx, pn2, tc) ||
993
!js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
994
!js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
998
RecycleTree(pn2, tc);
1003
ts->flags &= ~TSF_REGEXP;
1004
if (tt == TOK_ERROR)
1007
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1011
static JSParseNode *
1012
Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1014
JSParseNode *pn, *pn2;
1016
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1017
pn = Expr(cx, ts, tc);
1020
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1023
* Check for (a = b) and "correct" it to (a == b) iff b's operator has
1024
* greater precedence than ==.
1025
* XXX not ECMA, but documented in several books -- now a strict warning.
1027
if (pn->pn_type == TOK_ASSIGN &&
1028
pn->pn_op == JSOP_NOP &&
1029
pn->pn_right->pn_type > TOK_EQOP)
1031
JSBool rewrite = !JSVERSION_IS_ECMA(cx->version);
1032
if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1033
JSREPORT_WARNING | JSREPORT_STRICT,
1034
JSMSG_EQUAL_AS_ASSIGN,
1036
? "\nAssuming equality test"
1041
pn->pn_type = TOK_EQOP;
1042
pn->pn_op = (JSOp)cx->jsop_eq;
1044
switch (pn2->pn_op) {
1046
pn2->pn_op = JSOP_NAME;
1049
pn2->pn_op = JSOP_GETPROP;
1052
pn2->pn_op = JSOP_GETELEM;
1063
MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1066
#if JS_HAS_LABEL_STATEMENT
1069
tt = js_PeekTokenSameLine(cx, ts);
1070
if (tt == TOK_ERROR)
1072
if (tt == TOK_NAME) {
1073
(void) js_GetToken(cx, ts);
1074
label = CURRENT_TOKEN(ts).t_atom;
1081
pn->pn_atom = label;
1085
#if JS_HAS_EXPORT_IMPORT
1086
static JSParseNode *
1087
ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1089
JSParseNode *pn, *pn2, *pn3;
1092
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1093
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1096
pn->pn_op = JSOP_NAME;
1097
pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1102
ts->flags |= TSF_REGEXP;
1103
while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1104
ts->flags &= ~TSF_REGEXP;
1105
if (pn->pn_op == JSOP_IMPORTALL)
1108
if (tt == TOK_DOT) {
1109
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1112
if (js_MatchToken(cx, ts, TOK_STAR)) {
1113
pn2->pn_op = JSOP_IMPORTALL;
1114
pn2->pn_atom = NULL;
1116
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1117
pn2->pn_op = JSOP_GETPROP;
1118
pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1123
pn2->pn_pos.begin = pn->pn_pos.begin;
1124
pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1126
/* Make a TOK_LB node. */
1127
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1130
pn3 = Expr(cx, ts, tc);
1134
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1135
pn2->pn_pos.begin = pn->pn_pos.begin;
1136
pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1138
pn2->pn_op = JSOP_GETELEM;
1140
pn2->pn_right = pn3;
1144
ts->flags |= TSF_REGEXP;
1146
ts->flags &= ~TSF_REGEXP;
1147
if (tt == TOK_ERROR)
1151
switch (pn->pn_op) {
1153
pn->pn_op = JSOP_IMPORTPROP;
1156
pn->pn_op = JSOP_IMPORTELEM;
1158
case JSOP_IMPORTALL:
1166
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT);
1169
#endif /* JS_HAS_EXPORT_IMPORT */
1171
extern const char js_with_statement_str[];
1173
static JSParseNode *
1174
Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1177
JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
1178
JSStmtInfo stmtInfo, *stmt, *stmt2;
1181
ts->flags |= TSF_REGEXP;
1182
tt = js_GetToken(cx, ts);
1183
ts->flags &= ~TSF_REGEXP;
1185
#if JS_HAS_GETTER_SETTER
1186
if (tt == TOK_NAME) {
1187
tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
1188
if (tt == TOK_ERROR)
1194
#if JS_HAS_EXPORT_IMPORT
1196
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1200
if (js_MatchToken(cx, ts, TOK_STAR)) {
1201
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1207
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
1208
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1211
pn2->pn_op = JSOP_NAME;
1212
pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1213
pn2->pn_expr = NULL;
1217
} while (js_MatchToken(cx, ts, TOK_COMMA));
1219
pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1220
tc->flags |= TCF_FUN_HEAVYWEIGHT;
1224
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1229
pn2 = ImportExpr(cx, ts, tc);
1233
} while (js_MatchToken(cx, ts, TOK_COMMA));
1234
pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1235
tc->flags |= TCF_FUN_HEAVYWEIGHT;
1237
#endif /* JS_HAS_EXPORT_IMPORT */
1240
return FunctionStmt(cx, ts, tc);
1243
/* An IF node has three kids: condition, then, and optional else. */
1244
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1247
pn1 = Condition(cx, ts, tc);
1250
js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
1251
pn2 = Statement(cx, ts, tc);
1254
if (js_MatchToken(cx, ts, TOK_ELSE)) {
1255
stmtInfo.type = STMT_ELSE;
1256
pn3 = Statement(cx, ts, tc);
1259
pn->pn_pos.end = pn3->pn_pos.end;
1262
pn->pn_pos.end = pn2->pn_pos.end;
1264
js_PopStatement(tc);
1270
#if JS_HAS_SWITCH_STATEMENT
1274
JSBool seenDefault = JS_FALSE;
1276
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1279
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
1281
/* pn1 points to the switch's discriminant. */
1282
pn1 = Expr(cx, ts, tc);
1286
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
1287
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
1289
/* pn2 is a list of case nodes. The default case has pn_left == NULL */
1290
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1295
js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
1297
while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
1301
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1302
JSMSG_TOO_MANY_DEFAULTS);
1305
seenDefault = JS_TRUE;
1309
pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1312
if (tt == TOK_DEFAULT) {
1313
pn3->pn_left = NULL;
1315
pn3->pn_left = Expr(cx, ts, tc);
1319
PN_APPEND(pn2, pn3);
1320
if (pn2->pn_count == JS_BIT(16)) {
1321
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1322
JSMSG_TOO_MANY_CASES);
1331
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1335
MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
1337
pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1340
pn4->pn_type = TOK_LC;
1342
while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
1343
tt != TOK_CASE && tt != TOK_DEFAULT) {
1344
if (tt == TOK_ERROR)
1346
pn5 = Statement(cx, ts, tc);
1349
pn4->pn_pos.end = pn5->pn_pos.end;
1350
PN_APPEND(pn4, pn5);
1353
/* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1355
pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
1356
pn3->pn_pos.end = pn4->pn_pos.end;
1357
pn3->pn_right = pn4;
1360
js_PopStatement(tc);
1362
pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1367
#endif /* JS_HAS_SWITCH_STATEMENT */
1370
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1373
js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
1374
pn2 = Condition(cx, ts, tc);
1378
pn2 = Statement(cx, ts, tc);
1381
js_PopStatement(tc);
1382
pn->pn_pos.end = pn2->pn_pos.end;
1386
#if JS_HAS_DO_WHILE_LOOP
1388
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1391
js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
1392
pn2 = Statement(cx, ts, tc);
1396
MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
1397
pn2 = Condition(cx, ts, tc);
1400
js_PopStatement(tc);
1401
pn->pn_pos.end = pn2->pn_pos.end;
1404
#endif /* JS_HAS_DO_WHILE_LOOP */
1407
/* A FOR node is binary, left is loop control and right is the body. */
1408
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1411
js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
1413
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
1414
tt = js_PeekToken(cx, ts);
1415
if (tt == TOK_SEMI) {
1416
/* No initializer -- set first kid of left sub-node to null. */
1419
/* Set pn1 to a var list or an initializing expression. */
1420
#if JS_HAS_IN_OPERATOR
1422
* Set the TCF_IN_FOR_INIT flag during parsing of the first clause
1423
* of the for statement. This flag will be used by the RelExpr
1424
* production; if it is set, then the 'in' keyword will not be
1425
* recognized as an operator, leaving it available to be parsed as
1426
* part of a for/in loop. A side effect of this restriction is
1427
* that (unparenthesized) expressions involving an 'in' operator
1428
* are illegal in the init clause of an ordinary for loop.
1430
tc->flags |= TCF_IN_FOR_INIT;
1431
#endif /* JS_HAS_IN_OPERATOR */
1432
if (tt == TOK_VAR) {
1433
(void) js_GetToken(cx, ts);
1434
pn1 = Variables(cx, ts, tc);
1436
pn1 = Expr(cx, ts, tc);
1438
#if JS_HAS_IN_OPERATOR
1439
tc->flags &= ~TCF_IN_FOR_INIT;
1440
#endif /* JS_HAS_IN_OPERATOR */
1446
* We can be sure that it's a for/in loop if there's still an 'in'
1447
* keyword here, even if JavaScript recognizes 'in' as an operator,
1448
* as we've excluded 'in' from being parsed in RelExpr by setting
1449
* the TCF_IN_FOR_INIT flag in our JSTreeContext.
1451
if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
1452
stmtInfo.type = STMT_FOR_IN_LOOP;
1454
/* Check that the left side of the 'in' is valid. */
1455
if ((pn1->pn_type == TOK_VAR)
1456
? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)
1457
: (pn1->pn_type != TOK_NAME &&
1458
pn1->pn_type != TOK_DOT &&
1459
pn1->pn_type != TOK_LB)) {
1460
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1461
JSMSG_BAD_FOR_LEFTSIDE);
1465
if (pn1->pn_type == TOK_VAR) {
1466
/* Tell js_EmitTree(TOK_VAR) to generate a final POP. */
1467
pn1->pn_extra = JS_TRUE;
1473
/* Beware 'for (arguments in ...)' with or without a 'var'. */
1474
if (pn2->pn_type == TOK_NAME &&
1475
pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {
1476
tc->flags |= TCF_FUN_HEAVYWEIGHT;
1479
/* Parse the object expression as the right operand of 'in'. */
1480
pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
1485
/* Parse the loop condition or null into pn2. */
1486
MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
1487
if (js_PeekToken(cx, ts) == TOK_SEMI) {
1490
pn2 = Expr(cx, ts, tc);
1495
/* Parse the update expression or null into pn3. */
1496
MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
1497
if (js_PeekToken(cx, ts) == TOK_RP) {
1500
pn3 = Expr(cx, ts, tc);
1505
/* Build the RESERVED node to use as the left kid of pn. */
1506
pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1509
pn4->pn_type = TOK_RESERVED;
1510
pn4->pn_op = JSOP_NOP;
1517
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
1519
/* Parse the loop body into pn->pn_right. */
1520
pn2 = Statement(cx, ts, tc);
1524
js_PopStatement(tc);
1526
/* Record the absolute line number for source note emission. */
1527
pn->pn_pos.end = pn2->pn_pos.end;
1530
#if JS_HAS_EXCEPTIONS
1532
JSParseNode *catchtail = NULL;
1534
* try nodes are ternary.
1535
* kid1 is the try Statement
1536
* kid2 is the catch node
1537
* kid3 is the finally Statement
1539
* catch nodes are ternary.
1540
* kid1 is the discriminant
1541
* kid2 is the next catch node, or NULL
1542
* kid3 is the catch block (on kid3 so that we can always append a
1543
* new catch pn on catchtail->kid2)
1545
* catch discriminant nodes are binary
1546
* atom is the receptacle
1547
* expr is the discriminant code
1549
* finally nodes are unary (just the finally expression)
1551
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1552
pn->pn_op = JSOP_NOP;
1554
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
1555
js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
1556
pn->pn_kid1 = Statements(cx, ts, tc);
1559
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
1560
js_PopStatement(tc);
1563
while (js_PeekToken(cx, ts) == TOK_CATCH) {
1564
/* check for another catch after unconditional catch */
1565
if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {
1566
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1567
JSMSG_CATCH_AFTER_GENERAL);
1572
* legal catch forms are:
1574
* catch (v if <boolean_expression>)
1575
* (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
1577
(void) js_GetToken(cx, ts); /* eat `catch' */
1578
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1583
* We use a PN_NAME for the discriminant (catchguard) node
1584
* with the actual discriminant code in the initializer spot
1586
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
1587
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
1588
pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1592
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
1593
pn3->pn_expr = NULL;
1594
#if JS_HAS_CATCH_GUARD
1596
* We use `catch (x if x === 5)' (not `catch (x : x === 5)') to
1597
* avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.
1599
if (js_PeekToken(cx, ts) == TOK_IF) {
1600
(void)js_GetToken(cx, ts); /* eat `if' */
1601
pn3->pn_expr = Expr(cx, ts, tc);
1608
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
1610
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
1611
js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
1612
stmtInfo.label = pn3->pn_atom;
1613
pn2->pn_kid3 = Statements(cx, ts, tc);
1616
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
1617
js_PopStatement(tc);
1619
catchtail = catchtail->pn_kid2 = pn2;
1621
catchtail->pn_kid2 = NULL;
1623
if (js_MatchToken(cx, ts, TOK_FINALLY)) {
1625
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
1626
js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
1627
pn->pn_kid3 = Statements(cx, ts, tc);
1630
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
1631
js_PopStatement(tc);
1635
if (!pn->pn_kid2 && !pn->pn_kid3) {
1636
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1637
JSMSG_CATCH_OR_FINALLY);
1645
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1648
pn2 = Expr(cx, ts, tc);
1651
pn->pn_pos.end = pn2->pn_pos.end;
1652
pn->pn_op = JSOP_THROW;
1656
/* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
1658
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1659
JSMSG_CATCH_WITHOUT_TRY);
1663
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1664
JSMSG_FINALLY_WITHOUT_TRY);
1667
#endif /* JS_HAS_EXCEPTIONS */
1670
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1673
if (!MatchLabel(cx, ts, pn))
1676
label = pn->pn_atom;
1678
for (; ; stmt = stmt->down) {
1680
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1681
JSMSG_LABEL_NOT_FOUND);
1684
if (stmt->type == STMT_LABEL && stmt->label == label)
1688
for (; ; stmt = stmt->down) {
1690
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1694
if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
1699
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1703
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1706
if (!MatchLabel(cx, ts, pn))
1709
label = pn->pn_atom;
1711
for (stmt2 = NULL; ; stmt = stmt->down) {
1713
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1714
JSMSG_LABEL_NOT_FOUND);
1717
if (stmt->type == STMT_LABEL) {
1718
if (stmt->label == label) {
1719
if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
1720
js_ReportCompileErrorNumber(cx, ts, NULL,
1722
JSMSG_BAD_CONTINUE);
1732
for (; ; stmt = stmt->down) {
1734
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1735
JSMSG_BAD_CONTINUE);
1738
if (STMT_IS_LOOP(stmt))
1743
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1747
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1750
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
1751
pn2 = Expr(cx, ts, tc);
1754
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
1757
js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
1758
pn2 = Statement(cx, ts, tc);
1761
js_PopStatement(tc);
1763
/* Deprecate after parsing, in case of WERROR option. */
1764
if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1765
JSREPORT_WARNING | JSREPORT_STRICT,
1766
JSMSG_DEPRECATED_USAGE,
1767
js_with_statement_str)) {
1771
pn->pn_pos.end = pn2->pn_pos.end;
1773
tc->flags |= TCF_FUN_HEAVYWEIGHT;
1777
pn = Variables(cx, ts, tc);
1781
/* Tell js_EmitTree to generate a final POP. */
1782
pn->pn_extra = JS_TRUE;
1786
if (!(tc->flags & TCF_IN_FUNCTION)) {
1787
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1791
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1795
/* This is ugly, but we don't want to require a semicolon. */
1796
ts->flags |= TSF_REGEXP;
1797
tt = js_PeekTokenSameLine(cx, ts);
1798
ts->flags &= ~TSF_REGEXP;
1799
if (tt == TOK_ERROR)
1802
if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1803
pn2 = Expr(cx, ts, tc);
1806
tc->flags |= TCF_RETURN_EXPR;
1807
pn->pn_pos.end = pn2->pn_pos.end;
1810
tc->flags |= TCF_RETURN_VOID;
1814
if (JS_HAS_STRICT_OPTION(cx) &&
1815
(~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {
1817
* We must be in a frame with a non-native function, because
1818
* we're compiling one.
1820
if (!ReportNoReturnValue(cx, ts))
1826
js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1827
pn = Statements(cx, ts, tc);
1831
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
1832
js_PopStatement(tc);
1837
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1840
pn->pn_type = TOK_SEMI;
1844
#if JS_HAS_DEBUGGER_KEYWORD
1846
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1849
pn->pn_type = TOK_DEBUGGER;
1850
tc->flags |= TCF_FUN_HEAVYWEIGHT;
1852
#endif /* JS_HAS_DEBUGGER_KEYWORD */
1859
pn2 = Expr(cx, ts, tc);
1863
if (js_PeekToken(cx, ts) == TOK_COLON) {
1864
if (pn2->pn_type != TOK_NAME) {
1865
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1869
label = pn2->pn_atom;
1870
for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1871
if (stmt->type == STMT_LABEL && stmt->label == label) {
1872
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1873
JSMSG_DUPLICATE_LABEL);
1877
(void) js_GetToken(cx, ts);
1879
/* Push a label struct and parse the statement. */
1880
js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
1881
stmtInfo.label = label;
1882
pn = Statement(cx, ts, tc);
1886
/* Pop the label, set pn_expr, and return early. */
1887
js_PopStatement(tc);
1888
pn2->pn_type = TOK_COLON;
1889
pn2->pn_pos.end = pn->pn_pos.end;
1894
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1897
pn->pn_type = TOK_SEMI;
1898
pn->pn_pos = pn2->pn_pos;
1903
/* Check termination of this primitive statement. */
1904
if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
1905
tt = js_PeekTokenSameLine(cx, ts);
1906
if (tt == TOK_ERROR)
1908
if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1909
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1910
JSMSG_SEMI_BEFORE_STMNT);
1915
(void) js_MatchToken(cx, ts, TOK_SEMI);
1919
static JSParseNode *
1920
Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1922
JSParseNode *pn, *pn2;
1923
JSObject *obj, *pobj;
1927
JSPropertyOp getter, setter, currentGetter, currentSetter;
1929
JSAtomListElement *ale;
1932
JSScopeProperty *sprop;
1936
* The tricky part of this code is to create special parsenode opcodes for
1937
* getting and setting variables (which will be stored as special slots in
1938
* the frame). The complex special case is an eval() inside a function.
1939
* If the evaluated string references variables in the enclosing function,
1940
* then we need to generate the special variable opcodes. We determine
1941
* this by looking up the variable id in the current variable scope.
1943
JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);
1944
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1947
pn->pn_op = CURRENT_TOKEN(ts).t_op;
1948
pn->pn_extra = JS_FALSE; /* assume no JSOP_POP needed */
1952
* Skip eval and debugger frames when looking for the function whose code
1953
* is being compiled. If we are called from FunctionBody, TCF_IN_FUNCTION
1954
* will be set in tc->flags, and we can be sure fp->fun is the function to
1955
* use. But if a function calls eval, the string argument is treated as a
1956
* Program (per ECMA), so TCF_IN_FUNCTION won't be set.
1958
* What's more, when the following code is reached from eval, cx->fp->fun
1959
* is eval's JSFunction (a native function), so we need to skip its frame.
1960
* We should find the scripted caller's function frame just below it, but
1961
* we code a loop out of paranoia.
1963
for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down)
1967
clasp = OBJ_GET_CLASS(cx, obj);
1968
if (fun && clasp == &js_FunctionClass) {
1969
/* We are compiling code inside a function */
1970
getter = js_GetLocalVariable;
1971
setter = js_SetLocalVariable;
1972
} else if (fun && clasp == &js_CallClass) {
1973
/* We are compiling code from an eval inside a function */
1974
getter = js_GetCallVariable;
1975
setter = js_SetCallVariable;
1977
getter = clasp->getProperty;
1978
setter = clasp->setProperty;
1983
currentGetter = getter;
1984
currentSetter = setter;
1985
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
1986
atom = CURRENT_TOKEN(ts).t_atom;
1988
ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1990
prevop = ALE_JSOP(ale);
1991
if (JS_HAS_STRICT_OPTION(cx) ||
1992
pn->pn_op == JSOP_DEFCONST ||
1993
prevop == JSOP_DEFCONST) {
1994
const char *name = js_AtomToPrintableString(cx, atom);
1996
!js_ReportCompileErrorNumber(cx, ts, NULL,
1997
(pn->pn_op != JSOP_DEFCONST &&
1998
prevop != JSOP_DEFCONST)
1999
? JSREPORT_WARNING |
2002
JSMSG_REDECLARED_VAR,
2003
(prevop == JSOP_DEFFUN ||
2004
prevop == JSOP_CLOSURE)
2006
: (prevop == JSOP_DEFCONST)
2013
if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
2014
tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
2016
ale = js_IndexAtom(cx, atom, &tc->decls);
2020
ALE_SET_JSOP(ale, pn->pn_op);
2022
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2025
pn2->pn_op = JSOP_NAME;
2026
pn2->pn_atom = atom;
2027
pn2->pn_expr = NULL;
2029
pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)
2030
? JSPROP_ENUMERATE | JSPROP_PERMANENT |
2032
: JSPROP_ENUMERATE | JSPROP_PERMANENT;
2035
if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))
2038
OBJ_IS_NATIVE(pobj) &&
2039
(sprop = (JSScopeProperty *)prop) != NULL) {
2040
if (sprop->getter == js_GetArgument) {
2041
const char *name = js_AtomToPrintableString(cx, atom);
2044
} else if (pn->pn_op == JSOP_DEFCONST) {
2045
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2046
JSMSG_REDECLARED_PARAM,
2050
currentGetter = js_GetArgument;
2051
currentSetter = js_SetArgument;
2052
ok = js_ReportCompileErrorNumber(cx, ts, NULL,
2055
JSMSG_VAR_HIDES_ARG,
2060
/* Not an argument, must be a redeclared local var. */
2061
if (clasp == &js_FunctionClass) {
2062
JS_ASSERT(sprop->getter == js_GetLocalVariable);
2063
JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2064
sprop->shortid < fun->nvars);
2065
} else if (clasp == &js_CallClass) {
2066
if (sprop->getter == js_GetCallVariable) {
2068
* Referencing a variable introduced by a var
2069
* statement in the enclosing function. Check
2070
* that the slot number we have is in range.
2072
JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2073
sprop->shortid < fun->nvars);
2076
* A variable introduced through another eval:
2077
* don't use the special getters and setters
2078
* since we can't allocate a slot in the frame.
2080
currentGetter = sprop->getter;
2081
currentSetter = sprop->setter;
2085
/* Override the old getter and setter, to handle eval. */
2086
sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2096
* Property not found in current variable scope: we have not seen
2097
* this variable before. Define a new local variable by adding a
2098
* property to the function's scope, allocating one slot in the
2099
* function's frame. Global variables and any locals declared in
2100
* with statement bodies are handled at runtime, by script prolog
2101
* JSOP_DEFVAR bytecodes generated for slot-less vars.
2105
OBJ_DROP_PROPERTY(cx, pobj, prop);
2108
if (currentGetter == js_GetCallVariable) {
2109
/* Can't increase fun->nvars in an active frame! */
2110
currentGetter = clasp->getProperty;
2111
currentSetter = clasp->setProperty;
2113
if (currentGetter == js_GetLocalVariable &&
2114
atom != cx->runtime->atomState.argumentsAtom &&
2115
fp->scopeChain == obj &&
2116
!js_InWithStatement(tc)) {
2117
if (!js_AddNativeProperty(cx, obj, (jsid)atom,
2118
currentGetter, currentSetter,
2120
pn2->pn_attrs | JSPROP_SHARED,
2121
SPROP_HAS_SHORTID, fun->nvars)) {
2128
if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
2129
if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2130
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2131
JSMSG_BAD_VAR_INIT);
2134
pn2->pn_expr = AssignExpr(cx, ts, tc);
2135
if (!pn2->pn_expr) {
2138
pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)
2141
if (atom == cx->runtime->atomState.argumentsAtom)
2142
tc->flags |= TCF_FUN_HEAVYWEIGHT;
2148
OBJ_DROP_PROPERTY(cx, pobj, prop);
2151
} while (js_MatchToken(cx, ts, TOK_COMMA));
2153
pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2157
static JSParseNode *
2158
Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2160
JSParseNode *pn, *pn2;
2162
pn = AssignExpr(cx, ts, tc);
2163
if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
2164
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2167
pn2->pn_pos.begin = pn->pn_pos.begin;
2168
PN_INIT_LIST_1(pn2, pn);
2171
pn2 = AssignExpr(cx, ts, tc);
2175
} while (js_MatchToken(cx, ts, TOK_COMMA));
2176
pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2181
static JSParseNode *
2182
AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2184
JSParseNode *pn, *pn2;
2190
pn = CondExpr(cx, ts, tc);
2194
tt = js_GetToken(cx, ts);
2195
#if JS_HAS_GETTER_SETTER
2196
if (tt == TOK_NAME) {
2197
tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
2198
if (tt == TOK_ERROR)
2202
if (tt != TOK_ASSIGN) {
2207
op = CURRENT_TOKEN(ts).t_op;
2208
for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
2210
switch (pn2->pn_type) {
2212
pn2->pn_op = JSOP_SETNAME;
2213
if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2214
tc->flags |= TCF_FUN_HEAVYWEIGHT;
2217
pn2->pn_op = JSOP_SETPROP;
2220
pn2->pn_op = JSOP_SETELEM;
2222
#if JS_HAS_LVALUE_RETURN
2224
pn2->pn_op = JSOP_SETCALL;
2228
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2229
JSMSG_BAD_LEFTSIDE_OF_ASS);
2232
pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
2236
static JSParseNode *
2237
CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2239
JSParseNode *pn, *pn1, *pn2, *pn3;
2240
#if JS_HAS_IN_OPERATOR
2242
#endif /* JS_HAS_IN_OPERATOR */
2244
pn = OrExpr(cx, ts, tc);
2245
if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
2247
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
2250
#if JS_HAS_IN_OPERATOR
2252
* Always accept the 'in' operator in the middle clause of a ternary,
2253
* where it's unambiguous, even if we might be parsing the init of a
2256
oldflags = tc->flags;
2257
tc->flags &= ~TCF_IN_FOR_INIT;
2258
#endif /* JS_HAS_IN_OPERATOR */
2259
pn2 = AssignExpr(cx, ts, tc);
2260
#if JS_HAS_IN_OPERATOR
2261
tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
2262
#endif /* JS_HAS_IN_OPERATOR */
2266
MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
2267
pn3 = AssignExpr(cx, ts, tc);
2270
pn->pn_pos.begin = pn1->pn_pos.begin;
2271
pn->pn_pos.end = pn3->pn_pos.end;
2279
static JSParseNode *
2280
OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2284
pn = AndExpr(cx, ts, tc);
2285
if (pn && js_MatchToken(cx, ts, TOK_OR))
2286
pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
2290
static JSParseNode *
2291
AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2295
pn = BitOrExpr(cx, ts, tc);
2296
if (pn && js_MatchToken(cx, ts, TOK_AND))
2297
pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
2301
static JSParseNode *
2302
BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2306
pn = BitXorExpr(cx, ts, tc);
2307
while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
2308
pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
2314
static JSParseNode *
2315
BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2319
pn = BitAndExpr(cx, ts, tc);
2320
while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
2321
pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
2327
static JSParseNode *
2328
BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2332
pn = EqExpr(cx, ts, tc);
2333
while (pn && js_MatchToken(cx, ts, TOK_BITAND))
2334
pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
2338
static JSParseNode *
2339
EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2344
pn = RelExpr(cx, ts, tc);
2345
while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
2346
op = CURRENT_TOKEN(ts).t_op;
2347
pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
2352
static JSParseNode *
2353
RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2358
#if JS_HAS_IN_OPERATOR
2359
uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
2362
* Uses of the in operator in ShiftExprs are always unambiguous,
2363
* so unset the flag that prohibits recognizing it.
2365
tc->flags &= ~TCF_IN_FOR_INIT;
2366
#endif /* JS_HAS_IN_OPERATOR */
2368
pn = ShiftExpr(cx, ts, tc);
2370
(js_MatchToken(cx, ts, TOK_RELOP)
2371
#if JS_HAS_IN_OPERATOR
2373
* Recognize the 'in' token as an operator only if we're not
2374
* currently in the init expr of a for loop.
2376
|| (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))
2377
#endif /* JS_HAS_IN_OPERATOR */
2378
#if JS_HAS_INSTANCEOF
2379
|| js_MatchToken(cx, ts, TOK_INSTANCEOF)
2380
#endif /* JS_HAS_INSTANCEOF */
2382
tt = CURRENT_TOKEN(ts).type;
2383
op = CURRENT_TOKEN(ts).t_op;
2384
pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
2386
#if JS_HAS_IN_OPERATOR
2387
/* Restore previous state of inForInit flag. */
2388
tc->flags |= inForInitFlag;
2389
#endif /* JS_HAS_IN_OPERATOR */
2394
static JSParseNode *
2395
ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2400
pn = AddExpr(cx, ts, tc);
2401
while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
2402
op = CURRENT_TOKEN(ts).t_op;
2403
pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
2408
static JSParseNode *
2409
AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2415
pn = MulExpr(cx, ts, tc);
2417
(js_MatchToken(cx, ts, TOK_PLUS) ||
2418
js_MatchToken(cx, ts, TOK_MINUS))) {
2419
tt = CURRENT_TOKEN(ts).type;
2420
op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
2421
pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
2426
static JSParseNode *
2427
MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2433
pn = UnaryExpr(cx, ts, tc);
2435
(js_MatchToken(cx, ts, TOK_STAR) ||
2436
js_MatchToken(cx, ts, TOK_DIVOP))) {
2437
tt = CURRENT_TOKEN(ts).type;
2438
op = CURRENT_TOKEN(ts).t_op;
2439
pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
2444
static JSParseNode *
2445
SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
2448
while (kid->pn_type == TOK_RP)
2450
if (kid->pn_type != TOK_NAME &&
2451
kid->pn_type != TOK_DOT &&
2452
#if JS_HAS_LVALUE_RETURN
2453
(kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
2455
kid->pn_type != TOK_LB) {
2456
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2457
JSMSG_BAD_OPERAND, name);
2464
static const char *incop_name_str[] = {"increment", "decrement"};
2467
SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2468
JSParseNode *pn, JSParseNode *kid,
2469
JSTokenType tt, JSBool preorder)
2473
kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
2476
switch (kid->pn_type) {
2478
op = (tt == TOK_INC)
2479
? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
2480
: (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
2481
if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
2482
tc->flags |= TCF_FUN_HEAVYWEIGHT;
2486
op = (tt == TOK_INC)
2487
? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
2488
: (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
2491
#if JS_HAS_LVALUE_RETURN
2493
kid->pn_op = JSOP_SETCALL;
2496
op = (tt == TOK_INC)
2497
? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
2498
: (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
2509
static JSParseNode *
2510
UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2513
JSParseNode *pn, *pn2;
2515
ts->flags |= TSF_REGEXP;
2516
tt = js_GetToken(cx, ts);
2517
ts->flags &= ~TSF_REGEXP;
2523
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2526
pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
2527
pn->pn_op = CURRENT_TOKEN(ts).t_op;
2528
pn2 = UnaryExpr(cx, ts, tc);
2531
pn->pn_pos.end = pn2->pn_pos.end;
2537
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2540
pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
2543
if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
2545
pn->pn_pos.end = pn2->pn_pos.end;
2549
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2552
pn2 = UnaryExpr(cx, ts, tc);
2555
pn->pn_pos.end = pn2->pn_pos.end;
2558
* Under ECMA3, deleting any unary expression is valid -- it simply
2559
* returns true. Here we strip off any parentheses.
2561
while (pn2->pn_type == TOK_RP)
2571
pn = MemberExpr(cx, ts, tc, JS_TRUE);
2575
/* Don't look across a newline boundary for a postfix incop. */
2576
if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2577
tt = js_PeekTokenSameLine(cx, ts);
2578
if (tt == TOK_INC || tt == TOK_DEC) {
2579
(void) js_GetToken(cx, ts);
2580
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2583
if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
2585
pn2->pn_pos.begin = pn->pn_pos.begin;
2595
ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2596
JSParseNode *listNode)
2600
ts->flags |= TSF_REGEXP;
2601
matched = js_MatchToken(cx, ts, TOK_RP);
2602
ts->flags &= ~TSF_REGEXP;
2605
JSParseNode *argNode = AssignExpr(cx, ts, tc);
2608
PN_APPEND(listNode, argNode);
2609
} while (js_MatchToken(cx, ts, TOK_COMMA));
2611
if (js_GetToken(cx, ts) != TOK_RP) {
2612
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2613
JSMSG_PAREN_AFTER_ARGS);
2620
static JSParseNode *
2621
MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2622
JSBool allowCallSyntax)
2624
JSParseNode *pn, *pn2, *pn3;
2629
/* Check for new expression first. */
2630
ts->flags |= TSF_REGEXP;
2631
tt = js_PeekToken(cx, ts);
2632
ts->flags &= ~TSF_REGEXP;
2633
if (tt == TOK_NEW) {
2634
(void) js_GetToken(cx, ts);
2636
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2639
pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
2642
pn->pn_op = JSOP_NEW;
2643
PN_INIT_LIST_1(pn, pn2);
2644
pn->pn_pos.begin = pn2->pn_pos.begin;
2646
if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
2648
if (pn->pn_count > ARGC_LIMIT) {
2649
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2650
JSMSG_TOO_MANY_CON_ARGS);
2653
pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2655
pn = PrimaryExpr(cx, ts, tc);
2660
while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
2661
if (tt == TOK_DOT) {
2662
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2665
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
2666
pn2->pn_pos.begin = pn->pn_pos.begin;
2667
pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2668
pn2->pn_op = JSOP_GETPROP;
2670
pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
2671
} else if (tt == TOK_LB) {
2672
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
2675
pn3 = Expr(cx, ts, tc);
2679
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
2680
pn2->pn_pos.begin = pn->pn_pos.begin;
2681
pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2683
/* Optimize o['p'] to o.p by rewriting pn2. */
2684
if (pn3->pn_type == TOK_STRING) {
2685
pn2->pn_type = TOK_DOT;
2686
pn2->pn_op = JSOP_GETPROP;
2687
pn2->pn_arity = PN_NAME;
2689
pn2->pn_atom = pn3->pn_atom;
2691
pn2->pn_op = JSOP_GETELEM;
2693
pn2->pn_right = pn3;
2695
} else if (allowCallSyntax && tt == TOK_LP) {
2696
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2700
/* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
2701
pn2->pn_op = JSOP_CALL;
2702
if (pn->pn_op == JSOP_NAME &&
2703
pn->pn_atom == cx->runtime->atomState.evalAtom) {
2704
pn2->pn_op = JSOP_EVAL;
2705
tc->flags |= TCF_FUN_HEAVYWEIGHT;
2708
PN_INIT_LIST_1(pn2, pn);
2709
pn2->pn_pos.begin = pn->pn_pos.begin;
2711
if (!ArgumentList(cx, ts, tc, pn2))
2713
if (pn2->pn_count > ARGC_LIMIT) {
2714
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2715
JSMSG_TOO_MANY_FUN_ARGS);
2718
pn2->pn_pos.end = PN_LAST(pn2)->pn_pos.end;
2726
if (tt == TOK_ERROR)
2731
static JSParseNode *
2732
PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2735
JSParseNode *pn, *pn2, *pn3;
2737
#if JS_HAS_GETTER_SETTER
2742
#if JS_HAS_SHARP_VARS
2743
JSParseNode *defsharp;
2747
notsharp = JS_FALSE;
2750
* Control flows here after #n= is scanned. If the following primary is
2751
* not valid after such a "sharp variable" definition, the token type case
2752
* should set notsharp.
2758
ts->flags |= TSF_REGEXP;
2759
tt = js_GetToken(cx, ts);
2760
ts->flags &= ~TSF_REGEXP;
2762
#if JS_HAS_GETTER_SETTER
2763
if (tt == TOK_NAME) {
2764
tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
2765
if (tt == TOK_ERROR)
2771
#if JS_HAS_LEXICAL_CLOSURE
2773
pn = FunctionExpr(cx, ts, tc);
2779
#if JS_HAS_INITIALIZERS
2785
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2788
pn->pn_type = TOK_RB;
2789
pn->pn_extra = JS_FALSE;
2791
#if JS_HAS_SHARP_VARS
2793
PN_INIT_LIST_1(pn, defsharp);
2799
ts->flags |= TSF_REGEXP;
2800
matched = js_MatchToken(cx, ts, TOK_RB);
2801
ts->flags &= ~TSF_REGEXP;
2803
for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) {
2804
ts->flags |= TSF_REGEXP;
2805
tt = js_PeekToken(cx, ts);
2806
ts->flags &= ~TSF_REGEXP;
2808
pn->pn_extra = JS_TRUE;
2812
if (tt == TOK_COMMA) {
2813
/* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
2814
js_MatchToken(cx, ts, TOK_COMMA);
2815
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2817
pn2 = AssignExpr(cx, ts, tc);
2823
if (tt != TOK_COMMA) {
2824
/* If we didn't already match TOK_COMMA in above case. */
2825
if (!js_MatchToken(cx, ts, TOK_COMMA))
2830
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
2832
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2837
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2840
pn->pn_type = TOK_RC;
2842
#if JS_HAS_SHARP_VARS
2844
PN_INIT_LIST_1(pn, defsharp);
2850
if (!js_MatchToken(cx, ts, TOK_RC)) {
2854
tt = js_GetToken(cx, ts);
2857
pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2859
pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
2862
#if JS_HAS_GETTER_SETTER
2863
atom = CURRENT_TOKEN(ts).t_atom;
2865
if (atom == rt->atomState.getAtom ||
2866
atom == rt->atomState.setAtom) {
2867
op = (atom == rt->atomState.getAtom)
2870
if (js_MatchToken(cx, ts, TOK_NAME)) {
2871
pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME,
2875
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2876
pn3->pn_expr = NULL;
2878
/* We have to fake a 'function' token here. */
2879
CURRENT_TOKEN(ts).t_op = JSOP_NOP;
2880
CURRENT_TOKEN(ts).type = TOK_FUNCTION;
2881
pn2 = FunctionExpr(cx, ts, tc);
2882
pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
2886
/* else fall thru ... */
2889
pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2891
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2894
if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2897
JSMSG_TRAILING_COMMA)) {
2902
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2907
tt = js_GetToken(cx, ts);
2908
#if JS_HAS_GETTER_SETTER
2909
if (tt == TOK_NAME) {
2910
tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
2911
if (tt == TOK_ERROR)
2915
if (tt != TOK_COLON) {
2916
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2917
JSMSG_COLON_AFTER_ID);
2920
op = CURRENT_TOKEN(ts).t_op;
2921
pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
2923
#if JS_HAS_GETTER_SETTER
2929
} while (js_MatchToken(cx, ts, TOK_COMMA));
2931
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
2934
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2937
#if JS_HAS_SHARP_VARS
2941
defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2944
defsharp->pn_kid = NULL;
2945
defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
2949
/* Check for forward/dangling references at runtime, to allow eval. */
2950
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2953
pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
2956
#endif /* JS_HAS_SHARP_VARS */
2957
#endif /* JS_HAS_INITIALIZERS */
2961
#if JS_HAS_IN_OPERATOR
2964
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2967
#if JS_HAS_IN_OPERATOR
2969
* Always accept the 'in' operator in a parenthesized expression,
2970
* where it's unambiguous, even if we might be parsing the init of a
2973
oldflags = tc->flags;
2974
tc->flags &= ~TCF_IN_FOR_INIT;
2976
pn2 = Expr(cx, ts, tc);
2977
#if JS_HAS_IN_OPERATOR
2978
tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
2983
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
2984
pn->pn_type = TOK_RP;
2985
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2991
#if JS_HAS_SHARP_VARS
2997
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3000
pn->pn_op = CURRENT_TOKEN(ts).t_op;
3001
pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
3002
if (tt == TOK_NAME) {
3003
pn->pn_arity = PN_NAME;
3008
/* Unqualified __parent__ and __proto__ uses require activations. */
3009
if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
3010
pn->pn_atom == cx->runtime->atomState.protoAtom) {
3011
tc->flags |= TCF_FUN_HEAVYWEIGHT;
3017
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3020
pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
3021
#if JS_HAS_SHARP_VARS
3027
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3030
pn->pn_op = CURRENT_TOKEN(ts).t_op;
3031
#if JS_HAS_SHARP_VARS
3036
#if !JS_HAS_EXPORT_IMPORT
3041
badWord = js_DeflateString(cx, CURRENT_TOKEN(ts).ptr,
3042
(size_t) CURRENT_TOKEN(ts).pos.end.index
3043
- CURRENT_TOKEN(ts).pos.begin.index);
3044
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3045
JSMSG_RESERVED_ID, badWord);
3046
JS_free(cx, badWord);
3050
/* The scanner or one of its subroutines reported the error. */
3054
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3055
JSMSG_SYNTAX_ERROR);
3059
#if JS_HAS_SHARP_VARS
3063
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3064
JSMSG_BAD_SHARP_VAR_DEF);
3067
defsharp->pn_kid = pn;
3075
ContainsVarStmt(JSParseNode *pn)
3081
switch (pn->pn_arity) {
3083
if (pn->pn_type == TOK_VAR)
3085
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3086
if (ContainsVarStmt(pn2))
3091
return ContainsVarStmt(pn->pn_kid1) ||
3092
ContainsVarStmt(pn->pn_kid2) ||
3093
ContainsVarStmt(pn->pn_kid3);
3096
* Limit recursion if pn is a binary expression, which can't contain a
3099
if (pn->pn_op != JSOP_NOP)
3101
return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
3103
if (pn->pn_op != JSOP_NOP)
3105
return ContainsVarStmt(pn->pn_kid);
3112
* Fold from one constant type to another.
3113
* XXX handles only strings and numbers for now
3116
FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
3118
if (pn->pn_type != type) {
3121
if (pn->pn_type == TOK_STRING) {
3123
if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
3126
pn->pn_type = TOK_NUMBER;
3127
pn->pn_op = JSOP_NUMBER;
3132
if (pn->pn_type == TOK_NUMBER) {
3133
JSString *str = js_NumberToString(cx, pn->pn_dval);
3136
pn->pn_atom = js_AtomizeString(cx, str, 0);
3139
pn->pn_type = TOK_STRING;
3140
pn->pn_op = JSOP_STRING;
3151
* Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
3152
* one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
3153
* a successful call to this function.
3156
FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
3157
JSParseNode *pn, JSTreeContext *tc)
3163
JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
3169
if (!js_DoubleToECMAInt32(cx, d, &i))
3171
if (!js_DoubleToECMAInt32(cx, d2, &j))
3174
d = (op == JSOP_LSH) ? i << j : i >> j;
3178
if (!js_DoubleToECMAUint32(cx, d, &u))
3180
if (!js_DoubleToECMAInt32(cx, d2, &j))
3201
/* XXX MSVC miscompiles such that (NaN == 0) */
3202
if (JSDOUBLE_IS_NaN(d2))
3203
d = *cx->runtime->jsNaN;
3206
if (d == 0 || JSDOUBLE_IS_NaN(d))
3207
d = *cx->runtime->jsNaN;
3208
else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
3209
d = *cx->runtime->jsNegativeInfinity;
3211
d = *cx->runtime->jsPositiveInfinity;
3219
d = *cx->runtime->jsNaN;
3222
/* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
3223
if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
3232
/* Take care to allow pn1 or pn2 to alias pn. */
3234
RecycleTree(pn1, tc);
3236
RecycleTree(pn2, tc);
3237
pn->pn_type = TOK_NUMBER;
3238
pn->pn_op = JSOP_NUMBER;
3239
pn->pn_arity = PN_NULLARY;
3245
js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3247
JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
3250
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
3251
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
3255
switch (pn->pn_arity) {
3257
if (!js_FoldConstants(cx, pn->pn_body, tc))
3262
/* Save the list head in pn1 for later use. */
3263
for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3264
if (!js_FoldConstants(cx, pn2, tc))
3270
/* Any kid may be null (e.g. for (;;)). */
3274
if (pn1 && !js_FoldConstants(cx, pn1, tc))
3276
if (pn2 && !js_FoldConstants(cx, pn2, tc))
3278
if (pn3 && !js_FoldConstants(cx, pn3, tc))
3283
/* First kid may be null (for default case in switch). */
3286
if (pn1 && !js_FoldConstants(cx, pn1, tc))
3288
if (!js_FoldConstants(cx, pn2, tc))
3293
/* Our kid may be null (e.g. return; vs. return e;). */
3295
if (pn1 && !js_FoldConstants(cx, pn1, tc))
3301
* Skip pn1 down along a chain of dotted member expressions to avoid
3302
* excessive recursion. Our only goal here is to fold constants (if
3303
* any) in the primary expression operand to the left of the first
3307
while (pn1 && pn1->pn_arity == PN_NAME)
3309
if (pn1 && !js_FoldConstants(cx, pn1, tc))
3317
switch (pn->pn_type) {
3319
if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
3324
/* Reduce 'if (C) T; else E' into T for true C, E for false. */
3325
switch (pn1->pn_type) {
3327
if (pn1->pn_dval == 0)
3331
if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
3335
if (pn1->pn_op == JSOP_TRUE)
3337
if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
3343
/* Early return to dodge common code that copies pn2 to pn. */
3348
/* pn2 is the then- or else-statement subtree to compile. */
3349
PN_MOVE_NODE(pn, pn2);
3351
/* False condition and no else: make pn an empty statement. */
3352
pn->pn_type = TOK_SEMI;
3353
pn->pn_arity = PN_UNARY;
3356
RecycleTree(pn2, tc);
3357
if (pn3 && pn3 != pn2)
3358
RecycleTree(pn3, tc);
3362
if (pn->pn_arity == PN_LIST) {
3363
size_t length, length2;
3365
JSString *str, *str2;
3368
* Any string literal term with all others number or string means
3369
* this is a concatenation. If any term is not a string or number
3370
* literal, we can't fold.
3372
JS_ASSERT(pn->pn_count > 2);
3373
if (pn->pn_extra & PNX_CANTFOLD)
3375
if (pn->pn_extra != PNX_STRCAT)
3378
/* Ok, we're concatenating: convert non-string constant operands. */
3380
for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
3381
if (!FoldType(cx, pn2, TOK_STRING))
3383
/* XXX fold only if all operands convert to string */
3384
if (pn2->pn_type != TOK_STRING)
3386
length += ATOM_TO_STRING(pn2->pn_atom)->length;
3389
/* Allocate a new buffer and string descriptor for the result. */
3390
chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
3393
str = js_NewString(cx, chars, length, 0);
3399
/* Fill the buffer, advancing chars and recycling kids as we go. */
3400
for (pn2 = pn1; pn2; pn2 = pn3) {
3401
str2 = ATOM_TO_STRING(pn2->pn_atom);
3402
length2 = str2->length;
3403
js_strncpy(chars, str2->chars, length2);
3406
RecycleTree(pn2, tc);
3410
/* Atomize the result string and mutate pn to refer to it. */
3411
pn->pn_atom = js_AtomizeString(cx, str, 0);
3414
pn->pn_type = TOK_STRING;
3415
pn->pn_op = JSOP_STRING;
3416
pn->pn_arity = PN_NULLARY;
3420
/* Handle a binary string concatenation. */
3421
JS_ASSERT(pn->pn_arity == PN_BINARY);
3422
if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
3423
JSString *left, *right, *str;
3425
if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
3429
if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
3431
left = ATOM_TO_STRING(pn1->pn_atom);
3432
right = ATOM_TO_STRING(pn2->pn_atom);
3433
str = js_ConcatStrings(cx, left, right);
3436
pn->pn_atom = js_AtomizeString(cx, str, 0);
3439
pn->pn_type = TOK_STRING;
3440
pn->pn_op = JSOP_STRING;
3441
pn->pn_arity = PN_NULLARY;
3442
RecycleTree(pn1, tc);
3443
RecycleTree(pn2, tc);
3447
/* Can't concatenate string literals, let's try numbers. */
3451
/* The * in 'import *;' parses as a nullary star node. */
3452
if (pn->pn_arity == PN_NULLARY)
3460
if (pn->pn_arity == PN_LIST) {
3461
JS_ASSERT(pn->pn_count > 2);
3462
for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
3463
if (!FoldType(cx, pn2, TOK_NUMBER))
3465
/* XXX fold only if all operands convert to number */
3466
if (pn2->pn_type != TOK_NUMBER)
3470
JSOp op = pn->pn_op;
3474
if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
3476
while ((pn2 = pn3) != NULL) {
3478
if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
3483
JS_ASSERT(pn->pn_arity == PN_BINARY);
3484
if (!FoldType(cx, pn1, TOK_NUMBER) ||
3485
!FoldType(cx, pn2, TOK_NUMBER)) {
3488
if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
3489
if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
3496
if (pn1->pn_type == TOK_NUMBER) {
3500
/* Operate on one numeric constant. */
3502
switch (pn->pn_op) {
3504
if (!js_DoubleToECMAInt32(cx, d, &i))
3512
* Negation of a zero doesn't produce a negative
3513
* zero on HPUX. Perform the operation by bit
3516
JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
3526
pn->pn_type = TOK_PRIMARY;
3527
pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
3528
pn->pn_arity = PN_NULLARY;
3532
/* Return early to dodge the common TOK_NUMBER code. */
3535
pn->pn_type = TOK_NUMBER;
3536
pn->pn_op = JSOP_NUMBER;
3537
pn->pn_arity = PN_NULLARY;
3539
RecycleTree(pn1, tc);