1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
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/
10  *
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
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
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.
23  *
24  * Contributor(s):
25  *
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.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 /*
41  * JS lexical scanner.
42  */
43 #include "jsstddef.h"
44 #include <stdio.h>      /* first to avoid trouble on some systems */
45 #include <errno.h>
46 #include <limits.h>
47 #include <math.h>
48 #ifdef HAVE_MEMORY_H
49 #include <memory.h>
50 #endif
51 #include <stdarg.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include "jstypes.h"
55 #include "jsarena.h" /* Added by JSIFY */
56 #include "jsutil.h" /* Added by JSIFY */
57 #include "jsdtoa.h"
58 #include "jsprf.h"
59 #include "jsapi.h"
60 #include "jsatom.h"
61 #include "jscntxt.h"
62 #include "jsconfig.h"
63 #include "jsemit.h"
64 #include "jsexn.h"
65 #include "jsnum.h"
66 #include "jsopcode.h"
67 #include "jsregexp.h"
68 #include "jsscan.h"
69
70 #define RESERVE_JAVA_KEYWORDS
71 #define RESERVE_ECMA_KEYWORDS
72
73 static struct keyword {
74     const char  *name;
75     JSTokenType tokentype;      /* JSTokenType */
76     JSOp        op;             /* JSOp */
77     JSVersion   version;        /* JSVersion */
78 } keywords[] = {
79     {"break",           TOK_BREAK,              JSOP_NOP,   JSVERSION_DEFAULT},
80     {"case",            TOK_CASE,               JSOP_NOP,   JSVERSION_DEFAULT},
81     {"continue",        TOK_CONTINUE,           JSOP_NOP,   JSVERSION_DEFAULT},
82     {"default",         TOK_DEFAULT,            JSOP_NOP,   JSVERSION_DEFAULT},
83     {js_delete_str,     TOK_DELETE,             JSOP_NOP,   JSVERSION_DEFAULT},
84     {"do",              TOK_DO,                 JSOP_NOP,   JSVERSION_DEFAULT},
85     {"else",            TOK_ELSE,               JSOP_NOP,   JSVERSION_DEFAULT},
86     {"export",          TOK_EXPORT,             JSOP_NOP,   JSVERSION_1_2},
87     {js_false_str,      TOK_PRIMARY,            JSOP_FALSE, JSVERSION_DEFAULT},
88     {"for",             TOK_FOR,                JSOP_NOP,   JSVERSION_DEFAULT},
89     {js_function_str,   TOK_FUNCTION,           JSOP_NOP,   JSVERSION_DEFAULT},
90     {"if",              TOK_IF,                 JSOP_NOP,   JSVERSION_DEFAULT},
91     {js_in_str,         TOK_IN,                 JSOP_IN,    JSVERSION_DEFAULT},
92     {js_new_str,        TOK_NEW,                JSOP_NEW,   JSVERSION_DEFAULT},
93     {js_null_str,       TOK_PRIMARY,            JSOP_NULL,  JSVERSION_DEFAULT},
94     {"return",          TOK_RETURN,             JSOP_NOP,   JSVERSION_DEFAULT},
95     {"switch",          TOK_SWITCH,             JSOP_NOP,   JSVERSION_DEFAULT},
96     {js_this_str,       TOK_PRIMARY,            JSOP_THIS,  JSVERSION_DEFAULT},
97     {js_true_str,       TOK_PRIMARY,            JSOP_TRUE,  JSVERSION_DEFAULT},
98     {js_typeof_str,     TOK_UNARYOP,            JSOP_TYPEOF,JSVERSION_DEFAULT},
99     {"var",             TOK_VAR,                JSOP_DEFVAR,JSVERSION_DEFAULT},
100     {js_void_str,       TOK_UNARYOP,            JSOP_VOID,  JSVERSION_DEFAULT},
101     {"while",           TOK_WHILE,              JSOP_NOP,   JSVERSION_DEFAULT},
102     {"with",            TOK_WITH,               JSOP_NOP,   JSVERSION_DEFAULT},
103
104 #if JS_HAS_CONST
105     {js_const_str,      TOK_VAR,                JSOP_DEFCONST,JSVERSION_DEFAULT},
106 #else
107     {js_const_str,      TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
108 #endif
109
110 #if JS_HAS_EXCEPTIONS
111     {"try",             TOK_TRY,                JSOP_NOP,   JSVERSION_DEFAULT},
112     {"catch",           TOK_CATCH,              JSOP_NOP,   JSVERSION_DEFAULT},
113     {"finally",         TOK_FINALLY,            JSOP_NOP,   JSVERSION_DEFAULT},
114     {"throw",           TOK_THROW,              JSOP_NOP,   JSVERSION_DEFAULT},
115 #else
116     {"try",             TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
117     {"catch",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
118     {"finally",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
119     {"throw",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
120 #endif
121
122 #if JS_HAS_INSTANCEOF
123     {js_instanceof_str, TOK_INSTANCEOF,         JSOP_INSTANCEOF,JSVERSION_1_4},
124 #else
125     {js_instanceof_str, TOK_RESERVED,           JSOP_NOP,   JSVERSION_1_4},
126 #endif
127
128 #ifdef RESERVE_JAVA_KEYWORDS
129     {"abstract",        TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
130     {"boolean",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
131     {"byte",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
132     {"char",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
133     {"class",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
134     {"double",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
135     {"extends",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
136     {"final",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
137     {"float",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
138     {"goto",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
139     {"implements",      TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
140     {"import",          TOK_IMPORT,             JSOP_NOP,   JSVERSION_DEFAULT},
141     {"int",             TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
142     {"interface",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
143     {"long",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
144     {"native",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
145     {"package",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
146     {"private",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
147     {"protected",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
148     {"public",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
149     {"short",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
150     {"static",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
151     {"super",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
152     {"synchronized",    TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
153     {"throws",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
154     {"transient",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
155     {"volatile",        TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
156 #endif
157
158 #ifdef RESERVE_ECMA_KEYWORDS
159     {"enum",           TOK_RESERVED,            JSOP_NOP,   JSVERSION_1_3},
160 #endif
161
162 #if JS_HAS_DEBUGGER_KEYWORD
163     {"debugger",       TOK_DEBUGGER,            JSOP_NOP,   JSVERSION_1_3},
164 #elif defined(RESERVE_ECMA_KEYWORDS)
165     {"debugger",       TOK_RESERVED,            JSOP_NOP,   JSVERSION_1_3},
166 #endif
167     {0,                TOK_EOF,                 JSOP_NOP,   JSVERSION_DEFAULT}
168 };
169
170 JSBool
171 js_InitScanner(JSContext *cx)
172 17 {
173 17     struct keyword *kw;
174 17     JSAtom *atom;
175
176 1020     for (kw = keywords; kw->name; kw++) {
177 1003         atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED);
178 1003         if (!atom)
179 0             return JS_FALSE;
180 1003         ATOM_SET_KEYWORD(atom, kw);
181     }
182 17     return JS_TRUE;
183 }
184
185 JS_FRIEND_API(void)
186 js_MapKeywords(void (*mapfun)(const char *))
187 0 {
188 0     struct keyword *kw;
189
190 0     for (kw = keywords; kw->name; kw++)
191 0         mapfun(kw->name);
192 }
193
194 JSTokenStream *
195 js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,
196                   const char *filename, uintN lineno,
197                   JSPrincipals *principals)
198 1248 {
199 1248     JSTokenStream *ts;
200
201 1248     ts = js_NewBufferTokenStream(cx, base, length);
202 1248     if (!ts)
203 0         return NULL;
204 1248     ts->filename = filename;
205 1248     ts->lineno = lineno;
206 1248     if (principals)
207 0         JSPRINCIPALS_HOLD(cx, principals);
208 1248     ts->principals = principals;
209 1248     return ts;
210 }
211
212 JS_FRIEND_API(JSTokenStream *)
213 js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
214 1248 {
215 1248     size_t nb;
216 1248     JSTokenStream *ts;
217
218 1248     nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);
219 1248     JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb);
220 1248     if (!ts) {
221 0         JS_ReportOutOfMemory(cx);
222 0         return NULL;
223     }
224 1248     memset(ts, 0, nb);
225 1248     ts->lineno = 1;
226 1248     ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);
227 1248     ts->userbuf.base = (jschar *)base;
228 1248     ts->userbuf.limit = (jschar *)base + length;
229 1248     ts->userbuf.ptr = (jschar *)base;
230 1248     ts->listener = cx->runtime->sourceHandler;
231 1248     ts->listenerData = cx->runtime->sourceHandlerData;
232 1248     return ts;
233 }
234
235 JS_FRIEND_API(JSTokenStream *)
236 js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp)
237 0 {
238 0     jschar *base;
239 0     JSTokenStream *ts;
240 0     FILE *file;
241
242 0     JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool,
243                            JS_LINE_LIMIT * sizeof(jschar));
244 0     if (!base)
245 0         return NULL;
246 0     ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT);
247 0     if (!ts)
248 0         return NULL;
249 0     if (!filename || strcmp(filename, "-") == 0) {
250 0         file = defaultfp;
251     } else {
252 0         file = fopen(filename, "r");
253 0         if (!file) {
254 0             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
255                                  filename, "No such file or directory");
256 0             return NULL;
257         }
258     }
259 0     ts->userbuf.ptr = ts->userbuf.limit;
260 0     ts->file = file;
261 0     ts->filename = filename;
262 0     return ts;
263 }
264
265 JS_FRIEND_API(JSBool)
266 js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)
267 1248 {
268 1248     if (ts->principals)
269 0         JSPRINCIPALS_DROP(cx, ts->principals);
270 1248     return !ts->file || fclose(ts->file) == 0;
271 }
272
273 static int
274 my_fgets(char *buf, int size, FILE *file)
275 0 {
276 0     int n, i, c;
277 0     JSBool crflag;
278
279 0     n = size - 1;
280 0     if (n < 0)
281 0         return -1;
282
283 0     crflag = JS_FALSE;
284 0     for (i = 0; i < n && (c = getc(file)) != EOF; i++) {
285 0         buf[i] = c;
286 0         if (c == '\n') {        /* any \n ends a line */
287 0             i++;                /* keep the \n; we know there is room for \0 */
288 0             break;
289         }
290 0         if (crflag) {           /* \r not followed by \n ends line at the \r */
291 0             ungetc(c, file);
292 0             break;              /* and overwrite c in buf with \0 */
293         }
294 0         crflag = (c == '\r');
295     }
296
297 0     buf[i] = '\0';
298 0     return i;
299 }
300
301 static int32
302 GetChar(JSTokenStream *ts)
303 20504879 {
304 20504879     int32 c;
305 20504879     ptrdiff_t i, j, len, olen;
306 20504879     JSBool crflag;
307 20504879     char cbuf[JS_LINE_LIMIT];
308 20504879     jschar *ubuf, *nl;
309
310 20504879     if (ts->ungetpos != 0) {
311 737861         c = ts->ungetbuf[--ts->ungetpos];
312     } else {
313 19767018         do {
314 19767018             if (ts->linebuf.ptr == ts->linebuf.limit) {
315 668728                 len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
316 668728                 if (len <= 0) {
317 1248                     if (!ts->file) {
318 1248                         ts->flags |= TSF_EOF;
319 1248                         return EOF;
320                     }
321
322                     /* Fill ts->userbuf so that \r and \r\n convert to \n. */
323 0                     crflag = (ts->flags & TSF_CRFLAG) != 0;
324 0                     len = my_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file);
325 0                     if (len <= 0) {
326 0                         ts->flags |= TSF_EOF;
327 0                         return EOF;
328                     }
329 0                     olen = len;
330 0                     ubuf = ts->userbuf.base;
331 0                     i = 0;
332 0                     if (crflag) {
333 0                         ts->flags &= ~TSF_CRFLAG;
334 0                         if (cbuf[0] != '\n') {
335 0                             ubuf[i++] = '\n';
336 0                             len++;
337 0                             ts->linepos--;
338                         }
339                     }
340 0                     for (j = 0; i < len; i++, j++)
341 0                         ubuf[i] = (jschar) (unsigned char) cbuf[j];
342 0                     ts->userbuf.limit = ubuf + len;
343 0                     ts->userbuf.ptr = ubuf;
344                 }
345 667480                 if (ts->listener) {
346 0                     ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len,
347                                  &ts->listenerTSData, ts->listenerData);
348                 }
349
350 667480                 nl = ts->saveEOL;
351 667480                 if (!nl) {
352                     /*
353                      * Any one of \n, \r, or \r\n ends a line (the longest
354                      * match wins).  Also allow the Unicode line and paragraph
355                      * separators.
356                      */
357 19765770                     for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) {
358                         /*
359                          * Try to prevent value-testing on most characters by
360                          * filtering out characters that aren't 000x or 202x.
361                          */
362 19765770                         if ((*nl & 0xDFD0) == 0) {
363 6757288                             if (*nl == '\n')
364 649137                                 break;
365 6108151                             if (*nl == '\r') {
366 1248                                 if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
367 1248                                     nl++;
368 1248                                 break;
369                             }
370 6106903                             if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR)
371 19115385                                 break;
372                         }
373                     }
374                 }
375
376                 /*
377                  * If there was a line terminator, copy thru it into linebuf.
378                  * Else copy JS_LINE_LIMIT-1 bytes into linebuf.
379                  */
380 667480                 if (nl < ts->userbuf.limit)
381 667480                     len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;
382 667480                 if (len >= JS_LINE_LIMIT) {
383 17095                     len = JS_LINE_LIMIT - 1;
384 17095                     ts->saveEOL = nl;
385                 } else {
386 650385                     ts->saveEOL = NULL;
387                 }
388 667480                 js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);
389 667480                 ts->userbuf.ptr += len;
390 667480                 olen = len;
391
392                 /*
393                  * Make sure linebuf contains \n for EOL (don't do this in
394                  * userbuf because the user's string might be readonly).
395                  */
396 667480                 if (nl < ts->userbuf.limit) {
397 667480                     if (*nl == '\r') {
398 0                         if (ts->linebuf.base[len-1] == '\r') {
399                             /*
400                              * Does the line segment end in \r?  We must check
401                              * for a \n at the front of the next segment before
402                              * storing a \n into linebuf.  This case matters
403                              * only when we're reading from a file.
404                              */
405 0                             if (nl + 1 == ts->userbuf.limit && ts->file) {
406 0                                 len--;
407 0                                 ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */
408 0                                 if (len == 0) {
409                                     /*
410                                      * This can happen when a segment ends in
411                                      * \r\r.  Start over.  ptr == limit in this
412                                      * case, so we'll fall into buffer-filling
413                                      * code.
414                                      */
415 0                                     return GetChar(ts);
416                                 }
417                             } else {
418 0                                 ts->linebuf.base[len-1] = '\n';
419                             }
420                         }
421 667480                     } else if (*nl == '\n') {
422 667480                         if (nl > ts->userbuf.base &&
423                             nl[-1] == '\r' &&
424                             ts->linebuf.base[len-2] == '\r') {
425 1248                             len--;
426 1248                             JS_ASSERT(ts->linebuf.base[len] == '\n');
427 1248                             ts->linebuf.base[len-1] = '\n';
428                         }
429 0                     } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) {
430 0                         ts->linebuf.base[len-1] = '\n';
431                     }
432                 }
433
434                 /* Reset linebuf based on adjusted segment length. */
435 667480                 ts->linebuf.limit = ts->linebuf.base + len;
436 667480                 ts->linebuf.ptr = ts->linebuf.base;
437
438                 /* Update position of linebuf within physical userbuf line. */
439 667480                 if (!(ts->flags & TSF_NLFLAG))
440 18343                     ts->linepos += ts->linelen;
441                 else
442 649137                     ts->linepos = 0;
443 667480                 if (ts->linebuf.limit[-1] == '\n')
444 650385                     ts->flags |= TSF_NLFLAG;
445                 else
446 17095                     ts->flags &= ~TSF_NLFLAG;
447
448                 /* Update linelen from original segment length. */
449 667480                 ts->linelen = olen;
450             }
451 19765770             c = *ts->linebuf.ptr++;
452 19765770         } while (JS_ISFORMAT(c));
453     }
454 20503631     if (c == '\n')
455 653775         ts->lineno++;
456 20503631     return c;
457 }
458
459 static void
460 UngetChar(JSTokenStream *ts, int32 c)
461 737861 {
462 737861     if (c == EOF)
463 0         return;
464 737861     JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
465 737861     if (c == '\n')
466 3390         ts->lineno--;
467 737861     ts->ungetbuf[ts->ungetpos++] = (jschar)c;
468 }
469
470 static int32
471 PeekChar(JSTokenStream *ts)
472 70450 {
473 70450     int32 c;
474
475 70450     c = GetChar(ts);
476 70450     UngetChar(ts, c);
477 70450     return c;
478 }
479
480 static JSBool
481 PeekChars(JSTokenStream *ts, intN n, jschar *cp)
482 0 {
483 0     intN i, j;
484 0     int32 c;
485
486 0     for (i = 0; i < n; i++) {
487 0         c = GetChar(ts);
488 0         if (c == EOF)
489 0             break;
490 0         cp[i] = (jschar)c;
491     }
492 0     for (j = i - 1; j >= 0; j--)
493 0         UngetChar(ts, cp[j]);
494 0     return i == n;
495 }
496
497 static void
498 SkipChars(JSTokenStream *ts, intN n)
499 0 {
500 0     while (--n >= 0)
501 0         GetChar(ts);
502 }
503
504 static JSBool
505 MatchChar(JSTokenStream *ts, int32 expect)
506 72387 {
507 72387     int32 c;
508
509 72387     c = GetChar(ts);
510 72387     if (c == expect)
511 20002         return JS_TRUE;
512 52385     UngetChar(ts, c);
513 52385     return JS_FALSE;
514 }
515
516 JSBool
517 js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts,
518                             JSCodeGenerator *cg, uintN flags,
519                             const uintN errorNumber, ...)
520 34 {
521 34     va_list ap;
522 34     JSErrorReporter onError;
523 34     JSErrorReport report;
524 34     jschar *tokenptr;
525 34     JSString *linestr = NULL;
526 34     char *message;
527 34     JSBool warning;
528
529 34     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
530 34         return JS_TRUE;
531
532 0     memset(&report, 0, sizeof (struct JSErrorReport));
533 0     report.flags = flags;
534 0     report.errorNumber = errorNumber;
535 0     message = NULL;
536
537 0     va_start(ap, errorNumber);
538 0     if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL,
539                                  errorNumber, &message, &report, &warning,
540                                  JS_TRUE, ap)) {
541 0         return JS_FALSE;
542     }
543 0     va_end(ap);
544
545 0     js_AddRoot(cx, &linestr, "error line buffer");
546
547 0     JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);
548 0     onError = cx->errorReporter;
549 0     if (onError) {
550         /* 
551          * We are typically called with non-null ts and null cg from jsparse.c.
552          * We can be called with null ts from the regexp compilation functions.
553          * The code generator (jsemit.c) may pass null ts and non-null cg.
554          */
555 0         if (ts) {
556 0             report.filename = ts->filename;
557 0             report.lineno = ts->lineno;
558 0             linestr = js_NewStringCopyN(cx, ts->linebuf.base,
559                                         ts->linebuf.limit - ts->linebuf.base,
560                                         0);
561 0             report.linebuf = linestr
562                 ? JS_GetStringBytes(linestr)
563                 : NULL;
564 0             tokenptr =
565                 ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].ptr;
566 0             report.tokenptr = linestr
567                 ? report.linebuf + (tokenptr - ts->linebuf.base)
568                 : NULL;
569 0             report.uclinebuf = linestr
570                 ? JS_GetStringChars(linestr)
571                 : NULL;
572 0             report.uctokenptr = linestr
573                 ? report.uclinebuf + (tokenptr - ts->linebuf.base)
574                 : NULL;
575 0         } else if (cg) {
576 0             report.filename = cg->filename;
577 0             report.lineno = CG_CURRENT_LINE(cg);
578         }
579
580 #if JS_HAS_ERROR_EXCEPTIONS
581         /*
582          * If there's a runtime exception type associated with this error
583          * number, set that as the pending exception.  For errors occuring at
584          * compile time, this is very likely to be a JSEXN_SYNTAXERR.
585          *
586          * If an exception is thrown but not caught, the JSREPORT_EXCEPTION
587          * flag will be set in report.flags.  Proper behavior for an error
588          * reporter is to ignore a report with this flag for all but top-level
589          * compilation errors.  The exception will remain pending, and so long
590          * as the non-top-level "load", "eval", or "compile" native function
591          * returns false, the top-level reporter will eventually receive the
592          * uncaught exception report.
593          * 
594          * XXX it'd probably be best if there was only one call to this
595          * function, but there seem to be two error reporter call points.
596          */
597
598         /*
599          * Only try to raise an exception if there isn't one already set -
600          * otherwise the exception will describe only the last compile error,
601          * which is likely spurious.
602          */
603 0         if (!(ts && (ts->flags & TSF_ERROR)))
604 0             if (js_ErrorToException(cx, message, &report))
605 0                 onError = NULL;
606
607         /*
608          * Suppress any compiletime errors that don't occur at the top level.
609          * This may still fail, as interplevel may be zero in contexts where we
610          * don't really want to call the error reporter, as when js is called
611          * by other code which could catch the error.
612          */
613 0         if (cx->interpLevel != 0)
614 0             onError = NULL;
615 #endif
616 0         if (cx->runtime->debugErrorHook && onError) {
617 0             JSDebugErrorHook hook = cx->runtime->debugErrorHook;
618             /* test local in case debugErrorHook changed on another thread */
619 0             if (hook && !hook(cx, message, &report,
620                               cx->runtime->debugErrorHookData)) {
621 0                 onError = NULL;
622             }
623         }
624 0         if (onError)
625 0             (*onError)(cx, message, &report);
626     }
627 0     if (message)
628 0         JS_free(cx, message);
629 0     if (report.messageArgs) {
630 0         int i = 0;
631 0         while (report.messageArgs[i])
632 0             JS_free(cx, (void *)report.messageArgs[i++]);
633 0         JS_free(cx, (void *)report.messageArgs);
634     }
635 0     if (report.ucmessage)
636 0         JS_free(cx, (void *)report.ucmessage);
637
638 0     js_RemoveRoot(cx->runtime, &linestr);
639
640 0     if (ts && !JSREPORT_IS_WARNING(flags)) {
641         /* Set the error flag to suppress spurious reports. */
642 0         ts->flags |= TSF_ERROR;
643     }
644 0     return warning;
645 }
646
647 JSTokenType
648 js_PeekToken(JSContext *cx, JSTokenStream *ts)
649 2205629 {
650 2205629     JSTokenType tt;
651
652 2205629     if (ts->lookahead != 0) {
653 1871806         tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type;
654     } else {
655 333823         tt = js_GetToken(cx, ts);
656 333823         js_UngetToken(ts);
657     }
658 2205629     return tt;
659 }
660
661 JSTokenType
662 js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)
663 937595 {
664 937595     JSTokenType tt;
665
666 937595     JS_ASSERT(ts->lookahead == 0 ||
667               ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos));
668 937595     ts->flags |= TSF_NEWLINES;
669 937595     tt = js_PeekToken(cx, ts);
670 937595     ts->flags &= ~TSF_NEWLINES;
671 937595     return tt;
672 }
673
674 #define TBMIN   64
675
676 static JSBool
677 GrowTokenBuf(JSContext *cx, JSTokenBuf *tb)
678 5228 {
679 5228     jschar *base;
680 5228     ptrdiff_t offset, length;
681 5228     size_t tbsize;
682 5228     JSArenaPool *pool;
683
684 5228     base = tb->base;
685 5228     offset = PTRDIFF(tb->ptr, base, jschar);
686 5228     pool = &cx->tempPool;
687 5228     if (!base) {
688 1248         tbsize = TBMIN * sizeof(jschar);
689 1248         length = TBMIN;
690 1248         JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize);
691     } else {
692 3980         length = PTRDIFF(tb->limit, base, jschar);
693 3980         tbsize = length * sizeof(jschar);
694 3980         length <<= 1;
695 3980         JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize);
696     }
697 5228     if (!base) {
698 0         JS_ReportOutOfMemory(cx);
699 0         return JS_FALSE;
700     }
701 5228     tb->base = base;
702 5228     tb->limit = base + length;
703 5228     tb->ptr = base + offset;
704 5228     return JS_TRUE;
705 }
706
707 static JSBool
708 AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c)
709 16566930 {
710 16566930     if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb))
711 0         return JS_FALSE;
712 16566930     *tb->ptr++ = c;
713 16566930     return JS_TRUE;
714 }
715
716 /*
717  * We have encountered a '\': check for a Unicode escape sequence after it,
718  * returning the character code value if we found a Unicode escape sequence.
719  * Otherwise, non-destructively return the original '\'.
720  */
721 static int32
722 GetUnicodeEscape(JSTokenStream *ts)
723 0 {
724 0     jschar cp[5];
725 0     int32 c;
726
727 0     if (PeekChars(ts, 5, cp) && cp[0] == 'u' &&
728         JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&
729         JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4]))
730     {
731 0         c = (((((JS7_UNHEX(cp[1]) << 4)
732                 + JS7_UNHEX(cp[2])) << 4)
733               + JS7_UNHEX(cp[3])) << 4)
734             + JS7_UNHEX(cp[4]);
735 0         SkipChars(ts, 5);
736 0         return c;
737     }
738 0     return '\\';
739 }
740
741 JSTokenType
742 js_GetToken(JSContext *cx, JSTokenStream *ts)
743 14902630 {
744 14902630     JSTokenType tt;
745 14902630     JSToken *tp;
746 14902630     int32 c;
747 14902630     JSAtom *atom;
748 14902630     JSBool hadUnicodeEscape;
749
750 #define INIT_TOKENBUF(tb)   ((tb)->ptr = (tb)->base)
751 #define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR)
752 #define TOKEN_LENGTH(tb)    ((tb)->ptr - (tb)->base - 1)
753 #define RETURN(tt)          { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR;    \
754                               tp->pos.end.index = ts->linepos +               \
755                                   (ts->linebuf.ptr - ts->linebuf.base) -      \
756                                   ts->ungetpos;                               \
757                               return (tp->type = tt); }
758
759     /* If there was a fatal error, keep returning TOK_ERROR. */
760 14902630     if (ts->flags & TSF_ERROR)
761 0         return TOK_ERROR;
762
763     /* Check for a pushed-back token resulting from mismatching lookahead. */
764 14902630     while (ts->lookahead != 0) {
765 13055783         ts->lookahead--;
766 13055783         ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
767 13055783         tt = CURRENT_TOKEN(ts).type;
768 13055783         if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))
769 13055783             return tt;
770     }
771
772 retry:
773 3866810     do {
774 3866810         c = GetChar(ts);
775 3866810         if (c == '\n') {
776 650045             ts->flags &= ~TSF_DIRTYLINE;
777 650045             if (ts->flags & TSF_NEWLINES)
778 0                 break;
779         }
780 3866810     } while (JS_ISSPACE(c));
781
782 1847527     ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
783 1847527     tp = &CURRENT_TOKEN(ts);
784 1847527     tp->ptr = ts->linebuf.ptr - 1;
785 1847527     tp->pos.begin.index = ts->linepos + (tp->ptr - ts->linebuf.base);
786 1847527     tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno;
787
788 1847527     if (c == EOF)
789 1248         RETURN(TOK_EOF);
790 1846279     if (c != '-' && c != '\n')
791 1845648         ts->flags |= TSF_DIRTYLINE;
792
793 1846279     hadUnicodeEscape = JS_FALSE;
794 1846279     if (JS_ISIDENT_START(c) ||
795         (c == '\\' &&
796          (c = GetUnicodeEscape(ts),
797           hadUnicodeEscape = JS_ISIDENT_START(c)))) {
798 598677         INIT_TOKENBUF(&ts->tokenbuf);
799 4953394         for (;;) {
800 4354717             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
801 0                 RETURN(TOK_ERROR);
802 4354717             c = GetChar(ts);
803 4354717             if (c == '\\') {
804 0                 c = GetUnicodeEscape(ts);
805 0                 if (!JS_ISIDENT(c))
806 0                     break;
807 0                 hadUnicodeEscape = JS_TRUE;
808             } else {
809 4354717                 if (!JS_ISIDENT(c))
810 598677                     break;
811             }
812         }
813 598677         UngetChar(ts, c);
814 598677         FINISH_TOKENBUF(&ts->tokenbuf);
815
816 598677         atom = js_AtomizeChars(cx,
817                                ts->tokenbuf.base,
818                                TOKEN_LENGTH(&ts->tokenbuf),
819                                0);
820 598677         if (!atom)
821 0             RETURN(TOK_ERROR);
822 598677         if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) {
823 42279             struct keyword *kw = ATOM_KEYWORD(atom);
824
825 42279             if (JSVERSION_IS_ECMA(cx->version) || kw->version <= cx->version) {
826 42279                 tp->t_op = (JSOp) kw->op;
827 42279                 RETURN(kw->tokentype);
828             }
829         }
830 556398         tp->t_op = JSOP_NAME;
831 556398         tp->t_atom = atom;
832 556398         RETURN(TOK_NAME);
833     }
834
835 1247602     if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
836 16349         jsint radix;
837 16349         const jschar *endptr;
838 16349         jsdouble dval;
839
840 16349         radix = 10;
841 16349         INIT_TOKENBUF(&ts->tokenbuf);
842
843 16349         if (c == '0') {
844 6971             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
845 0                 RETURN(TOK_ERROR);
846 6971             c = GetChar(ts);
847 6971             if (JS_TOLOWER(c) == 'x') {
848 0                 if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
849 0                     RETURN(TOK_ERROR);
850 0                 c = GetChar(ts);
851 0                 radix = 16;
852 6971             } else if (JS7_ISDEC(c)) {
853 0                 radix = 8;
854             }
855         }
856
857 25727         while (JS7_ISHEX(c)) {
858 9378             if (radix < 16) {
859 9378                 if (JS7_ISLET(c))
860 0                     break;
861
862                 /*
863                  * We permit 08 and 09 as decimal numbers, which makes our
864                  * behaviour a superset of the ECMA numeric grammar.  We might
865                  * not always be so permissive, so we warn about it.
866                  */
867 9378                 if (radix == 8 && c >= '8') {
868 0                     if (!js_ReportCompileErrorNumber(cx, ts, NULL,
869                                                      JSREPORT_WARNING,
870                                                      JSMSG_BAD_OCTAL,
871                                                      c == '8' ? "08" : "09")) {
872 0                         RETURN(TOK_ERROR);
873                     }
874 0                     radix = 10;
875                 }
876             }
877 9378             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
878 0                 RETURN(TOK_ERROR);
879 9378             c = GetChar(ts);
880         }
881
882 16349         if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
883 0             if (c == '.') {
884 0                 do {
885 0                     if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
886 0                         RETURN(TOK_ERROR);
887 0                     c = GetChar(ts);
888 0                 } while (JS7_ISDEC(c));
889             }
890 0             if (JS_TOLOWER(c) == 'e') {
891 0                 if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
892 0                     RETURN(TOK_ERROR);
893 0                 c = GetChar(ts);
894 0                 if (c == '+' || c == '-') {
895 0                     if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
896 0                         RETURN(TOK_ERROR);
897 0                     c = GetChar(ts);
898                 }
899 0                 if (!JS7_ISDEC(c)) {
900 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
901                                                 JSMSG_MISSING_EXPONENT);
902 0                     RETURN(TOK_ERROR);
903                 }
904 0                 do {
905 0                     if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
906 0                         RETURN(TOK_ERROR);
907 0                     c = GetChar(ts);
908 0                 } while (JS7_ISDEC(c));
909             }
910         }
911
912 16349         UngetChar(ts, c);
913 16349         FINISH_TOKENBUF(&ts->tokenbuf);
914
915 16349         if (radix == 10) {
916 16349             if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) {
917 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
918                                             JSMSG_OUT_OF_MEMORY);
919 0                 RETURN(TOK_ERROR);
920             }
921         } else {
922 0             if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) {
923 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
924                                             JSMSG_OUT_OF_MEMORY);
925 0                 RETURN(TOK_ERROR);
926             }
927         }
928 16349         tp->t_dval = dval;
929 16349         RETURN(TOK_NUMBER);
930     }
931
932 1231253     if (c == '"' || c == '\'') {
933 143320         int32 val, qc = c;
934
935 143320         INIT_TOKENBUF(&ts->tokenbuf);
936 11576282         while ((c = GetChar(ts)) != qc) {
937 11432962             if (c == '\n' || c == EOF) {
938 0                 UngetChar(ts, c);
939 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
940                                             JSMSG_UNTERMINATED_STRING);
941 0                 RETURN(TOK_ERROR);
942             }
943 11432962             if (c == '\\') {
944 514122                 switch (c = GetChar(ts)) {
945 0                   case 'b': c = '\b'; break;
946 0                   case 'f': c = '\f'; break;
947 478095                   case 'n': c = '\n'; break;
948 0                   case 'r': c = '\r'; break;
949 0                   case 't': c = '\t'; break;
950 0                   case 'v': c = '\v'; break;
951
952                   default:
953 36027                     if ('0' <= c && c < '8') {
954 0                         val = JS7_UNDEC(c);
955 0                         c = PeekChar(ts);
956 0                         if ('0' <= c && c < '8') {
957 0                             val = 8 * val + JS7_UNDEC(c);
958 0                             GetChar(ts);
959 0                             c = PeekChar(ts);
960 0                             if ('0' <= c && c < '8') {
961 0                                 int32 save = val;
962 0                                 val = 8 * val + JS7_UNDEC(c);
963 0                                 if (val <= 0377)
964 0                                     GetChar(ts);
965                                 else
966 0                                     val = save;
967                             }
968                         }
969 0                         c = (jschar)val;
970 36027                     } else if (c == 'u') {
971 0                         jschar cp[4];
972 0                         if (PeekChars(ts, 4, cp) &&
973                             JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
974                             JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
975 0                             c = (((((JS7_UNHEX(cp[0]) << 4)
976                                     + JS7_UNHEX(cp[1])) << 4)
977                                   + JS7_UNHEX(cp[2])) << 4)
978                                 + JS7_UNHEX(cp[3]);
979 0                             SkipChars(ts, 4);
980                         }
981 36027                     } else if (c == 'x') {
982 0                         jschar cp[2];
983 0                         if (PeekChars(ts, 2, cp) &&
984                             JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
985 0                             c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
986 0                             SkipChars(ts, 2);
987                         }
988 36027                     } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) {
989                         /* ECMA follows C by removing escaped newlines. */
990 11432962                         continue;
991                     }
992 11432962                     break;
993                 }
994             }
995 11432962             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
996 0                 RETURN(TOK_ERROR);
997         }
998 143320         FINISH_TOKENBUF(&ts->tokenbuf);
999 143320         atom = js_AtomizeChars(cx,
1000                                ts->tokenbuf.base,
1001                                TOKEN_LENGTH(&ts->tokenbuf),
1002                                0);
1003 143320         if (!atom)
1004 0             RETURN(TOK_ERROR);
1005 143320         tp->pos.end.lineno = (uint16)ts->lineno;
1006 143320         tp->t_op = JSOP_STRING;
1007 143320         tp->t_atom = atom;
1008 143320         RETURN(TOK_STRING);
1009     }
1010
1011 1087933     switch (c) {
1012       case '\n': 
1013 0         c = TOK_EOL; 
1014 0         break;
1015
1016 277925       case ';': c = TOK_SEMI; break;
1017 35021       case '.': c = TOK_DOT; break;
1018 27088       case '[': c = TOK_LB; break;
1019 27088       case ']': c = TOK_RB; break;
1020 22260       case '{': c = TOK_LC; break;
1021 22260       case '}': c = TOK_RC; break;
1022 290285       case '(': c = TOK_LP; break;
1023 290285       case ')': c = TOK_RP; break;
1024 50190       case ',': c = TOK_COMMA; break;
1025 0       case '?': c = TOK_HOOK; break;
1026
1027       case ':':
1028         /*
1029          * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an
1030          * object initializer, likewise for setter.
1031          */
1032 0         tp->t_op = JSOP_NOP;
1033 0         c = TOK_COLON;
1034 0         break;
1035
1036       case '|':
1037 1686         if (MatchChar(ts, c)) {
1038 1686             c = TOK_OR;
1039 0         } else if (MatchChar(ts, '=')) {
1040 0             tp->t_op = JSOP_BITOR;
1041 0             c = TOK_ASSIGN;
1042         } else {
1043 0             c = TOK_BITOR;
1044         }
1045 0         break;
1046
1047       case '^':
1048 0         if (MatchChar(ts, '=')) {
1049 0             tp->t_op = JSOP_BITXOR;
1050 0             c = TOK_ASSIGN;
1051         } else {
1052 0             c = TOK_BITXOR;
1053         }
1054 0         break;
1055
1056       case '&':
1057 2015         if (MatchChar(ts, c)) {
1058 2015             c = TOK_AND;
1059 0         } else if (MatchChar(ts, '=')) {
1060 0             tp->t_op = JSOP_BITAND;
1061 0             c = TOK_ASSIGN;
1062         } else {
1063 0             c = TOK_BITAND;
1064         }
1065 0         break;
1066
1067       case '=':
1068 27273         if (MatchChar(ts, c)) {
1069 #if JS_HAS_TRIPLE_EQOPS
1070 10603             tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq;
1071 #else
1072             tp->t_op = cx->jsop_eq;
1073 #endif
1074 10603             c = TOK_EQOP;
1075         } else {
1076 16670             tp->t_op = JSOP_NOP;
1077 16670             c = TOK_ASSIGN;
1078         }
1079 16670         break;
1080
1081       case '!':
1082 4291         if (MatchChar(ts, '=')) {
1083 #if JS_HAS_TRIPLE_EQOPS
1084 3879             tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;
1085 #else
1086             tp->t_op = cx->jsop_ne;
1087 #endif
1088 3879             c = TOK_EQOP;
1089         } else {
1090 412             tp->t_op = JSOP_NOT;
1091 412             c = TOK_UNARYOP;
1092         }
1093 412         break;
1094
1095       case '<':
1096         /* NB: treat HTML begin-comment as comment-till-end-of-line */
1097 136         if (MatchChar(ts, '!')) {
1098 0             if (MatchChar(ts, '-')) {
1099 0                 if (MatchChar(ts, '-'))
1100 0                     goto skipline;
1101 0                 UngetChar(ts, '-');
1102             }
1103 0             UngetChar(ts, '!');
1104         }
1105 136         if (MatchChar(ts, c)) {
1106 0             tp->t_op = JSOP_LSH;
1107 0             c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
1108         } else {
1109 136             tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
1110 136             c = TOK_RELOP;
1111         }
1112 136         break;
1113
1114       case '>':
1115 136         if (MatchChar(ts, c)) {
1116 0             tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;
1117 0             c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
1118         } else {
1119 136             tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
1120 136             c = TOK_RELOP;
1121         }
1122 136         break;
1123
1124       case '*':
1125 0         tp->t_op = JSOP_MUL;
1126 0         c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;
1127 0         break;
1128
1129       case '/':
1130 1054         if (MatchChar(ts, '/')) {
1131 skipline:
1132 0             while ((c = GetChar(ts)) != EOF && c != '\n')
1133                 /* skip to end of line */;
1134 0             UngetChar(ts, c);
1135 0             goto retry;
1136         }
1137 1054         if (MatchChar(ts, '*')) {
1138 29206             while ((c = GetChar(ts)) != EOF &&
1139                    !(c == '*' && MatchChar(ts, '/'))) {
1140                 /* Ignore all characters until comment close. */
1141             }
1142 680             if (c == EOF) {
1143 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1144                                             JSMSG_UNTERMINATED_COMMENT);
1145 0                 RETURN(TOK_ERROR);
1146             }
1147 374             goto retry;
1148         }
1149
1150 #if JS_HAS_REGEXPS
1151 374         if (ts->flags & TSF_REGEXP) {
1152 374             JSObject *obj;
1153 374             uintN flags;
1154
1155 374             INIT_TOKENBUF(&ts->tokenbuf);
1156 4352             while ((c = GetChar(ts)) != '/') {
1157 3978                 if (c == '\n' || c == EOF) {
1158 0                     UngetChar(ts, c);
1159 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1160                                                 JSMSG_UNTERMINATED_REGEXP);
1161 0                     RETURN(TOK_ERROR);
1162                 }
1163 3978                 if (c == '\\') {
1164 204                     if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
1165 0                         RETURN(TOK_ERROR);
1166 204                     c = GetChar(ts);
1167                 }
1168 3978                 if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
1169 0                     RETURN(TOK_ERROR);
1170             }
1171 374             FINISH_TOKENBUF(&ts->tokenbuf);
1172 748             for (flags = 0; ; ) {
1173 374                 if (MatchChar(ts, 'g'))
1174 0                     flags |= JSREG_GLOB;
1175 374                 else if (MatchChar(ts, 'i'))
1176 0                     flags |= JSREG_FOLD;
1177 374                 else if (MatchChar(ts, 'm'))
1178 0                     flags |= JSREG_MULTILINE;
1179                 else
1180 374                     break;
1181             }
1182 374             c = PeekChar(ts);
1183 374             if (JS7_ISLET(c)) {
1184 0                 tp->ptr = ts->linebuf.ptr - 1;
1185 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1186                                             JSMSG_BAD_REGEXP_FLAG);
1187 0                 (void) GetChar(ts);
1188 0                 RETURN(TOK_ERROR);
1189             }
1190 374             obj = js_NewRegExpObject(cx, ts,
1191                                      ts->tokenbuf.base,
1192                                      TOKEN_LENGTH(&ts->tokenbuf),
1193                                      flags);
1194 374             if (!obj)
1195 0                 RETURN(TOK_ERROR);
1196 374             atom = js_AtomizeObject(cx, obj, 0);
1197 374             if (!atom)
1198 0                 RETURN(TOK_ERROR);
1199 374             tp->t_op = JSOP_OBJECT;
1200 374             tp->t_atom = atom;
1201 374             RETURN(TOK_OBJECT);
1202         }
1203 #endif /* JS_HAS_REGEXPS */
1204
1205 0         tp->t_op = JSOP_DIV;
1206 0         c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
1207 0         break;
1208
1209       case '%':
1210 0         tp->t_op = JSOP_MOD;
1211 0         c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
1212 0         break;
1213
1214       case '~':
1215 0         tp->t_op = JSOP_BITNOT;
1216 0         c = TOK_UNARYOP;
1217 0         break;
1218
1219       case '+':
1220 8309         if (MatchChar(ts, '=')) {
1221 17             tp->t_op = JSOP_ADD;
1222 17             c = TOK_ASSIGN;
1223 8292         } else if (MatchChar(ts, c)) {
1224 408             c = TOK_INC;
1225         } else {
1226 7884             tp->t_op = JSOP_POS;
1227 7884             c = TOK_PLUS;
1228         }
1229 7884         break;
1230
1231       case '-':
1232 631         if (MatchChar(ts, '=')) {
1233 0             tp->t_op = JSOP_SUB;
1234 0             c = TOK_ASSIGN;
1235 631         } else if (MatchChar(ts, c)) {
1236 34             if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE))
1237 0                 goto skipline;
1238 34             c = TOK_DEC;
1239         } else {
1240 597             tp->t_op = JSOP_NEG;
1241 597             c = TOK_MINUS;
1242         }
1243 631         ts->flags |= TSF_DIRTYLINE;
1244 631         break;
1245
1246 #if JS_HAS_SHARP_VARS
1247       case '#':
1248       {
1249 0         uint32 n;
1250
1251 0         c = GetChar(ts);
1252 0         if (!JS7_ISDEC(c)) {
1253 0             UngetChar(ts, c);
1254 0             goto badchar;
1255         }
1256 0         n = (uint32)JS7_UNDEC(c);
1257 0         for (;;) {
1258 0             c = GetChar(ts);
1259 0             if (!JS7_ISDEC(c))
1260 0                 break;
1261 0             n = 10 * n + JS7_UNDEC(c);
1262 0             if (n >= ATOM_INDEX_LIMIT) {
1263 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1264                                             JSMSG_SHARPVAR_TOO_BIG);
1265 0                 RETURN(TOK_ERROR);
1266             }
1267         }
1268 0         tp->t_dval = (jsdouble) n;
1269 0         if (JS_HAS_STRICT_OPTION(cx) &&
1270             (c == '=' || c == '#')) {
1271 0             char buf[20];
1272 0             JS_snprintf(buf, sizeof buf, "#%u%c", n, c);
1273 0             if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1274                                              JSREPORT_WARNING |
1275                                              JSREPORT_STRICT,
1276                                              JSMSG_DEPRECATED_USAGE,
1277                                              buf)) {
1278 0                 RETURN(TOK_ERROR);
1279             }
1280         }
1281 0         if (c == '=')
1282 0             RETURN(TOK_DEFSHARP);
1283 0         if (c == '#')
1284 0             RETURN(TOK_USESHARP);
1285 0         goto badchar;
1286       }
1287
1288       badchar:
1289 #endif /* JS_HAS_SHARP_VARS */
1290
1291       default:
1292 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1293                                     JSMSG_ILLEGAL_CHARACTER);
1294 0         RETURN(TOK_ERROR);
1295     }
1296
1297 1086879     JS_ASSERT(c < TOK_LIMIT);
1298 1086879     RETURN((JSTokenType)c);
1299
1300 #undef INIT_TOKENBUF
1301 #undef FINISH_TOKENBUF
1302 #undef TOKEN_LENGTH
1303 #undef RETURN
1304 }
1305
1306 void
1307 js_UngetToken(JSTokenStream *ts)
1308 13055783 {
1309 13055783     JS_ASSERT(ts->lookahead < NTOKENS_MASK);
1310 13055783     if (ts->flags & TSF_ERROR)
1311 0         return;
1312 13055783     ts->lookahead++;
1313 13055783     ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
1314 }
1315
1316 JSBool
1317 js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
1318 10897003 {
1319 10897003     if (js_GetToken(cx, ts) == tt)
1320 379019         return JS_TRUE;
1321 10517984     js_UngetToken(ts);
1322 10517984     return JS_FALSE;