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 0 {
173 0     struct keyword *kw;
174 0     JSAtom *atom;
175
176 0     for (kw = keywords; kw->name; kw++) {
177 0         atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED);
178 0         if (!atom)
179 0             return JS_FALSE;
180 0         ATOM_SET_KEYWORD(atom, kw);
181     }
182 0     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 0 {
199 0     JSTokenStream *ts;
200
201 0     ts = js_NewBufferTokenStream(cx, base, length);
202 0     if (!ts)
203 0         return NULL;
204 0     ts->filename = filename;
205 0     ts->lineno = lineno;
206 0     if (principals)
207 0         JSPRINCIPALS_HOLD(cx, principals);
208 0     ts->principals = principals;
209 0     return ts;
210 }
211
212 JS_FRIEND_API(JSTokenStream *)
213 js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
214 0 {
215 0     size_t nb;
216 0     JSTokenStream *ts;
217
218 0     nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);
219 0     JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb);
220 0     if (!ts) {
221 0         JS_ReportOutOfMemory(cx);
222 0         return NULL;
223     }
224 0     memset(ts, 0, nb);
225 0     ts->lineno = 1;
226 0     ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);
227 0     ts->userbuf.base = (jschar *)base;
228 0     ts->userbuf.limit = (jschar *)base + length;
229 0     ts->userbuf.ptr = (jschar *)base;
230 0     ts->listener = cx->runtime->sourceHandler;
231 0     ts->listenerData = cx->runtime->sourceHandlerData;
232 0     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 0 {
268 0     if (ts->principals)
269 0         JSPRINCIPALS_DROP(cx, ts->principals);
270 0     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 0 {
304 0     int32 c;
305 0     ptrdiff_t i, j, len, olen;
306 0     JSBool crflag;
307 0     char cbuf[JS_LINE_LIMIT];
308 0     jschar *ubuf, *nl;
309
310 0     if (ts->ungetpos != 0) {
311 0         c = ts->ungetbuf[--ts->ungetpos];
312     } else {
313 0         do {
314 0             if (ts->linebuf.ptr == ts->linebuf.limit) {
315 0                 len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
316 0                 if (len <= 0) {
317 0                     if (!ts->file) {
318 0                         ts->flags |= TSF_EOF;
319 0                         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 0                 if (ts->listener) {
346 0                     ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len,
347                                  &ts->listenerTSData, ts->listenerData);
348                 }
349
350 0                 nl = ts->saveEOL;
351 0                 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 0                     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 0                         if ((*nl & 0xDFD0) == 0) {
363 0                             if (*nl == '\n')
364 0                                 break;
365 0                             if (*nl == '\r') {
366 0                                 if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
367 0                                     nl++;
368 0                                 break;
369                             }
370 0                             if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR)
371 0                                 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 0                 if (nl < ts->userbuf.limit)
381 0                     len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;
382 0                 if (len >= JS_LINE_LIMIT) {
383 0                     len = JS_LINE_LIMIT - 1;
384 0                     ts->saveEOL = nl;
385                 } else {
386 0                     ts->saveEOL = NULL;
387                 }
388 0                 js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);
389 0                 ts->userbuf.ptr += len;
390 0                 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 0                 if (nl < ts->userbuf.limit) {
397 0                     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 0                     } else if (*nl == '\n') {
422 0                         if (nl > ts->userbuf.base &&
423                             nl[-1] == '\r' &&
424                             ts->linebuf.base[len-2] == '\r') {
425 0                             len--;
426 0                             JS_ASSERT(ts->linebuf.base[len] == '\n');
427 0                             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 0                 ts->linebuf.limit = ts->linebuf.base + len;
436 0                 ts->linebuf.ptr = ts->linebuf.base;
437
438                 /* Update position of linebuf within physical userbuf line. */
439 0                 if (!(ts->flags & TSF_NLFLAG))
440 0                     ts->linepos += ts->linelen;
441                 else
442 0                     ts->linepos = 0;
443 0                 if (ts->linebuf.limit[-1] == '\n')
444 0                     ts->flags |= TSF_NLFLAG;
445                 else
446 0                     ts->flags &= ~TSF_NLFLAG;
447
448                 /* Update linelen from original segment length. */
449 0                 ts->linelen = olen;
450             }
451 0             c = *ts->linebuf.ptr++;
452 0         } while (JS_ISFORMAT(c));
453     }
454 0     if (c == '\n')
455 0         ts->lineno++;
456 0     return c;
457 }
458
459 static void
460 UngetChar(JSTokenStream *ts, int32 c)
461 0 {
462 0     if (c == EOF)
463 0         return;
464 0     JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
465 0     if (c == '\n')
466 0         ts->lineno--;
467 0     ts->ungetbuf[ts->ungetpos++] = (jschar)c;
468 }
469
470 static int32
471 PeekChar(JSTokenStream *ts)
472 0 {
473 0     int32 c;
474
475 0     c = GetChar(ts);
476 0     UngetChar(ts, c);
477 0     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 0 {
507 0     int32 c;
508
509 0     c = GetChar(ts);
510 0     if (c == expect)
511 0         return JS_TRUE;
512 0     UngetChar(ts, c);
513 0     return JS_FALSE;
514 }
515
516 JSBool
517 js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts,
518                             JSCodeGenerator *cg, uintN flags,
519                             const uintN errorNumber, ...)
520 0 {
521 0     va_list ap;
522 0     JSErrorReporter onError;
523 0     JSErrorReport report;
524 0     jschar *tokenptr;
525 0     JSString *linestr = NULL;
526 0     char *message;
527 0     JSBool warning;
528
529 0     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
530 0         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 0 {
650 0     JSTokenType tt;
651
652 0     if (ts->lookahead != 0) {
653 0         tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type;
654     } else {
655 0         tt = js_GetToken(cx, ts);
656 0         js_UngetToken(ts);
657     }
658 0     return tt;
659 }
660
661 JSTokenType
662 js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)
663 0 {
664 0     JSTokenType tt;
665
666 0     JS_ASSERT(ts->lookahead == 0 ||
667               ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos));
668 0     ts->flags |= TSF_NEWLINES;
669 0     tt = js_PeekToken(cx, ts);
670 0     ts->flags &= ~TSF_NEWLINES;
671 0     return tt;
672 }
673
674 #define TBMIN   64
675
676 static JSBool
677 GrowTokenBuf(JSContext *cx, JSTokenBuf *tb)
678 0 {
679 0     jschar *base;
680 0     ptrdiff_t offset, length;
681 0     size_t tbsize;
682 0     JSArenaPool *pool;
683
684 0     base = tb->base;
685 0     offset = PTRDIFF(tb->ptr, base, jschar);
686 0     pool = &cx->tempPool;
687 0     if (!base) {
688 0         tbsize = TBMIN * sizeof(jschar);
689 0         length = TBMIN;
690 0         JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize);
691     } else {
692 0         length = PTRDIFF(tb->limit, base, jschar);
693 0         tbsize = length * sizeof(jschar);
694 0         length <<= 1;
695 0         JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize);
696     }
697 0     if (!base) {
698 0         JS_ReportOutOfMemory(cx);
699 0         return JS_FALSE;
700     }
701 0     tb->base = base;
702 0     tb->limit = base + length;
703 0     tb->ptr = base + offset;
704 0     return JS_TRUE;
705 }
706
707 static JSBool
708 AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c)
709 0 {
710 0     if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb))
711 0         return JS_FALSE;
712 0     *tb->ptr++ = c;
713 0     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 0 {
744 0     JSTokenType tt;
745 0     JSToken *tp;
746 0     int32 c;
747 0     JSAtom *atom;
748 0     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 0     if (ts->flags & TSF_ERROR)
761 0         return TOK_ERROR;
762
763     /* Check for a pushed-back token resulting from mismatching lookahead. */
764 0     while (ts->lookahead != 0) {
765 0         ts->lookahead--;
766 0         ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
767 0         tt = CURRENT_TOKEN(ts).type;
768 0         if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))
769 0             return tt;
770     }
771
772 retry:
773 0     do {
774 0         c = GetChar(ts);
775 0         if (c == '\n') {
776 0             ts->flags &= ~TSF_DIRTYLINE;
777 0             if (ts->flags & TSF_NEWLINES)
778 0                 break;
779         }
780 0     } while (JS_ISSPACE(c));
781
782 0     ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
783 0     tp = &CURRENT_TOKEN(ts);
784 0     tp->ptr = ts->linebuf.ptr - 1;
785 0     tp->pos.begin.index = ts->linepos + (tp->ptr - ts->linebuf.base);
786 0     tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno;
787
788 0     if (c == EOF)
789 0         RETURN(TOK_EOF);
790 0     if (c != '-' && c != '\n')
791 0         ts->flags |= TSF_DIRTYLINE;
792
793 0     hadUnicodeEscape = JS_FALSE;
794 0     if (JS_ISIDENT_START(c) ||
795         (c == '\\' &&
796          (c = GetUnicodeEscape(ts),
797           hadUnicodeEscape = JS_ISIDENT_START(c)))) {
798 0         INIT_TOKENBUF(&ts->tokenbuf);
799 0         for (;;) {
800 0             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
801 0                 RETURN(TOK_ERROR);
802 0             c = GetChar(ts);
803 0             if (c == '\\') {
804 0                 c = GetUnicodeEscape(ts);
805 0                 if (!JS_ISIDENT(c))
806 0                     break;
807 0                 hadUnicodeEscape = JS_TRUE;
808             } else {
809 0                 if (!JS_ISIDENT(c))
810 0                     break;
811             }
812         }
813 0         UngetChar(ts, c);
814 0         FINISH_TOKENBUF(&ts->tokenbuf);
815
816 0         atom = js_AtomizeChars(cx,
817                                ts->tokenbuf.base,
818                                TOKEN_LENGTH(&ts->tokenbuf),
819                                0);
820 0         if (!atom)
821 0             RETURN(TOK_ERROR);
822 0         if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) {
823 0             struct keyword *kw = ATOM_KEYWORD(atom);
824
825 0             if (JSVERSION_IS_ECMA(cx->version) || kw->version <= cx->version) {
826 0                 tp->t_op = (JSOp) kw->op;
827 0                 RETURN(kw->tokentype);
828             }
829         }
830 0         tp->t_op = JSOP_NAME;
831 0         tp->t_atom = atom;
832 0         RETURN(TOK_NAME);
833     }
834
835 0     if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
836 0         jsint radix;
837 0         const jschar *endptr;
838 0         jsdouble dval;
839
840 0         radix = 10;
841 0         INIT_TOKENBUF(&ts->tokenbuf);
842
843 0         if (c == '0') {
844 0             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
845 0                 RETURN(TOK_ERROR);
846 0             c = GetChar(ts);
847 0             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 0             } else if (JS7_ISDEC(c)) {
853 0                 radix = 8;
854             }
855         }
856
857 0         while (JS7_ISHEX(c)) {
858 0             if (radix < 16) {
859 0                 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 0                 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 0             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
878 0                 RETURN(TOK_ERROR);
879 0             c = GetChar(ts);
880         }
881
882 0         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 0         UngetChar(ts, c);
913 0         FINISH_TOKENBUF(&ts->tokenbuf);
914
915 0         if (radix == 10) {
916 0             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 0         tp->t_dval = dval;
929 0         RETURN(TOK_NUMBER);
930     }
931
932 0     if (c == '"' || c == '\'') {
933 0         int32 val, qc = c;
934
935 0         INIT_TOKENBUF(&ts->tokenbuf);
936 0         while ((c = GetChar(ts)) != qc) {
937 0             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 0             if (c == '\\') {
944 0                 switch (c = GetChar(ts)) {
945 0                   case 'b': c = '\b'; break;
946 0                   case 'f': c = '\f'; break;
947 0                   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 0                     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 0                     } 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 0                     } 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 0                     } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) {
989                         /* ECMA follows C by removing escaped newlines. */
990 0                         continue;
991                     }
992 0                     break;
993                 }
994             }
995 0             if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
996 0                 RETURN(TOK_ERROR);
997         }
998 0         FINISH_TOKENBUF(&ts->tokenbuf);
999 0         atom = js_AtomizeChars(cx,
1000                                ts->tokenbuf.base,
1001                                TOKEN_LENGTH(&ts->tokenbuf),
1002                                0);
1003 0         if (!atom)
1004 0             RETURN(TOK_ERROR);
1005 0         tp->pos.end.lineno = (uint16)ts->lineno;
1006 0         tp->t_op = JSOP_STRING;
1007 0         tp->t_atom = atom;
1008 0         RETURN(TOK_STRING);
1009     }
1010
1011 0     switch (c) {
1012       case '\n': 
1013 0         c = TOK_EOL; 
1014 0         break;
1015
1016 0       case ';': c = TOK_SEMI; break;
1017 0       case '.': c = TOK_DOT; break;
1018 0       case '[': c = TOK_LB; break;
1019 0       case ']': c = TOK_RB; break;
1020 0       case '{': c = TOK_LC; break;
1021 0       case '}': c = TOK_RC; break;
1022 0       case '(': c = TOK_LP; break;
1023 0       case ')': c = TOK_RP; break;
1024 0       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 0         if (MatchChar(ts, c)) {
1038 0             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 0         if (MatchChar(ts, c)) {
1058 0             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 0         if (MatchChar(ts, c)) {
1069 #if JS_HAS_TRIPLE_EQOPS
1070 0             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 0             c = TOK_EQOP;
1075         } else {
1076 0             tp->t_op = JSOP_NOP;
1077 0             c = TOK_ASSIGN;
1078         }
1079 0         break;
1080
1081       case '!':
1082 0         if (MatchChar(ts, '=')) {
1083 #if JS_HAS_TRIPLE_EQOPS
1084 0             tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;
1085 #else
1086             tp->t_op = cx->jsop_ne;
1087 #endif
1088 0             c = TOK_EQOP;
1089         } else {
1090 0             tp->t_op = JSOP_NOT;
1091 0             c = TOK_UNARYOP;
1092         }
1093 0         break;
1094
1095       case '<':
1096         /* NB: treat HTML begin-comment as comment-till-end-of-line */
1097 0         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 0         if (MatchChar(ts, c)) {
1106 0             tp->t_op = JSOP_LSH;
1107 0             c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
1108         } else {
1109 0             tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
1110 0             c = TOK_RELOP;
1111         }
1112 0         break;
1113
1114       case '>':
1115 0         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 0             tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
1120 0             c = TOK_RELOP;
1121         }
1122 0         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 0         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 0         if (MatchChar(ts, '*')) {
1138 0             while ((c = GetChar(ts)) != EOF &&
1139                    !(c == '*' && MatchChar(ts, '/'))) {
1140                 /* Ignore all characters until comment close. */
1141             }
1142 0             if (c == EOF) {
1143 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1144                                             JSMSG_UNTERMINATED_COMMENT);
1145 0                 RETURN(TOK_ERROR);
1146             }
1147 0             goto retry;
1148         }
1149
1150 #if JS_HAS_REGEXPS
1151 0         if (ts->flags & TSF_REGEXP) {
1152 0             JSObject *obj;
1153 0             uintN flags;
1154
1155 0             INIT_TOKENBUF(&ts->tokenbuf);
1156 0             while ((c = GetChar(ts)) != '/') {
1157 0                 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 0                 if (c == '\\') {
1164 0                     if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
1165 0                         RETURN(TOK_ERROR);
1166 0                     c = GetChar(ts);
1167                 }
1168 0                 if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
1169 0                     RETURN(TOK_ERROR);
1170             }
1171 0             FINISH_TOKENBUF(&ts->tokenbuf);
1172 0             for (flags = 0; ; ) {
1173 0                 if (MatchChar(ts, 'g'))
1174 0                     flags |= JSREG_GLOB;
1175 0                 else if (MatchChar(ts, 'i'))
1176 0                     flags |= JSREG_FOLD;
1177 0                 else if (MatchChar(ts, 'm'))
1178 0                     flags |= JSREG_MULTILINE;
1179                 else
1180 0                     break;
1181             }
1182 0             c = PeekChar(ts);
1183 0             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 0             obj = js_NewRegExpObject(cx, ts,
1191                                      ts->tokenbuf.base,
1192                                      TOKEN_LENGTH(&ts->tokenbuf),
1193                                      flags);
1194 0             if (!obj)
1195 0                 RETURN(TOK_ERROR);
1196 0             atom = js_AtomizeObject(cx, obj, 0);
1197 0             if (!atom)
1198 0                 RETURN(TOK_ERROR);
1199 0             tp->t_op = JSOP_OBJECT;
1200 0             tp->t_atom = atom;
1201 0             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 0         if (MatchChar(ts, '=')) {
1221 0             tp->t_op = JSOP_ADD;
1222 0             c = TOK_ASSIGN;
1223 0         } else if (MatchChar(ts, c)) {
1224 0             c = TOK_INC;
1225         } else {
1226 0             tp->t_op = JSOP_POS;
1227 0             c = TOK_PLUS;
1228         }
1229 0         break;
1230
1231       case '-':
1232 0         if (MatchChar(ts, '=')) {
1233 0             tp->t_op = JSOP_SUB;
1234 0             c = TOK_ASSIGN;
1235 0         } else if (MatchChar(ts, c)) {
1236 0             if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE))
1237 0                 goto skipline;
1238 0             c = TOK_DEC;
1239         } else {
1240 0             tp->t_op = JSOP_NEG;
1241 0             c = TOK_MINUS;
1242         }
1243 0         ts->flags |= TSF_DIRTYLINE;
1244 0         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 0     JS_ASSERT(c < TOK_LIMIT);
1298 0     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 0 {
1309 0     JS_ASSERT(ts->lookahead < NTOKENS_MASK);
1310 0     if (ts->flags & TSF_ERROR)
1311 0         return;
1312 0     ts->lookahead++;
1313 0     ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
1314 }
1315
1316 JSBool
1317 js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
1318 0 {
1319 0     if (js_GetToken(cx, ts) == tt)
1320 0         return JS_TRUE;
1321 0     js_UngetToken(ts);
1322 0     return JS_FALSE;