Code

fix include paths
[inkscape.git] / src / dom / js / jsscan.c
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 ***** */
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"
70 #define RESERVE_JAVA_KEYWORDS
71 #define RESERVE_ECMA_KEYWORDS
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},
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
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
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
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
158 #ifdef RESERVE_ECMA_KEYWORDS
159     {"enum",           TOK_RESERVED,            JSOP_NOP,   JSVERSION_1_3},
160 #endif
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 };
170 JSBool
171 js_InitScanner(JSContext *cx)
173     struct keyword *kw;
174     JSAtom *atom;
176     for (kw = keywords; kw->name; kw++) {
177         atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED);
178         if (!atom)
179             return JS_FALSE;
180         ATOM_SET_KEYWORD(atom, kw);
181     }
182     return JS_TRUE;
185 JS_FRIEND_API(void)
186 js_MapKeywords(void (*mapfun)(const char *))
188     struct keyword *kw;
190     for (kw = keywords; kw->name; kw++)
191         mapfun(kw->name);
194 JSTokenStream *
195 js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,
196                   const char *filename, uintN lineno,
197                   JSPrincipals *principals)
199     JSTokenStream *ts;
201     ts = js_NewBufferTokenStream(cx, base, length);
202     if (!ts)
203         return NULL;
204     ts->filename = filename;
205     ts->lineno = lineno;
206     if (principals)
207         JSPRINCIPALS_HOLD(cx, principals);
208     ts->principals = principals;
209     return ts;
212 JS_FRIEND_API(JSTokenStream *)
213 js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
215     size_t nb;
216     JSTokenStream *ts;
218     nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);
219     JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb);
220     if (!ts) {
221         JS_ReportOutOfMemory(cx);
222         return NULL;
223     }
224     memset(ts, 0, nb);
225     ts->lineno = 1;
226     ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);
227     ts->userbuf.base = (jschar *)base;
228     ts->userbuf.limit = (jschar *)base + length;
229     ts->userbuf.ptr = (jschar *)base;
230     ts->listener = cx->runtime->sourceHandler;
231     ts->listenerData = cx->runtime->sourceHandlerData;
232     return ts;
235 JS_FRIEND_API(JSTokenStream *)
236 js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp)
238     jschar *base;
239     JSTokenStream *ts;
240     FILE *file;
242     JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool,
243                            JS_LINE_LIMIT * sizeof(jschar));
244     if (!base)
245         return NULL;
246     ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT);
247     if (!ts)
248         return NULL;
249     if (!filename || strcmp(filename, "-") == 0) {
250         file = defaultfp;
251     } else {
252         file = fopen(filename, "r");
253         if (!file) {
254             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
255                                  filename, "No such file or directory");
256             return NULL;
257         }
258     }
259     ts->userbuf.ptr = ts->userbuf.limit;
260     ts->file = file;
261     ts->filename = filename;
262     return ts;
265 JS_FRIEND_API(JSBool)
266 js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)
268     if (ts->flags & TSF_OWNFILENAME)
269         JS_free(cx, (void *) ts->filename);
270     if (ts->principals)
271         JSPRINCIPALS_DROP(cx, ts->principals);
272     return !ts->file || fclose(ts->file) == 0;
275 static int
276 my_fgets(char *buf, int size, FILE *file)
278     int n, i, c;
279     JSBool crflag;
281     n = size - 1;
282     if (n < 0)
283         return -1;
285     crflag = JS_FALSE;
286     for (i = 0; i < n && (c = getc(file)) != EOF; i++) {
287         buf[i] = c;
288         if (c == '\n') {        /* any \n ends a line */
289             i++;                /* keep the \n; we know there is room for \0 */
290             break;
291         }
292         if (crflag) {           /* \r not followed by \n ends line at the \r */
293             ungetc(c, file);
294             break;              /* and overwrite c in buf with \0 */
295         }
296         crflag = (c == '\r');
297     }
299     buf[i] = '\0';
300     return i;
303 static int32
304 GetChar(JSTokenStream *ts)
306     int32 c;
307     ptrdiff_t i, j, len, olen;
308     JSBool crflag;
309     char cbuf[JS_LINE_LIMIT];
310     jschar *ubuf, *nl;
312     if (ts->ungetpos != 0) {
313         c = ts->ungetbuf[--ts->ungetpos];
314     } else {
315         do {
316             if (ts->linebuf.ptr == ts->linebuf.limit) {
317                 len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
318                 if (len <= 0) {
319                     if (!ts->file) {
320                         ts->flags |= TSF_EOF;
321                         return EOF;
322                     }
324                     /* Fill ts->userbuf so that \r and \r\n convert to \n. */
325                     crflag = (ts->flags & TSF_CRFLAG) != 0;
326                     len = my_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file);
327                     if (len <= 0) {
328                         ts->flags |= TSF_EOF;
329                         return EOF;
330                     }
331                     olen = len;
332                     ubuf = ts->userbuf.base;
333                     i = 0;
334                     if (crflag) {
335                         ts->flags &= ~TSF_CRFLAG;
336                         if (cbuf[0] != '\n') {
337                             ubuf[i++] = '\n';
338                             len++;
339                             ts->linepos--;
340                         }
341                     }
342                     for (j = 0; i < len; i++, j++)
343                         ubuf[i] = (jschar) (unsigned char) cbuf[j];
344                     ts->userbuf.limit = ubuf + len;
345                     ts->userbuf.ptr = ubuf;
346                 }
347                 if (ts->listener) {
348                     ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len,
349                                  &ts->listenerTSData, ts->listenerData);
350                 }
352                 nl = ts->saveEOL;
353                 if (!nl) {
354                     /*
355                      * Any one of \n, \r, or \r\n ends a line (the longest
356                      * match wins).  Also allow the Unicode line and paragraph
357                      * separators.
358                      */
359                     for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) {
360                         /*
361                          * Try to prevent value-testing on most characters by
362                          * filtering out characters that aren't 000x or 202x.
363                          */
364                         if ((*nl & 0xDFD0) == 0) {
365                             if (*nl == '\n')
366                                 break;
367                             if (*nl == '\r') {
368                                 if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
369                                     nl++;
370                                 break;
371                             }
372                             if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR)
373                                 break;
374                         }
375                     }
376                 }
378                 /*
379                  * If there was a line terminator, copy thru it into linebuf.
380                  * Else copy JS_LINE_LIMIT-1 bytes into linebuf.
381                  */
382                 if (nl < ts->userbuf.limit)
383                     len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;
384                 if (len >= JS_LINE_LIMIT) {
385                     len = JS_LINE_LIMIT - 1;
386                     ts->saveEOL = nl;
387                 } else {
388                     ts->saveEOL = NULL;
389                 }
390                 js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);
391                 ts->userbuf.ptr += len;
392                 olen = len;
394                 /*
395                  * Make sure linebuf contains \n for EOL (don't do this in
396                  * userbuf because the user's string might be readonly).
397                  */
398                 if (nl < ts->userbuf.limit) {
399                     if (*nl == '\r') {
400                         if (ts->linebuf.base[len-1] == '\r') {
401                             /*
402                              * Does the line segment end in \r?  We must check
403                              * for a \n at the front of the next segment before
404                              * storing a \n into linebuf.  This case matters
405                              * only when we're reading from a file.
406                              */
407                             if (nl + 1 == ts->userbuf.limit && ts->file) {
408                                 len--;
409                                 ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */
410                                 if (len == 0) {
411                                     /*
412                                      * This can happen when a segment ends in
413                                      * \r\r.  Start over.  ptr == limit in this
414                                      * case, so we'll fall into buffer-filling
415                                      * code.
416                                      */
417                                     return GetChar(ts);
418                                 }
419                             } else {
420                                 ts->linebuf.base[len-1] = '\n';
421                             }
422                         }
423                     } else if (*nl == '\n') {
424                         if (nl > ts->userbuf.base &&
425                             nl[-1] == '\r' &&
426                             ts->linebuf.base[len-2] == '\r') {
427                             len--;
428                             JS_ASSERT(ts->linebuf.base[len] == '\n');
429                             ts->linebuf.base[len-1] = '\n';
430                         }
431                     } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) {
432                         ts->linebuf.base[len-1] = '\n';
433                     }
434                 }
436                 /* Reset linebuf based on adjusted segment length. */
437                 ts->linebuf.limit = ts->linebuf.base + len;
438                 ts->linebuf.ptr = ts->linebuf.base;
440                 /* Update position of linebuf within physical userbuf line. */
441                 if (!(ts->flags & TSF_NLFLAG))
442                     ts->linepos += ts->linelen;
443                 else
444                     ts->linepos = 0;
445                 if (ts->linebuf.limit[-1] == '\n')
446                     ts->flags |= TSF_NLFLAG;
447                 else
448                     ts->flags &= ~TSF_NLFLAG;
450                 /* Update linelen from original segment length. */
451                 ts->linelen = olen;
452             }
453             c = *ts->linebuf.ptr++;
454         } while (JS_ISFORMAT(c));
455     }
456     if (c == '\n')
457         ts->lineno++;
458     return c;
461 static void
462 UngetChar(JSTokenStream *ts, int32 c)
464     if (c == EOF)
465         return;
466     JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
467     if (c == '\n')
468         ts->lineno--;
469     ts->ungetbuf[ts->ungetpos++] = (jschar)c;
472 static int32
473 PeekChar(JSTokenStream *ts)
475     int32 c;
477     c = GetChar(ts);
478     UngetChar(ts, c);
479     return c;
482 static JSBool
483 PeekChars(JSTokenStream *ts, intN n, jschar *cp)
485     intN i, j;
486     int32 c;
488     for (i = 0; i < n; i++) {
489         c = GetChar(ts);
490         if (c == EOF)
491             break;
492         cp[i] = (jschar)c;
493     }
494     for (j = i - 1; j >= 0; j--)
495         UngetChar(ts, cp[j]);
496     return i == n;
499 static void
500 SkipChars(JSTokenStream *ts, intN n)
502     while (--n >= 0)
503         GetChar(ts);
506 static JSBool
507 MatchChar(JSTokenStream *ts, int32 expect)
509     int32 c;
511     c = GetChar(ts);
512     if (c == expect)
513         return JS_TRUE;
514     UngetChar(ts, c);
515     return JS_FALSE;
518 JSBool
519 js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts,
520                             JSCodeGenerator *cg, uintN flags,
521                             const uintN errorNumber, ...)
523     va_list ap;
524     JSErrorReporter onError;
525     JSErrorReport report;
526     jschar *tokenptr;
527     JSString *linestr = NULL;
528     char *message;
529     JSBool warning;
531     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
532         return JS_TRUE;
534     memset(&report, 0, sizeof (struct JSErrorReport));
535     report.flags = flags;
536     report.errorNumber = errorNumber;
537     message = NULL;
539     va_start(ap, errorNumber);
540     if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL,
541                                  errorNumber, &message, &report, &warning,
542                                  JS_TRUE, ap)) {
543         return JS_FALSE;
544     }
545     va_end(ap);
547     js_AddRoot(cx, &linestr, "error line buffer");
549     JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);
550     onError = cx->errorReporter;
551     if (onError) {
552         /*
553          * We are typically called with non-null ts and null cg from jsparse.c.
554          * We can be called with null ts from the regexp compilation functions.
555          * The code generator (jsemit.c) may pass null ts and non-null cg.
556          */
557         if (ts) {
558             report.filename = ts->filename;
559             report.lineno = ts->lineno;
560             linestr = js_NewStringCopyN(cx, ts->linebuf.base,
561                                         ts->linebuf.limit - ts->linebuf.base,
562                                         0);
563             report.linebuf = linestr
564                 ? JS_GetStringBytes(linestr)
565                 : NULL;
566             tokenptr =
567                 ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].ptr;
568             report.tokenptr = linestr
569                 ? report.linebuf + (tokenptr - ts->linebuf.base)
570                 : NULL;
571             report.uclinebuf = linestr
572                 ? JS_GetStringChars(linestr)
573                 : NULL;
574             report.uctokenptr = linestr
575                 ? report.uclinebuf + (tokenptr - ts->linebuf.base)
576                 : NULL;
577         } else if (cg) {
578             report.filename = cg->filename;
579             report.lineno = CG_CURRENT_LINE(cg);
580         }
582 #if JS_HAS_ERROR_EXCEPTIONS
583         /*
584          * If there's a runtime exception type associated with this error
585          * number, set that as the pending exception.  For errors occuring at
586          * compile time, this is very likely to be a JSEXN_SYNTAXERR.
587          *
588          * If an exception is thrown but not caught, the JSREPORT_EXCEPTION
589          * flag will be set in report.flags.  Proper behavior for an error
590          * reporter is to ignore a report with this flag for all but top-level
591          * compilation errors.  The exception will remain pending, and so long
592          * as the non-top-level "load", "eval", or "compile" native function
593          * returns false, the top-level reporter will eventually receive the
594          * uncaught exception report.
595          *
596          * XXX it'd probably be best if there was only one call to this
597          * function, but there seem to be two error reporter call points.
598          */
600         /*
601          * Only try to raise an exception if there isn't one already set -
602          * otherwise the exception will describe only the last compile error,
603          * which is likely spurious.
604          */
605         if (!(ts && (ts->flags & TSF_ERROR)))
606             if (js_ErrorToException(cx, message, &report))
607                 onError = NULL;
609         /*
610          * Suppress any compiletime errors that don't occur at the top level.
611          * This may still fail, as interplevel may be zero in contexts where we
612          * don't really want to call the error reporter, as when js is called
613          * by other code which could catch the error.
614          */
615         if (cx->interpLevel != 0)
616             onError = NULL;
617 #endif
618         if (cx->runtime->debugErrorHook && onError) {
619             JSDebugErrorHook hook = cx->runtime->debugErrorHook;
620             /* test local in case debugErrorHook changed on another thread */
621             if (hook && !hook(cx, message, &report,
622                               cx->runtime->debugErrorHookData)) {
623                 onError = NULL;
624             }
625         }
626         if (onError)
627             (*onError)(cx, message, &report);
628     }
629     if (message)
630         JS_free(cx, message);
631     if (report.messageArgs) {
632         int i = 0;
633         while (report.messageArgs[i])
634             JS_free(cx, (void *)report.messageArgs[i++]);
635         JS_free(cx, (void *)report.messageArgs);
636     }
637     if (report.ucmessage)
638         JS_free(cx, (void *)report.ucmessage);
640     js_RemoveRoot(cx->runtime, &linestr);
642     if (ts && !JSREPORT_IS_WARNING(flags)) {
643         /* Set the error flag to suppress spurious reports. */
644         ts->flags |= TSF_ERROR;
645     }
646     return warning;
649 JSTokenType
650 js_PeekToken(JSContext *cx, JSTokenStream *ts)
652     JSTokenType tt;
654     if (ts->lookahead != 0) {
655         tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type;
656     } else {
657         tt = js_GetToken(cx, ts);
658         js_UngetToken(ts);
659     }
660     return tt;
663 JSTokenType
664 js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)
666     JSTokenType tt;
668     JS_ASSERT(ts->lookahead == 0 ||
669               ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos));
670     ts->flags |= TSF_NEWLINES;
671     tt = js_PeekToken(cx, ts);
672     ts->flags &= ~TSF_NEWLINES;
673     return tt;
676 #define TBMIN   64
678 static JSBool
679 GrowTokenBuf(JSContext *cx, JSTokenBuf *tb)
681     jschar *base;
682     ptrdiff_t offset, length;
683     size_t tbsize;
684     JSArenaPool *pool;
686     base = tb->base;
687     offset = PTRDIFF(tb->ptr, base, jschar);
688     pool = &cx->tempPool;
689     if (!base) {
690         tbsize = TBMIN * sizeof(jschar);
691         length = TBMIN;
692         JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize);
693     } else {
694         length = PTRDIFF(tb->limit, base, jschar);
695         tbsize = length * sizeof(jschar);
696         length <<= 1;
697         JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize);
698     }
699     if (!base) {
700         JS_ReportOutOfMemory(cx);
701         return JS_FALSE;
702     }
703     tb->base = base;
704     tb->limit = base + length;
705     tb->ptr = base + offset;
706     return JS_TRUE;
709 static JSBool
710 AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c)
712     if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb))
713         return JS_FALSE;
714     *tb->ptr++ = c;
715     return JS_TRUE;
718 /*
719  * We have encountered a '\': check for a Unicode escape sequence after it,
720  * returning the character code value if we found a Unicode escape sequence.
721  * Otherwise, non-destructively return the original '\'.
722  */
723 static int32
724 GetUnicodeEscape(JSTokenStream *ts)
726     jschar cp[5];
727     int32 c;
729     if (PeekChars(ts, 5, cp) && cp[0] == 'u' &&
730         JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&
731         JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4]))
732     {
733         c = (((((JS7_UNHEX(cp[1]) << 4)
734                 + JS7_UNHEX(cp[2])) << 4)
735               + JS7_UNHEX(cp[3])) << 4)
736             + JS7_UNHEX(cp[4]);
737         SkipChars(ts, 5);
738         return c;
739     }
740     return '\\';
743 static JSToken *
744 NewToken(JSTokenStream *ts)
746     JSToken *tp;
748     ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
749     tp = &CURRENT_TOKEN(ts);
750     tp->ptr = ts->linebuf.ptr - 1;
751     tp->pos.begin.index = ts->linepos + (tp->ptr - ts->linebuf.base);
752     tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno;
753     return tp;
756 JSTokenType
757 js_GetToken(JSContext *cx, JSTokenStream *ts)
759     JSTokenType tt;
760     int32 c, qc;
761     JSToken *tp;
762     JSAtom *atom;
763     JSBool hadUnicodeEscape;
765 #define INIT_TOKENBUF()     (ts->tokenbuf.ptr = ts->tokenbuf.base)
766 #define TRIM_TOKENBUF(i)    (ts->tokenbuf.ptr = ts->tokenbuf.base + i)
767 #define TOKENBUF_LENGTH()   (ts->tokenbuf.ptr - ts->tokenbuf.base)
768 #define TOKENBUF_BASE()     (ts->tokenbuf.base)
769 #define TOKENBUF_CHAR(i)    (ts->tokenbuf.base[i])
770 #define TOKENBUF_TO_ATOM()  (js_AtomizeChars(cx,                              \
771                                              TOKENBUF_BASE(),                 \
772                                              TOKENBUF_LENGTH(),               \
773                                              0))
775 #define ADD_TO_TOKENBUF(c)                                                    \
776     JS_BEGIN_MACRO                                                            \
777         if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))                     \
778             goto error;                                                       \
779     JS_END_MACRO
781     /* If there was a fatal error, keep returning TOK_ERROR. */
782     if (ts->flags & TSF_ERROR)
783         return TOK_ERROR;
785     /* Check for a pushed-back token resulting from mismatching lookahead. */
786     while (ts->lookahead != 0) {
787         ts->lookahead--;
788         ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
789         tt = CURRENT_TOKEN(ts).type;
790         if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))
791             return tt;
792     }
794 retry:
795     do {
796         c = GetChar(ts);
797         if (c == '\n') {
798             ts->flags &= ~TSF_DIRTYLINE;
799             if (ts->flags & TSF_NEWLINES)
800                 break;
801         }
802     } while (JS_ISSPACE(c));
804     tp = NewToken(ts);
805     if (c == EOF) {
806         tt = TOK_EOF;
807         goto out;
808     }
810     if (c != '-' && c != '\n')
811         ts->flags |= TSF_DIRTYLINE;
813     hadUnicodeEscape = JS_FALSE;
814     if (JS_ISIDENT_START(c) ||
815         (c == '\\' &&
816          (c = GetUnicodeEscape(ts),
817           hadUnicodeEscape = JS_ISIDENT_START(c)))) {
818         INIT_TOKENBUF();
819         for (;;) {
820             ADD_TO_TOKENBUF(c);
821             c = GetChar(ts);
822             if (c == '\\') {
823                 c = GetUnicodeEscape(ts);
824                 if (!JS_ISIDENT(c))
825                     break;
826                 hadUnicodeEscape = JS_TRUE;
827             } else {
828                 if (!JS_ISIDENT(c))
829                     break;
830             }
831         }
832         UngetChar(ts, c);
834         atom = TOKENBUF_TO_ATOM();
835         if (!atom)
836             goto error;
837         if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) {
838             struct keyword *kw = ATOM_KEYWORD(atom);
840             if (JSVERSION_IS_ECMA(cx->version) || kw->version <= cx->version) {
841                 tp->t_op = (JSOp) kw->op;
842                 tt = kw->tokentype;
843                 goto out;
844             }
845         }
846         tp->t_op = JSOP_NAME;
847         tp->t_atom = atom;
848         tt = TOK_NAME;
849         goto out;
850     }
852     if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
853         jsint radix;
854         const jschar *endptr;
855         jsdouble dval;
857         radix = 10;
858         INIT_TOKENBUF();
860         if (c == '0') {
861             ADD_TO_TOKENBUF(c);
862             c = GetChar(ts);
863             if (JS_TOLOWER(c) == 'x') {
864                 ADD_TO_TOKENBUF(c);
865                 c = GetChar(ts);
866                 radix = 16;
867             } else if (JS7_ISDEC(c)) {
868                 radix = 8;
869             }
870         }
872         while (JS7_ISHEX(c)) {
873             if (radix < 16) {
874                 if (JS7_ISLET(c))
875                     break;
877                 /*
878                  * We permit 08 and 09 as decimal numbers, which makes our
879                  * behaviour a superset of the ECMA numeric grammar.  We might
880                  * not always be so permissive, so we warn about it.
881                  */
882                 if (radix == 8 && c >= '8') {
883                     if (!js_ReportCompileErrorNumber(cx, ts, NULL,
884                                                      JSREPORT_WARNING,
885                                                      JSMSG_BAD_OCTAL,
886                                                      c == '8' ? "08" : "09")) {
887                         goto error;
888                     }
889                     radix = 10;
890                 }
891             }
892             ADD_TO_TOKENBUF(c);
893             c = GetChar(ts);
894         }
896         if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
897             if (c == '.') {
898                 do {
899                     ADD_TO_TOKENBUF(c);
900                     c = GetChar(ts);
901                 } while (JS7_ISDEC(c));
902             }
903             if (JS_TOLOWER(c) == 'e') {
904                 ADD_TO_TOKENBUF(c);
905                 c = GetChar(ts);
906                 if (c == '+' || c == '-') {
907                     ADD_TO_TOKENBUF(c);
908                     c = GetChar(ts);
909                 }
910                 if (!JS7_ISDEC(c)) {
911                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
912                                                 JSMSG_MISSING_EXPONENT);
913                     goto error;
914                 }
915                 do {
916                     ADD_TO_TOKENBUF(c);
917                     c = GetChar(ts);
918                 } while (JS7_ISDEC(c));
919             }
920         }
922         /* Put back the next char and NUL-terminate tokenbuf for js_strto*. */
923         UngetChar(ts, c);
924         ADD_TO_TOKENBUF(0);
926         if (radix == 10) {
927             if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) {
928                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
929                                             JSMSG_OUT_OF_MEMORY);
930                 goto error;
931             }
932         } else {
933             if (!js_strtointeger(cx, TOKENBUF_BASE(), &endptr, radix, &dval)) {
934                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
935                                             JSMSG_OUT_OF_MEMORY);
936                 goto error;
937             }
938         }
939         tp->t_dval = dval;
940         tt = TOK_NUMBER;
941         goto out;
942     }
944     if (c == '"' || c == '\'') {
945         qc = c;
946         INIT_TOKENBUF();
947         while ((c = GetChar(ts)) != qc) {
948             if (c == '\n' || c == EOF) {
949                 UngetChar(ts, c);
950                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
951                                             JSMSG_UNTERMINATED_STRING);
952                 goto error;
953             }
954             if (c == '\\') {
955                 switch (c = GetChar(ts)) {
956                   case 'b': c = '\b'; break;
957                   case 'f': c = '\f'; break;
958                   case 'n': c = '\n'; break;
959                   case 'r': c = '\r'; break;
960                   case 't': c = '\t'; break;
961                   case 'v': c = '\v'; break;
963                   default:
964                     if ('0' <= c && c < '8') {
965                         int32 val = JS7_UNDEC(c);
967                         c = PeekChar(ts);
968                         if ('0' <= c && c < '8') {
969                             val = 8 * val + JS7_UNDEC(c);
970                             GetChar(ts);
971                             c = PeekChar(ts);
972                             if ('0' <= c && c < '8') {
973                                 int32 save = val;
974                                 val = 8 * val + JS7_UNDEC(c);
975                                 if (val <= 0377)
976                                     GetChar(ts);
977                                 else
978                                     val = save;
979                             }
980                         }
982                         c = (jschar)val;
983                     } else if (c == 'u') {
984                         jschar cp[4];
985                         if (PeekChars(ts, 4, cp) &&
986                             JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
987                             JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
988                             c = (((((JS7_UNHEX(cp[0]) << 4)
989                                     + JS7_UNHEX(cp[1])) << 4)
990                                   + JS7_UNHEX(cp[2])) << 4)
991                                 + JS7_UNHEX(cp[3]);
992                             SkipChars(ts, 4);
993                         }
994                     } else if (c == 'x') {
995                         jschar cp[2];
996                         if (PeekChars(ts, 2, cp) &&
997                             JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
998                             c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
999                             SkipChars(ts, 2);
1000                         }
1001                     } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) {
1002                         /* ECMA follows C by removing escaped newlines. */
1003                         continue;
1004                     }
1005                     break;
1006                 }
1007             }
1008             ADD_TO_TOKENBUF(c);
1009         }
1010         atom = TOKENBUF_TO_ATOM();
1011         if (!atom)
1012             goto error;
1013         tp->pos.end.lineno = (uint16)ts->lineno;
1014         tp->t_op = JSOP_STRING;
1015         tp->t_atom = atom;
1016         tt = TOK_STRING;
1017         goto out;
1018     }
1020     switch (c) {
1021       case '\n':
1022         tt = TOK_EOL;
1023         break;
1025       case ';': tt = TOK_SEMI; break;
1026       case '[': tt = TOK_LB; break;
1027       case ']': tt = TOK_RB; break;
1028       case '{': tt = TOK_LC; break;
1029       case '}': tt = TOK_RC; break;
1030       case '(': tt = TOK_LP; break;
1031       case ')': tt = TOK_RP; break;
1032       case ',': tt = TOK_COMMA; break;
1033       case '?': tt = TOK_HOOK; break;
1035       case '.':
1036         tt = TOK_DOT;
1037         break;
1039       case ':':
1040         /*
1041          * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an
1042          * object initializer, likewise for setter.
1043          */
1044         tp->t_op = JSOP_NOP;
1045         tt = TOK_COLON;
1046         break;
1048       case '|':
1049         if (MatchChar(ts, c)) {
1050             tt = TOK_OR;
1051         } else if (MatchChar(ts, '=')) {
1052             tp->t_op = JSOP_BITOR;
1053             tt = TOK_ASSIGN;
1054         } else {
1055             tt = TOK_BITOR;
1056         }
1057         break;
1059       case '^':
1060         if (MatchChar(ts, '=')) {
1061             tp->t_op = JSOP_BITXOR;
1062             tt = TOK_ASSIGN;
1063         } else {
1064             tt = TOK_BITXOR;
1065         }
1066         break;
1068       case '&':
1069         if (MatchChar(ts, c)) {
1070             tt = TOK_AND;
1071         } else if (MatchChar(ts, '=')) {
1072             tp->t_op = JSOP_BITAND;
1073             tt = TOK_ASSIGN;
1074         } else {
1075             tt = TOK_BITAND;
1076         }
1077         break;
1079       case '=':
1080         if (MatchChar(ts, c)) {
1081 #if JS_HAS_TRIPLE_EQOPS
1082             tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq;
1083 #else
1084             tp->t_op = cx->jsop_eq;
1085 #endif
1086             tt = TOK_EQOP;
1087         } else {
1088             tp->t_op = JSOP_NOP;
1089             tt = TOK_ASSIGN;
1090         }
1091         break;
1093       case '!':
1094         if (MatchChar(ts, '=')) {
1095 #if JS_HAS_TRIPLE_EQOPS
1096             tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;
1097 #else
1098             tp->t_op = cx->jsop_ne;
1099 #endif
1100             tt = TOK_EQOP;
1101         } else {
1102             tp->t_op = JSOP_NOT;
1103             tt = TOK_UNARYOP;
1104         }
1105         break;
1107       case '<':
1108         /* NB: treat HTML begin-comment as comment-till-end-of-line */
1109         if (MatchChar(ts, '!')) {
1110             if (MatchChar(ts, '-')) {
1111                 if (MatchChar(ts, '-'))
1112                     goto skipline;
1113                 UngetChar(ts, '-');
1114             }
1115             UngetChar(ts, '!');
1116         }
1117         if (MatchChar(ts, c)) {
1118             tp->t_op = JSOP_LSH;
1119             tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
1120         } else {
1121             tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
1122             tt = TOK_RELOP;
1123         }
1124         break;
1126       case '>':
1127         if (MatchChar(ts, c)) {
1128             tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;
1129             tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
1130         } else {
1131             tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
1132             tt = TOK_RELOP;
1133         }
1134         break;
1136       case '*':
1137         tp->t_op = JSOP_MUL;
1138         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;
1139         break;
1141       case '/':
1142         if (MatchChar(ts, '/')) {
1143             /*
1144              * Hack for source filters such as the Mozilla XUL preprocessor:
1145              * "//@line 123\n" sets the number of the *next* line after the
1146              * comment to 123.
1147              */
1148             if (JS_HAS_ATLINE_OPTION(cx)) {
1149                 jschar cp[5];
1150                 uintN i, line, temp;
1151                 char filename[1024];
1153                 if (PeekChars(ts, 5, cp) &&
1154                     cp[0] == '@' &&
1155                     cp[1] == 'l' &&
1156                     cp[2] == 'i' &&
1157                     cp[3] == 'n' &&
1158                     cp[4] == 'e') {
1159                     SkipChars(ts, 5);
1160                     while ((c = GetChar(ts)) != '\n' && JS_ISSPACE(c))
1161                         continue;
1162                     if (JS7_ISDEC(c)) {
1163                         line = JS7_UNDEC(c);
1164                         while ((c = GetChar(ts)) != EOF && JS7_ISDEC(c)) {
1165                             temp = 10 * line + JS7_UNDEC(c);
1166                             if (temp < line) {
1167                                 /* Ignore overlarge line numbers. */
1168                                 goto skipline;
1169                             }
1170                             line = temp;
1171                         }
1172                         while (c != '\n' && JS_ISSPACE(c))
1173                             c = GetChar(ts);
1174                         i = 0;
1175                         if (c == '"') {
1176                             while ((c = GetChar(ts)) != EOF && c != '"') {
1177                                 if (c == '\n') {
1178                                     UngetChar(ts, c);
1179                                     goto skipline;
1180                                 }
1181                                 if ((c >> 8) != 0 || i >= sizeof filename - 1)
1182                                     goto skipline;
1183                                 filename[i++] = (char) c;
1184                             }
1185                             if (c == '"') {
1186                                 while ((c = GetChar(ts)) != '\n' &&
1187                                        JS_ISSPACE(c)) {
1188                                     continue;
1189                                 }
1190                             }
1191                         }
1192                         filename[i] = '\0';
1193                         if (c == '\n') {
1194                             if (i > 0) {
1195                                 if (ts->flags & TSF_OWNFILENAME)
1196                                     JS_free(cx, (void *) ts->filename);
1197                                 ts->filename = JS_strdup(cx, filename);
1198                                 if (!ts->filename)
1199                                     goto error;
1200                                 ts->flags |= TSF_OWNFILENAME;
1201                             }
1202                             ts->lineno = line;
1203                         }
1204                     }
1205                     UngetChar(ts, c);
1206                 }
1207             }
1209 skipline:
1210             while ((c = GetChar(ts)) != EOF && c != '\n')
1211                 continue;
1212             UngetChar(ts, c);
1213             goto retry;
1214         }
1215         if (MatchChar(ts, '*')) {
1216             while ((c = GetChar(ts)) != EOF &&
1217                    !(c == '*' && MatchChar(ts, '/'))) {
1218                 /* Ignore all characters until comment close. */
1219             }
1220             if (c == EOF) {
1221                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1222                                             JSMSG_UNTERMINATED_COMMENT);
1223                 goto error;
1224             }
1225             goto retry;
1226         }
1228 #if JS_HAS_REGEXPS
1229         if (ts->flags & TSF_OPERAND) {
1230             JSObject *obj;
1231             uintN flags;
1233             INIT_TOKENBUF();
1234             while ((c = GetChar(ts)) != '/') {
1235                 if (c == '\n' || c == EOF) {
1236                     UngetChar(ts, c);
1237                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1238                                                 JSMSG_UNTERMINATED_REGEXP);
1239                     goto error;
1240                 }
1241                 if (c == '\\') {
1242                     ADD_TO_TOKENBUF(c);
1243                     c = GetChar(ts);
1244                 }
1245                 ADD_TO_TOKENBUF(c);
1246             }
1247             for (flags = 0; ; ) {
1248                 if (MatchChar(ts, 'g'))
1249                     flags |= JSREG_GLOB;
1250                 else if (MatchChar(ts, 'i'))
1251                     flags |= JSREG_FOLD;
1252                 else if (MatchChar(ts, 'm'))
1253                     flags |= JSREG_MULTILINE;
1254                 else
1255                     break;
1256             }
1257             c = PeekChar(ts);
1258             if (JS7_ISLET(c)) {
1259                 tp->ptr = ts->linebuf.ptr - 1;
1260                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1261                                             JSMSG_BAD_REGEXP_FLAG);
1262                 (void) GetChar(ts);
1263                 goto error;
1264             }
1265             obj = js_NewRegExpObject(cx, ts,
1266                                      TOKENBUF_BASE(),
1267                                      TOKENBUF_LENGTH(),
1268                                      flags);
1269             if (!obj)
1270                 goto error;
1271             atom = js_AtomizeObject(cx, obj, 0);
1272             if (!atom)
1273                 goto error;
1275             /*
1276              * If the regexp's script is one-shot, we can avoid the extra
1277              * fork-on-exec costs of JSOP_REGEXP by selecting JSOP_OBJECT.
1278              * Otherwise, to avoid incorrect proto, parent, and lastIndex
1279              * sharing among threads and sequentially across re-execution,
1280              * select JSOP_REGEXP.
1281              */
1282             tp->t_op = (cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))
1283                        ? JSOP_OBJECT
1284                        : JSOP_REGEXP;
1285             tp->t_atom = atom;
1286             tt = TOK_OBJECT;
1287             break;
1288         }
1289 #endif /* JS_HAS_REGEXPS */
1291         tp->t_op = JSOP_DIV;
1292         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
1293         break;
1295       case '%':
1296         tp->t_op = JSOP_MOD;
1297         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
1298         break;
1300       case '~':
1301         tp->t_op = JSOP_BITNOT;
1302         tt = TOK_UNARYOP;
1303         break;
1305       case '+':
1306         if (MatchChar(ts, '=')) {
1307             tp->t_op = JSOP_ADD;
1308             tt = TOK_ASSIGN;
1309         } else if (MatchChar(ts, c)) {
1310             tt = TOK_INC;
1311         } else {
1312             tp->t_op = JSOP_POS;
1313             tt = TOK_PLUS;
1314         }
1315         break;
1317       case '-':
1318         if (MatchChar(ts, '=')) {
1319             tp->t_op = JSOP_SUB;
1320             tt = TOK_ASSIGN;
1321         } else if (MatchChar(ts, c)) {
1322             if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE))
1323                 goto skipline;
1324             tt = TOK_DEC;
1325         } else {
1326             tp->t_op = JSOP_NEG;
1327             tt = TOK_MINUS;
1328         }
1329         ts->flags |= TSF_DIRTYLINE;
1330         break;
1332 #if JS_HAS_SHARP_VARS
1333       case '#':
1334       {
1335         uint32 n;
1337         c = GetChar(ts);
1338         if (!JS7_ISDEC(c)) {
1339             UngetChar(ts, c);
1340             goto badchar;
1341         }
1342         n = (uint32)JS7_UNDEC(c);
1343         for (;;) {
1344             c = GetChar(ts);
1345             if (!JS7_ISDEC(c))
1346                 break;
1347             n = 10 * n + JS7_UNDEC(c);
1348             if (n >= ATOM_INDEX_LIMIT) {
1349                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1350                                             JSMSG_SHARPVAR_TOO_BIG);
1351                 goto error;
1352             }
1353         }
1354         tp->t_dval = (jsdouble) n;
1355         if (JS_HAS_STRICT_OPTION(cx) &&
1356             (c == '=' || c == '#')) {
1357             char buf[20];
1358             JS_snprintf(buf, sizeof buf, "#%u%c", n, c);
1359             if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1360                                              JSREPORT_WARNING |
1361                                              JSREPORT_STRICT,
1362                                              JSMSG_DEPRECATED_USAGE,
1363                                              buf)) {
1364                 goto error;
1365             }
1366         }
1367         if (c == '=')
1368             tt = TOK_DEFSHARP;
1369         else if (c == '#')
1370             tt = TOK_USESHARP;
1371         else
1372             goto badchar;
1373         break;
1374       }
1376       badchar:
1377 #endif /* JS_HAS_SHARP_VARS */
1379       default:
1380         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1381                                     JSMSG_ILLEGAL_CHARACTER);
1382         goto error;
1383     }
1385 out:
1386     JS_ASSERT(tt < TOK_LIMIT);
1387     tp->pos.end.index = ts->linepos +
1388                         (ts->linebuf.ptr - ts->linebuf.base) -
1389                         ts->ungetpos;
1390     tp->type = tt;
1391     return tt;
1393 error:
1394     tt = TOK_ERROR;
1395     ts->flags |= TSF_ERROR;
1396     goto out;
1398 #undef INIT_TOKENBUF
1399 #undef TRIM_TOKENBUF
1400 #undef TOKENBUF_LENGTH
1401 #undef TOKENBUF_BASE
1402 #undef TOKENBUF_CHAR
1403 #undef TOKENBUF_TO_ATOM
1404 #undef ADD_TO_TOKENBUF
1407 void
1408 js_UngetToken(JSTokenStream *ts)
1410     JS_ASSERT(ts->lookahead < NTOKENS_MASK);
1411     if (ts->flags & TSF_ERROR)
1412         return;
1413     ts->lookahead++;
1414     ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
1417 JSBool
1418 js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
1420     if (js_GetToken(cx, ts) == tt)
1421         return JS_TRUE;
1422     js_UngetToken(ts);
1423     return JS_FALSE;