1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set sw=4 ts=8 et tw=78:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
20  * The Initial Developer of the Original Code is
21  * Netscape Communications Corporation.
22  * Portions created by the Initial Developer are Copyright (C) 1998
23  * the Initial Developer. All Rights Reserved.
24  *
25  * Contributor(s):
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either of the GNU General Public License Version 2 or later (the "GPL"),
29  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40 
41 /*
42  * JS lexical scanner.
43  */
44 #include "jsstddef.h"
45 #include <stdio.h>      /* first to avoid trouble on some systems */
46 #include <errno.h>
47 #include <limits.h>
48 #include <math.h>
49 #ifdef HAVE_MEMORY_H
50 #include <memory.h>
51 #endif
52 #include <stdarg.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include "jstypes.h"
56 #include "jsarena.h" /* Added by JSIFY */
57 #include "jsutil.h" /* Added by JSIFY */
58 #include "jsdtoa.h"
59 #include "jsprf.h"
60 #include "jsapi.h"
61 #include "jsatom.h"
62 #include "jscntxt.h"
63 #include "jsconfig.h"
64 #include "jsemit.h"
65 #include "jsexn.h"
66 #include "jsnum.h"
67 #include "jsopcode.h"
68 #include "jsregexp.h"
69 #include "jsscan.h"
70 #include "jsscript.h"
71 
72 #if JS_HAS_XML_SUPPORT
73 #include "jsparse.h"
74 #include "jsxml.h"
75 #endif
76 
77 #define JS_KEYWORD(keyword, type, op, version) \
78     const char js_##keyword##_str[] = #keyword;
79 #include "jskeyword.tbl"
80 #undef JS_KEYWORD
81 
82 struct keyword {
83     const char  *chars;         /* C string with keyword text */
84     JSTokenType tokentype;      /* JSTokenType */
85     JSOp        op;             /* JSOp */
86     JSVersion   version;        /* JSVersion */
87 };
88 
89 static const struct keyword keyword_defs[] = {
90 #define JS_KEYWORD(keyword, type, op, version) \
91     {js_##keyword##_str, type, op, version},
92 #include "jskeyword.tbl"
93 #undef JS_KEYWORD
94 };
95 
96 #define KEYWORD_COUNT (sizeof keyword_defs / sizeof keyword_defs[0])
97 
98 static const struct keyword *
FindKeyword(const jschar * s,size_t length)99 FindKeyword(const jschar *s, size_t length)
100 {
101     register size_t i;
102     const struct keyword *kw;
103     const char *chars;
104 
105     JS_ASSERT(length != 0);
106 
107 #define JSKW_LENGTH()           length
108 #define JSKW_AT(column)         s[column]
109 #define JSKW_GOT_MATCH(index)   i = (index); goto got_match;
110 #define JSKW_TEST_GUESS(index)  i = (index); goto test_guess;
111 #define JSKW_NO_MATCH()         goto no_match;
112 #include "jsautokw.h"
113 #undef JSKW_NO_MATCH
114 #undef JSKW_TEST_GUESS
115 #undef JSKW_GOT_MATCH
116 #undef JSKW_AT
117 #undef JSKW_LENGTH
118 
119   got_match:
120     return &keyword_defs[i];
121 
122   test_guess:
123     kw = &keyword_defs[i];
124     chars = kw->chars;
125     do {
126         if (*s++ != (unsigned char)(*chars++))
127             goto no_match;
128     } while (--length != 0);
129     return kw;
130 
131   no_match:
132     return NULL;
133 }
134 
135 JSTokenType
js_CheckKeyword(const jschar * str,size_t length)136 js_CheckKeyword(const jschar *str, size_t length)
137 {
138     const struct keyword *kw;
139 
140     JS_ASSERT(length != 0);
141     kw = FindKeyword(str, length);
142     return kw ? kw->tokentype : TOK_EOF;
143 }
144 
145 JS_FRIEND_API(void)
js_MapKeywords(void (* mapfun)(const char *))146 js_MapKeywords(void (*mapfun)(const char *))
147 {
148     size_t i;
149 
150     for (i = 0; i != KEYWORD_COUNT; ++i)
151         mapfun(keyword_defs[i].chars);
152 }
153 
154 JSTokenStream *
js_NewTokenStream(JSContext * cx,const jschar * base,size_t length,const char * filename,uintN lineno,JSPrincipals * principals)155 js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,
156                   const char *filename, uintN lineno,
157                   JSPrincipals *principals)
158 {
159     JSTokenStream *ts;
160 
161     ts = js_NewBufferTokenStream(cx, base, length);
162     if (!ts)
163         return NULL;
164     ts->filename = filename;
165     ts->lineno = lineno;
166     if (principals)
167         JSPRINCIPALS_HOLD(cx, principals);
168     ts->principals = principals;
169     return ts;
170 }
171 
172 #define TBMIN   64
173 
174 static JSBool
GrowTokenBuf(JSStringBuffer * sb,size_t newlength)175 GrowTokenBuf(JSStringBuffer *sb, size_t newlength)
176 {
177     JSContext *cx;
178     jschar *base;
179     ptrdiff_t offset, length;
180     size_t tbsize;
181     JSArenaPool *pool;
182 
183     cx = sb->data;
184     base = sb->base;
185     offset = PTRDIFF(sb->ptr, base, jschar);
186     pool = &cx->tempPool;
187     if (!base) {
188         tbsize = TBMIN * sizeof(jschar);
189         length = TBMIN - 1;
190         JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize);
191     } else {
192         length = PTRDIFF(sb->limit, base, jschar);
193         if ((size_t)length >= ~(size_t)0 / sizeof(jschar)) {
194             base = NULL;
195         } else {
196             tbsize = (length + 1) * sizeof(jschar);
197             length += length + 1;
198             JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize);
199         }
200     }
201     if (!base) {
202         JS_ReportOutOfMemory(cx);
203         sb->base = STRING_BUFFER_ERROR_BASE;
204         return JS_FALSE;
205     }
206     sb->base = base;
207     sb->limit = base + length;
208     sb->ptr = base + offset;
209     return JS_TRUE;
210 }
211 
212 JS_FRIEND_API(JSTokenStream *)
js_NewBufferTokenStream(JSContext * cx,const jschar * base,size_t length)213 js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
214 {
215     size_t nb;
216     JSTokenStream *ts;
217 
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->tokenbuf.grow = GrowTokenBuf;
231     ts->tokenbuf.data = cx;
232     ts->listener = cx->runtime->sourceHandler;
233     ts->listenerData = cx->runtime->sourceHandlerData;
234     return ts;
235 }
236 
237 JS_FRIEND_API(JSTokenStream *)
js_NewFileTokenStream(JSContext * cx,const char * filename,FILE * defaultfp)238 js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp)
239 {
240     jschar *base;
241     JSTokenStream *ts;
242     FILE *file;
243 
244     JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool,
245                            JS_LINE_LIMIT * sizeof(jschar));
246     if (!base)
247         return NULL;
248     ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT);
249     if (!ts)
250         return NULL;
251     if (!filename || strcmp(filename, "-") == 0) {
252         file = defaultfp;
253     } else {
254         file = fopen(filename, "r");
255         if (!file) {
256             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
257                                  filename, "No such file or directory");
258             return NULL;
259         }
260     }
261     ts->userbuf.ptr = ts->userbuf.limit;
262     ts->file = file;
263     ts->filename = filename;
264     return ts;
265 }
266 
267 JS_FRIEND_API(JSBool)
js_CloseTokenStream(JSContext * cx,JSTokenStream * ts)268 js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)
269 {
270     if (ts->flags & TSF_OWNFILENAME)
271         JS_free(cx, (void *) ts->filename);
272     if (ts->principals)
273         JSPRINCIPALS_DROP(cx, ts->principals);
274     return !ts->file || fclose(ts->file) == 0;
275 }
276 
277 JS_FRIEND_API(int)
js_fgets(char * buf,int size,FILE * file)278 js_fgets(char *buf, int size, FILE *file)
279 {
280     int n, i, c;
281     JSBool crflag;
282 
283     n = size - 1;
284     if (n < 0)
285         return -1;
286 
287     crflag = JS_FALSE;
288     for (i = 0; i < n && (c = getc(file)) != EOF; i++) {
289         buf[i] = c;
290         if (c == '\n') {        /* any \n ends a line */
291             i++;                /* keep the \n; we know there is room for \0 */
292             break;
293         }
294         if (crflag) {           /* \r not followed by \n ends line at the \r */
295             ungetc(c, file);
296             break;              /* and overwrite c in buf with \0 */
297         }
298         crflag = (c == '\r');
299     }
300 
301     buf[i] = '\0';
302     return i;
303 }
304 
305 static int32
GetChar(JSTokenStream * ts)306 GetChar(JSTokenStream *ts)
307 {
308     int32 c;
309     ptrdiff_t i, j, len, olen;
310     JSBool crflag;
311     char cbuf[JS_LINE_LIMIT];
312     jschar *ubuf, *nl;
313 
314     if (ts->ungetpos != 0) {
315         c = ts->ungetbuf[--ts->ungetpos];
316     } else {
317         do {
318             if (ts->linebuf.ptr == ts->linebuf.limit) {
319                 len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
320                 if (len <= 0) {
321                     if (!ts->file) {
322                         ts->flags |= TSF_EOF;
323                         return EOF;
324                     }
325 
326                     /* Fill ts->userbuf so that \r and \r\n convert to \n. */
327                     crflag = (ts->flags & TSF_CRFLAG) != 0;
328                     len = js_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file);
329                     if (len <= 0) {
330                         ts->flags |= TSF_EOF;
331                         return EOF;
332                     }
333                     olen = len;
334                     ubuf = ts->userbuf.base;
335                     i = 0;
336                     if (crflag) {
337                         ts->flags &= ~TSF_CRFLAG;
338                         if (cbuf[0] != '\n') {
339                             ubuf[i++] = '\n';
340                             len++;
341                             ts->linepos--;
342                         }
343                     }
344                     for (j = 0; i < len; i++, j++)
345                         ubuf[i] = (jschar) (unsigned char) cbuf[j];
346                     ts->userbuf.limit = ubuf + len;
347                     ts->userbuf.ptr = ubuf;
348                 }
349                 if (ts->listener) {
350                     ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len,
351                                  &ts->listenerTSData, ts->listenerData);
352                 }
353 
354                 nl = ts->saveEOL;
355                 if (!nl) {
356                     /*
357                      * Any one of \n, \r, or \r\n ends a line (the longest
358                      * match wins).  Also allow the Unicode line and paragraph
359                      * separators.
360                      */
361                     for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) {
362                         /*
363                          * Try to prevent value-testing on most characters by
364                          * filtering out characters that aren't 000x or 202x.
365                          */
366                         if ((*nl & 0xDFD0) == 0) {
367                             if (*nl == '\n')
368                                 break;
369                             if (*nl == '\r') {
370                                 if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
371                                     nl++;
372                                 break;
373                             }
374                             if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR)
375                                 break;
376                         }
377                     }
378                 }
379 
380                 /*
381                  * If there was a line terminator, copy thru it into linebuf.
382                  * Else copy JS_LINE_LIMIT-1 bytes into linebuf.
383                  */
384                 if (nl < ts->userbuf.limit)
385                     len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;
386                 if (len >= JS_LINE_LIMIT) {
387                     len = JS_LINE_LIMIT - 1;
388                     ts->saveEOL = nl;
389                 } else {
390                     ts->saveEOL = NULL;
391                 }
392                 js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);
393                 ts->userbuf.ptr += len;
394                 olen = len;
395 
396                 /*
397                  * Make sure linebuf contains \n for EOL (don't do this in
398                  * userbuf because the user's string might be readonly).
399                  */
400                 if (nl < ts->userbuf.limit) {
401                     if (*nl == '\r') {
402                         if (ts->linebuf.base[len-1] == '\r') {
403                             /*
404                              * Does the line segment end in \r?  We must check
405                              * for a \n at the front of the next segment before
406                              * storing a \n into linebuf.  This case matters
407                              * only when we're reading from a file.
408                              */
409                             if (nl + 1 == ts->userbuf.limit && ts->file) {
410                                 len--;
411                                 ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */
412                                 if (len == 0) {
413                                     /*
414                                      * This can happen when a segment ends in
415                                      * \r\r.  Start over.  ptr == limit in this
416                                      * case, so we'll fall into buffer-filling
417                                      * code.
418                                      */
419                                     return GetChar(ts);
420                                 }
421                             } else {
422                                 ts->linebuf.base[len-1] = '\n';
423                             }
424                         }
425                     } else if (*nl == '\n') {
426                         if (nl > ts->userbuf.base &&
427                             nl[-1] == '\r' &&
428                             ts->linebuf.base[len-2] == '\r') {
429                             len--;
430                             JS_ASSERT(ts->linebuf.base[len] == '\n');
431                             ts->linebuf.base[len-1] = '\n';
432                         }
433                     } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) {
434                         ts->linebuf.base[len-1] = '\n';
435                     }
436                 }
437 
438                 /* Reset linebuf based on adjusted segment length. */
439                 ts->linebuf.limit = ts->linebuf.base + len;
440                 ts->linebuf.ptr = ts->linebuf.base;
441 
442                 /* Update position of linebuf within physical userbuf line. */
443                 if (!(ts->flags & TSF_NLFLAG))
444                     ts->linepos += ts->linelen;
445                 else
446                     ts->linepos = 0;
447                 if (ts->linebuf.limit[-1] == '\n')
448                     ts->flags |= TSF_NLFLAG;
449                 else
450                     ts->flags &= ~TSF_NLFLAG;
451 
452                 /* Update linelen from original segment length. */
453                 ts->linelen = olen;
454             }
455             c = *ts->linebuf.ptr++;
456         } while (JS_ISFORMAT(c));
457     }
458     if (c == '\n')
459         ts->lineno++;
460     return c;
461 }
462 
463 static void
UngetChar(JSTokenStream * ts,int32 c)464 UngetChar(JSTokenStream *ts, int32 c)
465 {
466     if (c == EOF)
467         return;
468     JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
469     if (c == '\n')
470         ts->lineno--;
471     ts->ungetbuf[ts->ungetpos++] = (jschar)c;
472 }
473 
474 static int32
PeekChar(JSTokenStream * ts)475 PeekChar(JSTokenStream *ts)
476 {
477     int32 c;
478 
479     c = GetChar(ts);
480     UngetChar(ts, c);
481     return c;
482 }
483 
484 /*
485  * Peek n chars ahead into ts.  Return true if n chars were read, false if
486  * there weren't enough characters in the input stream.  This function cannot
487  * be used to peek into or past a newline.
488  */
489 static JSBool
PeekChars(JSTokenStream * ts,intN n,jschar * cp)490 PeekChars(JSTokenStream *ts, intN n, jschar *cp)
491 {
492     intN i, j;
493     int32 c;
494 
495     for (i = 0; i < n; i++) {
496         c = GetChar(ts);
497         if (c == EOF)
498             break;
499         if (c == '\n') {
500             UngetChar(ts, c);
501             break;
502         }
503         cp[i] = (jschar)c;
504     }
505     for (j = i - 1; j >= 0; j--)
506         UngetChar(ts, cp[j]);
507     return i == n;
508 }
509 
510 static void
SkipChars(JSTokenStream * ts,intN n)511 SkipChars(JSTokenStream *ts, intN n)
512 {
513     while (--n >= 0)
514         GetChar(ts);
515 }
516 
517 static JSBool
MatchChar(JSTokenStream * ts,int32 expect)518 MatchChar(JSTokenStream *ts, int32 expect)
519 {
520     int32 c;
521 
522     c = GetChar(ts);
523     if (c == expect)
524         return JS_TRUE;
525     UngetChar(ts, c);
526     return JS_FALSE;
527 }
528 
529 static JSBool
ReportCompileErrorNumber(JSContext * cx,void * handle,uintN flags,uintN errorNumber,JSErrorReport * report,JSBool charArgs,va_list ap)530 ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags,
531                          uintN errorNumber, JSErrorReport *report,
532                          JSBool charArgs, va_list ap)
533 {
534     JSTempValueRooter linetvr;
535     JSString *linestr = NULL;
536     JSTokenStream *ts = NULL;
537     JSCodeGenerator *cg = NULL;
538     JSParseNode *pn = NULL;
539     JSErrorReporter onError;
540     JSTokenPos *tp;
541     JSStackFrame *fp;
542     uintN index;
543     char *message;
544     JSBool warning;
545 
546     memset(report, 0, sizeof (struct JSErrorReport));
547     report->flags = flags;
548     report->errorNumber = errorNumber;
549     message = NULL;
550 
551     if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL,
552                                  errorNumber, &message, report, &warning,
553                                  charArgs, ap)) {
554         return JS_FALSE;
555     }
556 
557     JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &linetvr);
558 
559     switch (flags & JSREPORT_HANDLE) {
560       case JSREPORT_TS:
561         ts = handle;
562         break;
563       case JSREPORT_CG:
564         cg = handle;
565         break;
566       case JSREPORT_PN:
567         pn = handle;
568         ts = pn->pn_ts;
569         break;
570     }
571 
572     JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);
573     /*
574      * We are typically called with non-null ts and null cg from jsparse.c.
575      * We can be called with null ts from the regexp compilation functions.
576      * The code generator (jsemit.c) may pass null ts and non-null cg.
577      */
578     do {
579         if (ts) {
580             report->filename = ts->filename;
581             if (pn) {
582                 report->lineno = pn->pn_pos.begin.lineno;
583                 if (report->lineno != ts->lineno)
584                     break;
585             }
586             report->lineno = ts->lineno;
587             linestr = js_NewStringCopyN(cx, ts->linebuf.base,
588                                         PTRDIFF(ts->linebuf.limit,
589                                                 ts->linebuf.base,
590                                                 jschar),
591                                         0);
592             linetvr.u.string = linestr;
593             report->linebuf = linestr
594                               ? JS_GetStringBytes(linestr)
595                               : NULL;
596             tp = &ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos;
597             if (pn)
598                 tp = &pn->pn_pos;
599 
600             /*
601              * FIXME: What should instead happen here is that we should
602              * find error-tokens in userbuf, if !ts->file.  That will
603              * allow us to deliver a more helpful error message, which
604              * includes all or part of the bad string or bad token.  The
605              * code here yields something that looks truncated.
606              * See https://bugzilla.mozilla.org/show_bug.cgi?id=352970
607              */
608             index = 0;
609             if (tp->begin.lineno == tp->end.lineno) {
610                 if (tp->begin.index < ts->linepos)
611                     break;
612 
613                 index = tp->begin.index - ts->linepos;
614             }
615 
616             report->tokenptr = linestr ? report->linebuf + index : NULL;
617             report->uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL;
618             report->uctokenptr = linestr ? report->uclinebuf + index : NULL;
619             break;
620         }
621 
622         if (cg) {
623             report->filename = cg->filename;
624             report->lineno = CG_CURRENT_LINE(cg);
625             break;
626         }
627 
628         /*
629          * If we can't find out where the error was based on the current
630          * frame, see if the next frame has a script/pc combo we can use.
631          */
632         for (fp = cx->fp; fp; fp = fp->down) {
633             if (fp->script && fp->pc) {
634                 report->filename = fp->script->filename;
635                 report->lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
636                 break;
637             }
638         }
639     } while (0);
640 
641     /*
642      * If there's a runtime exception type associated with this error
643      * number, set that as the pending exception.  For errors occuring at
644      * compile time, this is very likely to be a JSEXN_SYNTAXERR.
645      *
646      * If an exception is thrown but not caught, the JSREPORT_EXCEPTION
647      * flag will be set in report.flags.  Proper behavior for an error
648      * reporter is to ignore a report with this flag for all but top-level
649      * compilation errors.  The exception will remain pending, and so long
650      * as the non-top-level "load", "eval", or "compile" native function
651      * returns false, the top-level reporter will eventually receive the
652      * uncaught exception report.
653      *
654      * XXX it'd probably be best if there was only one call to this
655      * function, but there seem to be two error reporter call points.
656      */
657     onError = cx->errorReporter;
658 
659     /*
660      * Try to raise an exception only if there isn't one already set --
661      * otherwise the exception will describe the last compile-time error,
662      * which is likely spurious.
663      */
664     if (!ts || !(ts->flags & TSF_ERROR)) {
665         if (js_ErrorToException(cx, message, report))
666             onError = NULL;
667     }
668 
669     /*
670      * Suppress any compile-time errors that don't occur at the top level.
671      * This may still fail, as interplevel may be zero in contexts where we
672      * don't really want to call the error reporter, as when js is called
673      * by other code which could catch the error.
674      */
675     if (cx->interpLevel != 0 && !JSREPORT_IS_WARNING(flags))
676         onError = NULL;
677 
678     if (onError) {
679         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
680 
681         /*
682          * If debugErrorHook is present then we give it a chance to veto
683          * sending the error on to the regular error reporter.
684          */
685         if (hook && !hook(cx, message, report,
686                           cx->runtime->debugErrorHookData)) {
687             onError = NULL;
688         }
689     }
690     if (onError)
691         (*onError)(cx, message, report);
692 
693     if (message)
694         JS_free(cx, message);
695     if (report->ucmessage)
696         JS_free(cx, (void *)report->ucmessage);
697 
698     JS_POP_TEMP_ROOT(cx, &linetvr);
699 
700     if (ts && !JSREPORT_IS_WARNING(flags)) {
701         /* Set the error flag to suppress spurious reports. */
702         ts->flags |= TSF_ERROR;
703     }
704 
705     return warning;
706 }
707 
708 JSBool
js_ReportCompileErrorNumber(JSContext * cx,void * handle,uintN flags,uintN errorNumber,...)709 js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags,
710                             uintN errorNumber, ...)
711 {
712     va_list ap;
713     JSErrorReport report;
714     JSBool warning;
715 
716     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
717         return JS_TRUE;
718 
719     va_start(ap, errorNumber);
720     warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber,
721                                        &report, JS_TRUE, ap);
722     va_end(ap);
723 
724     /*
725      * We have to do this here because js_ReportCompileErrorNumberUC doesn't
726      * need to do this.
727      */
728     if (report.messageArgs) {
729         int i = 0;
730         while (report.messageArgs[i])
731             JS_free(cx, (void *)report.messageArgs[i++]);
732         JS_free(cx, (void *)report.messageArgs);
733     }
734 
735     return warning;
736 }
737 
738 JSBool
js_ReportCompileErrorNumberUC(JSContext * cx,void * handle,uintN flags,uintN errorNumber,...)739 js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags,
740                               uintN errorNumber, ...)
741 {
742     va_list ap;
743     JSErrorReport report;
744     JSBool warning;
745 
746     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
747         return JS_TRUE;
748 
749     va_start(ap, errorNumber);
750     warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber,
751                                        &report, JS_FALSE, ap);
752     va_end(ap);
753 
754     if (report.messageArgs)
755         JS_free(cx, (void *)report.messageArgs);
756 
757     return warning;
758 }
759 
760 static JSBool
GrowStringBuffer(JSStringBuffer * sb,size_t newlength)761 GrowStringBuffer(JSStringBuffer *sb, size_t newlength)
762 {
763     ptrdiff_t offset;
764     jschar *bp;
765 
766     offset = PTRDIFF(sb->ptr, sb->base, jschar);
767     JS_ASSERT(offset >= 0);
768     newlength += offset + 1;
769     if ((size_t)offset < newlength && newlength < ~(size_t)0 / sizeof(jschar))
770         bp = realloc(sb->base, newlength * sizeof(jschar));
771     else
772         bp = NULL;
773     if (!bp) {
774         free(sb->base);
775         sb->base = STRING_BUFFER_ERROR_BASE;
776         return JS_FALSE;
777     }
778     sb->base = bp;
779     sb->ptr = bp + offset;
780     sb->limit = bp + newlength - 1;
781     return JS_TRUE;
782 }
783 
784 static void
FreeStringBuffer(JSStringBuffer * sb)785 FreeStringBuffer(JSStringBuffer *sb)
786 {
787     JS_ASSERT(STRING_BUFFER_OK(sb));
788     if (sb->base)
789         free(sb->base);
790 }
791 
792 void
js_InitStringBuffer(JSStringBuffer * sb)793 js_InitStringBuffer(JSStringBuffer *sb)
794 {
795     sb->base = sb->limit = sb->ptr = NULL;
796     sb->data = NULL;
797     sb->grow = GrowStringBuffer;
798     sb->free = FreeStringBuffer;
799 }
800 
801 void
js_FinishStringBuffer(JSStringBuffer * sb)802 js_FinishStringBuffer(JSStringBuffer *sb)
803 {
804     sb->free(sb);
805 }
806 
807 #define ENSURE_STRING_BUFFER(sb,n) \
808     ((sb)->ptr + (n) <= (sb)->limit || sb->grow(sb, n))
809 
810 static void
FastAppendChar(JSStringBuffer * sb,jschar c)811 FastAppendChar(JSStringBuffer *sb, jschar c)
812 {
813     if (!STRING_BUFFER_OK(sb))
814         return;
815     if (!ENSURE_STRING_BUFFER(sb, 1))
816         return;
817     *sb->ptr++ = c;
818 }
819 
820 void
js_AppendChar(JSStringBuffer * sb,jschar c)821 js_AppendChar(JSStringBuffer *sb, jschar c)
822 {
823     jschar *bp;
824 
825     if (!STRING_BUFFER_OK(sb))
826         return;
827     if (!ENSURE_STRING_BUFFER(sb, 1))
828         return;
829     bp = sb->ptr;
830     *bp++ = c;
831     *bp = 0;
832     sb->ptr = bp;
833 }
834 
835 #if JS_HAS_XML_SUPPORT
836 
837 void
js_RepeatChar(JSStringBuffer * sb,jschar c,uintN count)838 js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count)
839 {
840     jschar *bp;
841 
842     if (!STRING_BUFFER_OK(sb) || count == 0)
843         return;
844     if (!ENSURE_STRING_BUFFER(sb, count))
845         return;
846     for (bp = sb->ptr; count; --count)
847         *bp++ = c;
848     *bp = 0;
849     sb->ptr = bp;
850 }
851 
852 void
js_AppendCString(JSStringBuffer * sb,const char * asciiz)853 js_AppendCString(JSStringBuffer *sb, const char *asciiz)
854 {
855     size_t length;
856     jschar *bp;
857 
858     if (!STRING_BUFFER_OK(sb) || *asciiz == '\0')
859         return;
860     length = strlen(asciiz);
861     if (!ENSURE_STRING_BUFFER(sb, length))
862         return;
863     for (bp = sb->ptr; length; --length)
864         *bp++ = (jschar) *asciiz++;
865     *bp = 0;
866     sb->ptr = bp;
867 }
868 
869 void
js_AppendJSString(JSStringBuffer * sb,JSString * str)870 js_AppendJSString(JSStringBuffer *sb, JSString *str)
871 {
872     size_t length;
873     jschar *bp;
874 
875     if (!STRING_BUFFER_OK(sb))
876         return;
877     length = JSSTRING_LENGTH(str);
878     if (length == 0 || !ENSURE_STRING_BUFFER(sb, length))
879         return;
880     bp = sb->ptr;
881     js_strncpy(bp, JSSTRING_CHARS(str), length);
882     bp += length;
883     *bp = 0;
884     sb->ptr = bp;
885 }
886 
887 static JSBool
GetXMLEntity(JSContext * cx,JSTokenStream * ts)888 GetXMLEntity(JSContext *cx, JSTokenStream *ts)
889 {
890     ptrdiff_t offset, length, i;
891     int32 c, d;
892     JSBool ispair;
893     jschar *bp, digit;
894     char *bytes;
895     JSErrNum msg;
896 
897     /* Put the entity, including the '&' already scanned, in ts->tokenbuf. */
898     offset = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar);
899     FastAppendChar(&ts->tokenbuf, '&');
900     while ((c = GetChar(ts)) != ';') {
901         if (c == EOF || c == '\n') {
902             js_ReportCompileErrorNumber(cx, ts,
903                                         JSREPORT_TS | JSREPORT_ERROR,
904                                         JSMSG_END_OF_XML_ENTITY);
905             return JS_FALSE;
906         }
907         FastAppendChar(&ts->tokenbuf, (jschar) c);
908     }
909 
910     /* Let length be the number of jschars after the '&', including the ';'. */
911     length = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) - offset;
912     bp = ts->tokenbuf.base + offset;
913     c = d = 0;
914     ispair = JS_FALSE;
915     if (length > 2 && bp[1] == '#') {
916         /* Match a well-formed XML Character Reference. */
917         i = 2;
918         if (length > 3 && JS_TOLOWER(bp[i]) == 'x') {
919             if (length > 9)     /* at most 6 hex digits allowed */
920                 goto badncr;
921             while (++i < length) {
922                 digit = bp[i];
923                 if (!JS7_ISHEX(digit))
924                     goto badncr;
925                 c = (c << 4) + JS7_UNHEX(digit);
926             }
927         } else {
928             while (i < length) {
929                 digit = bp[i++];
930                 if (!JS7_ISDEC(digit))
931                     goto badncr;
932                 c = (c * 10) + JS7_UNDEC(digit);
933                 if (c < 0)
934                     goto badncr;
935             }
936         }
937 
938         if (0x10000 <= c && c <= 0x10FFFF) {
939             /* Form a surrogate pair (c, d) -- c is the high surrogate. */
940             d = 0xDC00 + (c & 0x3FF);
941             c = 0xD7C0 + (c >> 10);
942             ispair = JS_TRUE;
943         } else {
944             /* Enforce the http://www.w3.org/TR/REC-xml/#wf-Legalchar WFC. */
945             if (c != 0x9 && c != 0xA && c != 0xD &&
946                 !(0x20 <= c && c <= 0xD7FF) &&
947                 !(0xE000 <= c && c <= 0xFFFD)) {
948                 goto badncr;
949             }
950         }
951     } else {
952         /* Try to match one of the five XML 1.0 predefined entities. */
953         switch (length) {
954           case 3:
955             if (bp[2] == 't') {
956                 if (bp[1] == 'l')
957                     c = '<';
958                 else if (bp[1] == 'g')
959                     c = '>';
960             }
961             break;
962           case 4:
963             if (bp[1] == 'a' && bp[2] == 'm' && bp[3] == 'p')
964                 c = '&';
965             break;
966           case 5:
967             if (bp[3] == 'o') {
968                 if (bp[1] == 'a' && bp[2] == 'p' && bp[4] == 's')
969                     c = '\'';
970                 else if (bp[1] == 'q' && bp[2] == 'u' && bp[4] == 't')
971                     c = '"';
972             }
973             break;
974         }
975         if (c == 0) {
976             msg = JSMSG_UNKNOWN_XML_ENTITY;
977             goto bad;
978         }
979     }
980 
981     /* If we matched, retract ts->tokenbuf and store the entity's value. */
982     *bp++ = (jschar) c;
983     if (ispair)
984         *bp++ = (jschar) d;
985     *bp = 0;
986     ts->tokenbuf.ptr = bp;
987     return JS_TRUE;
988 
989 badncr:
990     msg = JSMSG_BAD_XML_NCR;
991 bad:
992     /* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */
993     bytes = js_DeflateString(cx, bp + 1,
994                              PTRDIFF(ts->tokenbuf.ptr, bp, jschar) - 1);
995     if (bytes) {
996         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
997                                     msg, bytes);
998         JS_free(cx, bytes);
999     }
1000     return JS_FALSE;
1001 }
1002 
1003 #endif /* JS_HAS_XML_SUPPORT */
1004 
1005 JSTokenType
js_PeekToken(JSContext * cx,JSTokenStream * ts)1006 js_PeekToken(JSContext *cx, JSTokenStream *ts)
1007 {
1008     JSTokenType tt;
1009 
1010     if (ts->lookahead != 0) {
1011         tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type;
1012     } else {
1013         tt = js_GetToken(cx, ts);
1014         js_UngetToken(ts);
1015     }
1016     return tt;
1017 }
1018 
1019 JSTokenType
js_PeekTokenSameLine(JSContext * cx,JSTokenStream * ts)1020 js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)
1021 {
1022     JSTokenType tt;
1023 
1024     if (!ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos))
1025         return TOK_EOL;
1026     ts->flags |= TSF_NEWLINES;
1027     tt = js_PeekToken(cx, ts);
1028     ts->flags &= ~TSF_NEWLINES;
1029     return tt;
1030 }
1031 
1032 /*
1033  * We have encountered a '\': check for a Unicode escape sequence after it,
1034  * returning the character code value if we found a Unicode escape sequence.
1035  * Otherwise, non-destructively return the original '\'.
1036  */
1037 static int32
GetUnicodeEscape(JSTokenStream * ts)1038 GetUnicodeEscape(JSTokenStream *ts)
1039 {
1040     jschar cp[5];
1041     int32 c;
1042 
1043     if (PeekChars(ts, 5, cp) && cp[0] == 'u' &&
1044         JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&
1045         JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4]))
1046     {
1047         c = (((((JS7_UNHEX(cp[1]) << 4)
1048                 + JS7_UNHEX(cp[2])) << 4)
1049               + JS7_UNHEX(cp[3])) << 4)
1050             + JS7_UNHEX(cp[4]);
1051         SkipChars(ts, 5);
1052         return c;
1053     }
1054     return '\\';
1055 }
1056 
1057 static JSToken *
NewToken(JSTokenStream * ts,ptrdiff_t adjust)1058 NewToken(JSTokenStream *ts, ptrdiff_t adjust)
1059 {
1060     JSToken *tp;
1061 
1062     ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
1063     tp = &CURRENT_TOKEN(ts);
1064     tp->ptr = ts->linebuf.ptr + adjust;
1065     tp->pos.begin.index = ts->linepos +
1066                           PTRDIFF(tp->ptr, ts->linebuf.base, jschar) -
1067                           ts->ungetpos;
1068     tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno;
1069     return tp;
1070 }
1071 
1072 JSTokenType
js_GetToken(JSContext * cx,JSTokenStream * ts)1073 js_GetToken(JSContext *cx, JSTokenStream *ts)
1074 {
1075     JSTokenType tt;
1076     int32 c, qc;
1077     JSToken *tp;
1078     JSAtom *atom;
1079     JSBool hadUnicodeEscape;
1080     const struct keyword *kw;
1081 
1082 #define INIT_TOKENBUF()     (ts->tokenbuf.ptr = ts->tokenbuf.base)
1083 #define TOKENBUF_LENGTH()   PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar)
1084 #define TOKENBUF_OK()       STRING_BUFFER_OK(&ts->tokenbuf)
1085 #define TOKENBUF_TO_ATOM()  (TOKENBUF_OK()                                    \
1086                              ? js_AtomizeChars(cx,                            \
1087                                                TOKENBUF_BASE(),               \
1088                                                TOKENBUF_LENGTH(),             \
1089                                                0)                             \
1090                              : NULL)
1091 #define ADD_TO_TOKENBUF(c)  FastAppendChar(&ts->tokenbuf, (jschar) (c))
1092 
1093 /* The following 4 macros should only be used when TOKENBUF_OK() is true. */
1094 #define TOKENBUF_BASE()     (ts->tokenbuf.base)
1095 #define TOKENBUF_CHAR(i)    (ts->tokenbuf.base[i])
1096 #define TRIM_TOKENBUF(i)    (ts->tokenbuf.ptr = ts->tokenbuf.base + i)
1097 #define NUL_TERM_TOKENBUF() (*ts->tokenbuf.ptr = 0)
1098 
1099     /* Check for a pushed-back token resulting from mismatching lookahead. */
1100     while (ts->lookahead != 0) {
1101         JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE));
1102         ts->lookahead--;
1103         ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
1104         tt = CURRENT_TOKEN(ts).type;
1105         if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))
1106             return tt;
1107     }
1108 
1109     /* If there was a fatal error, keep returning TOK_ERROR. */
1110     if (ts->flags & TSF_ERROR)
1111         return TOK_ERROR;
1112 
1113 #if JS_HAS_XML_SUPPORT
1114     if (ts->flags & TSF_XMLTEXTMODE) {
1115         tt = TOK_XMLSPACE;      /* veto if non-space, return TOK_XMLTEXT */
1116         tp = NewToken(ts, 0);
1117         INIT_TOKENBUF();
1118         qc = (ts->flags & TSF_XMLONLYMODE) ? '<' : '{';
1119 
1120         while ((c = GetChar(ts)) != qc && c != '<' && c != EOF) {
1121             if (c == '&' && qc == '<') {
1122                 if (!GetXMLEntity(cx, ts))
1123                     goto error;
1124                 tt = TOK_XMLTEXT;
1125                 continue;
1126             }
1127 
1128             if (!JS_ISXMLSPACE(c))
1129                 tt = TOK_XMLTEXT;
1130             ADD_TO_TOKENBUF(c);
1131         }
1132         UngetChar(ts, c);
1133 
1134         if (TOKENBUF_LENGTH() == 0) {
1135             atom = NULL;
1136         } else {
1137             atom = TOKENBUF_TO_ATOM();
1138             if (!atom)
1139                 goto error;
1140         }
1141         tp->pos.end.lineno = (uint16)ts->lineno;
1142         tp->t_op = JSOP_STRING;
1143         tp->t_atom = atom;
1144         goto out;
1145     }
1146 
1147     if (ts->flags & TSF_XMLTAGMODE) {
1148         tp = NewToken(ts, 0);
1149         c = GetChar(ts);
1150         if (JS_ISXMLSPACE(c)) {
1151             do {
1152                 c = GetChar(ts);
1153             } while (JS_ISXMLSPACE(c));
1154             UngetChar(ts, c);
1155             tt = TOK_XMLSPACE;
1156             goto out;
1157         }
1158 
1159         if (c == EOF) {
1160             tt = TOK_EOF;
1161             goto out;
1162         }
1163 
1164         INIT_TOKENBUF();
1165         if (JS_ISXMLNSSTART(c)) {
1166             JSBool sawColon = JS_FALSE;
1167 
1168             ADD_TO_TOKENBUF(c);
1169             while ((c = GetChar(ts)) != EOF && JS_ISXMLNAME(c)) {
1170                 if (c == ':') {
1171                     int nextc;
1172 
1173                     if (sawColon ||
1174                         (nextc = PeekChar(ts),
1175                          ((ts->flags & TSF_XMLONLYMODE) || nextc != '{') &&
1176                          !JS_ISXMLNAME(nextc))) {
1177                         js_ReportCompileErrorNumber(cx, ts,
1178                                                     JSREPORT_TS |
1179                                                     JSREPORT_ERROR,
1180                                                     JSMSG_BAD_XML_QNAME);
1181                         goto error;
1182                     }
1183                     sawColon = JS_TRUE;
1184                 }
1185 
1186                 ADD_TO_TOKENBUF(c);
1187             }
1188 
1189             UngetChar(ts, c);
1190             atom = TOKENBUF_TO_ATOM();
1191             if (!atom)
1192                 goto error;
1193             tp->t_op = JSOP_STRING;
1194             tp->t_atom = atom;
1195             tt = TOK_XMLNAME;
1196             goto out;
1197         }
1198 
1199         switch (c) {
1200           case '{':
1201             if (ts->flags & TSF_XMLONLYMODE)
1202                 goto bad_xml_char;
1203             tt = TOK_LC;
1204             goto out;
1205 
1206           case '=':
1207             tt = TOK_ASSIGN;
1208             goto out;
1209 
1210           case '"':
1211           case '\'':
1212             qc = c;
1213             while ((c = GetChar(ts)) != qc) {
1214                 if (c == EOF) {
1215                     js_ReportCompileErrorNumber(cx, ts,
1216                                                 JSREPORT_TS | JSREPORT_ERROR,
1217                                                 JSMSG_UNTERMINATED_STRING);
1218                     goto error;
1219                 }
1220 
1221                 /*
1222                  * XML attribute values are double-quoted when pretty-printed,
1223                  * so escape " if it is expressed directly in a single-quoted
1224                  * attribute value.
1225                  */
1226                 if (c == '"' && !(ts->flags & TSF_XMLONLYMODE)) {
1227                     JS_ASSERT(qc == '\'');
1228                     js_AppendCString(&ts->tokenbuf, js_quot_entity_str);
1229                     continue;
1230                 }
1231 
1232                 if (c == '&' && (ts->flags & TSF_XMLONLYMODE)) {
1233                     if (!GetXMLEntity(cx, ts))
1234                         goto error;
1235                     continue;
1236                 }
1237 
1238                 ADD_TO_TOKENBUF(c);
1239             }
1240             atom = TOKENBUF_TO_ATOM();
1241             if (!atom)
1242                 goto error;
1243             tp->pos.end.lineno = (uint16)ts->lineno;
1244             tp->t_op = JSOP_STRING;
1245             tp->t_atom = atom;
1246             tt = TOK_XMLATTR;
1247             goto out;
1248 
1249           case '>':
1250             tt = TOK_XMLTAGC;
1251             goto out;
1252 
1253           case '/':
1254             if (MatchChar(ts, '>')) {
1255                 tt = TOK_XMLPTAGC;
1256                 goto out;
1257             }
1258             /* FALL THROUGH */
1259 
1260           bad_xml_char:
1261           default:
1262             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1263                                         JSMSG_BAD_XML_CHARACTER);
1264             goto error;
1265         }
1266         /* NOTREACHED */
1267     }
1268 #endif /* JS_HAS_XML_SUPPORT */
1269 
1270 retry:
1271     do {
1272         c = GetChar(ts);
1273         if (c == '\n') {
1274             ts->flags &= ~TSF_DIRTYLINE;
1275             if (ts->flags & TSF_NEWLINES)
1276                 break;
1277         }
1278     } while (JS_ISSPACE(c));
1279 
1280     tp = NewToken(ts, -1);
1281     if (c == EOF) {
1282         tt = TOK_EOF;
1283         goto out;
1284     }
1285 
1286     hadUnicodeEscape = JS_FALSE;
1287     if (JS_ISIDSTART(c) ||
1288         (c == '\\' &&
1289          (c = GetUnicodeEscape(ts),
1290           hadUnicodeEscape = JS_ISIDSTART(c)))) {
1291         INIT_TOKENBUF();
1292         for (;;) {
1293             ADD_TO_TOKENBUF(c);
1294             c = GetChar(ts);
1295             if (c == '\\') {
1296                 c = GetUnicodeEscape(ts);
1297                 if (!JS_ISIDENT(c))
1298                     break;
1299                 hadUnicodeEscape = JS_TRUE;
1300             } else {
1301                 if (!JS_ISIDENT(c))
1302                     break;
1303             }
1304         }
1305         UngetChar(ts, c);
1306 
1307         /*
1308          * Check for keywords unless we saw Unicode escape or parser asks
1309          * to ignore keywords.
1310          */
1311         if (!hadUnicodeEscape &&
1312             !(ts->flags & TSF_KEYWORD_IS_NAME) &&
1313             TOKENBUF_OK() &&
1314             (kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) {
1315             if (kw->tokentype == TOK_RESERVED) {
1316                 if (!js_ReportCompileErrorNumber(cx, ts,
1317                                                  JSREPORT_TS |
1318                                                  JSREPORT_WARNING |
1319                                                  JSREPORT_STRICT,
1320                                                  JSMSG_RESERVED_ID,
1321                                                  kw->chars)) {
1322                     goto error;
1323                 }
1324             } else if (kw->version <= JSVERSION_NUMBER(cx)) {
1325                 tt = kw->tokentype;
1326                 tp->t_op = (JSOp) kw->op;
1327                 goto out;
1328             }
1329         }
1330 
1331         atom = TOKENBUF_TO_ATOM();
1332         if (!atom)
1333             goto error;
1334         tp->t_op = JSOP_NAME;
1335         tp->t_atom = atom;
1336         tt = TOK_NAME;
1337         goto out;
1338     }
1339 
1340     if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
1341         jsint radix;
1342         const jschar *endptr;
1343         jsdouble dval;
1344 
1345         radix = 10;
1346         INIT_TOKENBUF();
1347 
1348         if (c == '0') {
1349             ADD_TO_TOKENBUF(c);
1350             c = GetChar(ts);
1351             if (JS_TOLOWER(c) == 'x') {
1352                 ADD_TO_TOKENBUF(c);
1353                 c = GetChar(ts);
1354                 radix = 16;
1355             } else if (JS7_ISDEC(c)) {
1356                 radix = 8;
1357             }
1358         }
1359 
1360         while (JS7_ISHEX(c)) {
1361             if (radix < 16) {
1362                 if (JS7_ISLET(c))
1363                     break;
1364 
1365                 /*
1366                  * We permit 08 and 09 as decimal numbers, which makes our
1367                  * behaviour a superset of the ECMA numeric grammar.  We might
1368                  * not always be so permissive, so we warn about it.
1369                  */
1370                 if (radix == 8 && c >= '8') {
1371                     if (!js_ReportCompileErrorNumber(cx, ts,
1372                                                      JSREPORT_TS |
1373                                                      JSREPORT_WARNING,
1374                                                      JSMSG_BAD_OCTAL,
1375                                                      c == '8' ? "08" : "09")) {
1376                         goto error;
1377                     }
1378                     radix = 10;
1379                 }
1380             }
1381             ADD_TO_TOKENBUF(c);
1382             c = GetChar(ts);
1383         }
1384 
1385         if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
1386             if (c == '.') {
1387                 do {
1388                     ADD_TO_TOKENBUF(c);
1389                     c = GetChar(ts);
1390                 } while (JS7_ISDEC(c));
1391             }
1392             if (JS_TOLOWER(c) == 'e') {
1393                 ADD_TO_TOKENBUF(c);
1394                 c = GetChar(ts);
1395                 if (c == '+' || c == '-') {
1396                     ADD_TO_TOKENBUF(c);
1397                     c = GetChar(ts);
1398                 }
1399                 if (!JS7_ISDEC(c)) {
1400                     js_ReportCompileErrorNumber(cx, ts,
1401                                                 JSREPORT_TS | JSREPORT_ERROR,
1402                                                 JSMSG_MISSING_EXPONENT);
1403                     goto error;
1404                 }
1405                 do {
1406                     ADD_TO_TOKENBUF(c);
1407                     c = GetChar(ts);
1408                 } while (JS7_ISDEC(c));
1409             }
1410         }
1411 
1412         /* Put back the next char and NUL-terminate tokenbuf for js_strto*. */
1413         UngetChar(ts, c);
1414         ADD_TO_TOKENBUF(0);
1415 
1416         if (!TOKENBUF_OK())
1417             goto error;
1418         if (radix == 10) {
1419             if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) {
1420                 js_ReportCompileErrorNumber(cx, ts,
1421                                             JSREPORT_TS | JSREPORT_ERROR,
1422                                             JSMSG_OUT_OF_MEMORY);
1423                 goto error;
1424             }
1425         } else {
1426             if (!js_strtointeger(cx, TOKENBUF_BASE(), &endptr, radix, &dval)) {
1427                 js_ReportCompileErrorNumber(cx, ts,
1428                                             JSREPORT_TS | JSREPORT_ERROR,
1429                                             JSMSG_OUT_OF_MEMORY);
1430                 goto error;
1431             }
1432         }
1433         tp->t_dval = dval;
1434         tt = TOK_NUMBER;
1435         goto out;
1436     }
1437 
1438     if (c == '"' || c == '\'') {
1439         qc = c;
1440         INIT_TOKENBUF();
1441         while ((c = GetChar(ts)) != qc) {
1442             if (c == '\n' || c == EOF) {
1443                 UngetChar(ts, c);
1444                 js_ReportCompileErrorNumber(cx, ts,
1445                                             JSREPORT_TS | JSREPORT_ERROR,
1446                                             JSMSG_UNTERMINATED_STRING);
1447                 goto error;
1448             }
1449             if (c == '\\') {
1450                 switch (c = GetChar(ts)) {
1451                   case 'b': c = '\b'; break;
1452                   case 'f': c = '\f'; break;
1453                   case 'n': c = '\n'; break;
1454                   case 'r': c = '\r'; break;
1455                   case 't': c = '\t'; break;
1456                   case 'v': c = '\v'; break;
1457 
1458                   default:
1459                     if ('0' <= c && c < '8') {
1460                         int32 val = JS7_UNDEC(c);
1461 
1462                         c = PeekChar(ts);
1463                         if ('0' <= c && c < '8') {
1464                             val = 8 * val + JS7_UNDEC(c);
1465                             GetChar(ts);
1466                             c = PeekChar(ts);
1467                             if ('0' <= c && c < '8') {
1468                                 int32 save = val;
1469                                 val = 8 * val + JS7_UNDEC(c);
1470                                 if (val <= 0377)
1471                                     GetChar(ts);
1472                                 else
1473                                     val = save;
1474                             }
1475                         }
1476 
1477                         c = (jschar)val;
1478                     } else if (c == 'u') {
1479                         jschar cp[4];
1480                         if (PeekChars(ts, 4, cp) &&
1481                             JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
1482                             JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
1483                             c = (((((JS7_UNHEX(cp[0]) << 4)
1484                                     + JS7_UNHEX(cp[1])) << 4)
1485                                   + JS7_UNHEX(cp[2])) << 4)
1486                                 + JS7_UNHEX(cp[3]);
1487                             SkipChars(ts, 4);
1488                         }
1489                     } else if (c == 'x') {
1490                         jschar cp[2];
1491                         if (PeekChars(ts, 2, cp) &&
1492                             JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
1493                             c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
1494                             SkipChars(ts, 2);
1495                         }
1496                     } else if (c == '\n' && JS_VERSION_IS_ECMA(cx)) {
1497                         /* ECMA follows C by removing escaped newlines. */
1498                         continue;
1499                     }
1500                     break;
1501                 }
1502             }
1503             ADD_TO_TOKENBUF(c);
1504         }
1505         atom = TOKENBUF_TO_ATOM();
1506         if (!atom)
1507             goto error;
1508         tp->pos.end.lineno = (uint16)ts->lineno;
1509         tp->t_op = JSOP_STRING;
1510         tp->t_atom = atom;
1511         tt = TOK_STRING;
1512         goto out;
1513     }
1514 
1515     switch (c) {
1516       case '\n': tt = TOK_EOL; goto eol_out;
1517       case ';':  tt = TOK_SEMI; break;
1518       case '[':  tt = TOK_LB; break;
1519       case ']':  tt = TOK_RB; break;
1520       case '{':  tt = TOK_LC; break;
1521       case '}':  tt = TOK_RC; break;
1522       case '(':  tt = TOK_LP; break;
1523       case ')':  tt = TOK_RP; break;
1524       case ',':  tt = TOK_COMMA; break;
1525       case '?':  tt = TOK_HOOK; break;
1526 
1527       case '.':
1528 #if JS_HAS_XML_SUPPORT
1529         if (MatchChar(ts, c))
1530             tt = TOK_DBLDOT;
1531         else
1532 #endif
1533             tt = TOK_DOT;
1534         break;
1535 
1536       case ':':
1537 #if JS_HAS_XML_SUPPORT
1538         if (MatchChar(ts, c)) {
1539             tt = TOK_DBLCOLON;
1540             break;
1541         }
1542 #endif
1543         /*
1544          * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an
1545          * object initializer, likewise for setter.
1546          */
1547         tp->t_op = JSOP_NOP;
1548         tt = TOK_COLON;
1549         break;
1550 
1551       case '|':
1552         if (MatchChar(ts, c)) {
1553             tt = TOK_OR;
1554         } else if (MatchChar(ts, '=')) {
1555             tp->t_op = JSOP_BITOR;
1556             tt = TOK_ASSIGN;
1557         } else {
1558             tt = TOK_BITOR;
1559         }
1560         break;
1561 
1562       case '^':
1563         if (MatchChar(ts, '=')) {
1564             tp->t_op = JSOP_BITXOR;
1565             tt = TOK_ASSIGN;
1566         } else {
1567             tt = TOK_BITXOR;
1568         }
1569         break;
1570 
1571       case '&':
1572         if (MatchChar(ts, c)) {
1573             tt = TOK_AND;
1574         } else if (MatchChar(ts, '=')) {
1575             tp->t_op = JSOP_BITAND;
1576             tt = TOK_ASSIGN;
1577         } else {
1578             tt = TOK_BITAND;
1579         }
1580         break;
1581 
1582       case '=':
1583         if (MatchChar(ts, c)) {
1584             tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq;
1585             tt = TOK_EQOP;
1586         } else {
1587             tp->t_op = JSOP_NOP;
1588             tt = TOK_ASSIGN;
1589         }
1590         break;
1591 
1592       case '!':
1593         if (MatchChar(ts, '=')) {
1594             tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;
1595             tt = TOK_EQOP;
1596         } else {
1597             tp->t_op = JSOP_NOT;
1598             tt = TOK_UNARYOP;
1599         }
1600         break;
1601 
1602 #if JS_HAS_XML_SUPPORT
1603       case '@':
1604         tt = TOK_AT;
1605         break;
1606 #endif
1607 
1608       case '<':
1609 #if JS_HAS_XML_SUPPORT
1610         /*
1611          * After much testing, it's clear that Postel's advice to protocol
1612          * designers ("be liberal in what you accept, and conservative in what
1613          * you send") invites a natural-law repercussion for JS as "protocol":
1614          *
1615          * "If you are liberal in what you accept, others will utterly fail to
1616          *  be conservative in what they send."
1617          *
1618          * Which means you will get <!-- comments to end of line in the middle
1619          * of .js files, and after if conditions whose then statements are on
1620          * the next line, and other wonders.  See at least the following bugs:
1621          * https://bugzilla.mozilla.org/show_bug.cgi?id=309242
1622          * https://bugzilla.mozilla.org/show_bug.cgi?id=309712
1623          * https://bugzilla.mozilla.org/show_bug.cgi?id=310993
1624          *
1625          * So without JSOPTION_XML, we never scan an XML comment or CDATA
1626          * literal.  We always scan <! as the start of an HTML comment hack
1627          * to end of line, used since Netscape 2 to hide script tag content
1628          * from script-unaware browsers.
1629          */
1630         if ((ts->flags & TSF_OPERAND) &&
1631             (JS_HAS_XML_OPTION(cx) || PeekChar(ts) != '!')) {
1632             /* Check for XML comment or CDATA section. */
1633             if (MatchChar(ts, '!')) {
1634                 INIT_TOKENBUF();
1635 
1636                 /* Scan XML comment. */
1637                 if (MatchChar(ts, '-')) {
1638                     if (!MatchChar(ts, '-'))
1639                         goto bad_xml_markup;
1640                     while ((c = GetChar(ts)) != '-' || !MatchChar(ts, '-')) {
1641                         if (c == EOF)
1642                             goto bad_xml_markup;
1643                         ADD_TO_TOKENBUF(c);
1644                     }
1645                     tt = TOK_XMLCOMMENT;
1646                     tp->t_op = JSOP_XMLCOMMENT;
1647                     goto finish_xml_markup;
1648                 }
1649 
1650                 /* Scan CDATA section. */
1651                 if (MatchChar(ts, '[')) {
1652                     jschar cp[6];
1653                     if (PeekChars(ts, 6, cp) &&
1654                         cp[0] == 'C' &&
1655                         cp[1] == 'D' &&
1656                         cp[2] == 'A' &&
1657                         cp[3] == 'T' &&
1658                         cp[4] == 'A' &&
1659                         cp[5] == '[') {
1660                         SkipChars(ts, 6);
1661                         while ((c = GetChar(ts)) != ']' ||
1662                                !PeekChars(ts, 2, cp) ||
1663                                cp[0] != ']' ||
1664                                cp[1] != '>') {
1665                             if (c == EOF)
1666                                 goto bad_xml_markup;
1667                             ADD_TO_TOKENBUF(c);
1668                         }
1669                         GetChar(ts);            /* discard ] but not > */
1670                         tt = TOK_XMLCDATA;
1671                         tp->t_op = JSOP_XMLCDATA;
1672                         goto finish_xml_markup;
1673                     }
1674                     goto bad_xml_markup;
1675                 }
1676             }
1677 
1678             /* Check for processing instruction. */
1679             if (MatchChar(ts, '?')) {
1680                 JSBool inTarget = JS_TRUE;
1681                 size_t targetLength = 0;
1682                 ptrdiff_t contentIndex = -1;
1683 
1684                 INIT_TOKENBUF();
1685                 while ((c = GetChar(ts)) != '?' || PeekChar(ts) != '>') {
1686                     if (c == EOF)
1687                         goto bad_xml_markup;
1688                     if (inTarget) {
1689                         if (JS_ISXMLSPACE(c)) {
1690                             if (TOKENBUF_LENGTH() == 0)
1691                                 goto bad_xml_markup;
1692                             inTarget = JS_FALSE;
1693                         } else {
1694                             if (!((TOKENBUF_LENGTH() == 0)
1695                                   ? JS_ISXMLNSSTART(c)
1696                                   : JS_ISXMLNS(c))) {
1697                                 goto bad_xml_markup;
1698                             }
1699                             ++targetLength;
1700                         }
1701                     } else {
1702                         if (contentIndex < 0 && !JS_ISXMLSPACE(c))
1703                             contentIndex = TOKENBUF_LENGTH();
1704                     }
1705                     ADD_TO_TOKENBUF(c);
1706                 }
1707                 if (targetLength == 0)
1708                     goto bad_xml_markup;
1709                 if (!TOKENBUF_OK())
1710                     goto error;
1711                 if (contentIndex < 0) {
1712                     atom = cx->runtime->atomState.emptyAtom;
1713                 } else {
1714                     atom = js_AtomizeChars(cx,
1715                                            &TOKENBUF_CHAR(contentIndex),
1716                                            TOKENBUF_LENGTH() - contentIndex,
1717                                            0);
1718                     if (!atom)
1719                         goto error;
1720                 }
1721                 TRIM_TOKENBUF(targetLength);
1722                 tp->t_atom2 = atom;
1723                 tt = TOK_XMLPI;
1724 
1725         finish_xml_markup:
1726                 if (!MatchChar(ts, '>'))
1727                     goto bad_xml_markup;
1728                 atom = TOKENBUF_TO_ATOM();
1729                 if (!atom)
1730                     goto error;
1731                 tp->t_atom = atom;
1732                 tp->pos.end.lineno = (uint16)ts->lineno;
1733                 goto out;
1734             }
1735 
1736             /* An XML start-of-tag character. */
1737             tt = MatchChar(ts, '/') ? TOK_XMLETAGO : TOK_XMLSTAGO;
1738             goto out;
1739 
1740         bad_xml_markup:
1741             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1742                                         JSMSG_BAD_XML_MARKUP);
1743             goto error;
1744         }
1745 #endif /* JS_HAS_XML_SUPPORT */
1746 
1747         /* NB: treat HTML begin-comment as comment-till-end-of-line */
1748         if (MatchChar(ts, '!')) {
1749             if (MatchChar(ts, '-')) {
1750                 if (MatchChar(ts, '-')) {
1751                     ts->flags |= TSF_IN_HTML_COMMENT;
1752                     goto skipline;
1753                 }
1754                 UngetChar(ts, '-');
1755             }
1756             UngetChar(ts, '!');
1757         }
1758         if (MatchChar(ts, c)) {
1759             tp->t_op = JSOP_LSH;
1760             tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
1761         } else {
1762             tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
1763             tt = TOK_RELOP;
1764         }
1765         break;
1766 
1767       case '>':
1768         if (MatchChar(ts, c)) {
1769             tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;
1770             tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
1771         } else {
1772             tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
1773             tt = TOK_RELOP;
1774         }
1775         break;
1776 
1777       case '*':
1778         tp->t_op = JSOP_MUL;
1779         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;
1780         break;
1781 
1782       case '/':
1783         if (MatchChar(ts, '/')) {
1784             /*
1785              * Hack for source filters such as the Mozilla XUL preprocessor:
1786              * "//@line 123\n" sets the number of the *next* line after the
1787              * comment to 123.
1788              */
1789             if (JS_HAS_ATLINE_OPTION(cx)) {
1790                 jschar cp[5];
1791                 uintN i, line, temp;
1792                 char filename[1024];
1793 
1794                 if (PeekChars(ts, 5, cp) &&
1795                     cp[0] == '@' &&
1796                     cp[1] == 'l' &&
1797                     cp[2] == 'i' &&
1798                     cp[3] == 'n' &&
1799                     cp[4] == 'e') {
1800                     SkipChars(ts, 5);
1801                     while ((c = GetChar(ts)) != '\n' && JS_ISSPACE(c))
1802                         continue;
1803                     if (JS7_ISDEC(c)) {
1804                         line = JS7_UNDEC(c);
1805                         while ((c = GetChar(ts)) != EOF && JS7_ISDEC(c)) {
1806                             temp = 10 * line + JS7_UNDEC(c);
1807                             if (temp < line) {
1808                                 /* Ignore overlarge line numbers. */
1809                                 goto skipline;
1810                             }
1811                             line = temp;
1812                         }
1813                         while (c != '\n' && JS_ISSPACE(c))
1814                             c = GetChar(ts);
1815                         i = 0;
1816                         if (c == '"') {
1817                             while ((c = GetChar(ts)) != EOF && c != '"') {
1818                                 if (c == '\n') {
1819                                     UngetChar(ts, c);
1820                                     goto skipline;
1821                                 }
1822                                 if ((c >> 8) != 0 || i >= sizeof filename - 1)
1823                                     goto skipline;
1824                                 filename[i++] = (char) c;
1825                             }
1826                             if (c == '"') {
1827                                 while ((c = GetChar(ts)) != '\n' &&
1828                                        JS_ISSPACE(c)) {
1829                                     continue;
1830                                 }
1831                             }
1832                         }
1833                         filename[i] = '\0';
1834                         if (c == '\n') {
1835                             if (i > 0) {
1836                                 if (ts->flags & TSF_OWNFILENAME)
1837                                     JS_free(cx, (void *) ts->filename);
1838                                 ts->filename = JS_strdup(cx, filename);
1839                                 if (!ts->filename)
1840                                     goto error;
1841                                 ts->flags |= TSF_OWNFILENAME;
1842                             }
1843                             ts->lineno = line;
1844                         }
1845                     }
1846                     UngetChar(ts, c);
1847                 }
1848             }
1849 
1850 skipline:
1851             /* Optimize line skipping if we are not in an HTML comment. */
1852             if (ts->flags & TSF_IN_HTML_COMMENT) {
1853                 while ((c = GetChar(ts)) != EOF && c != '\n') {
1854                     if (c == '-' && MatchChar(ts, '-') && MatchChar(ts, '>'))
1855                         ts->flags &= ~TSF_IN_HTML_COMMENT;
1856                 }
1857             } else {
1858                 while ((c = GetChar(ts)) != EOF && c != '\n')
1859                     continue;
1860             }
1861             UngetChar(ts, c);
1862             ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
1863             goto retry;
1864         }
1865 
1866         if (MatchChar(ts, '*')) {
1867             while ((c = GetChar(ts)) != EOF &&
1868                    !(c == '*' && MatchChar(ts, '/'))) {
1869                 /* Ignore all characters until comment close. */
1870             }
1871             if (c == EOF) {
1872                 js_ReportCompileErrorNumber(cx, ts,
1873                                             JSREPORT_TS | JSREPORT_ERROR,
1874                                             JSMSG_UNTERMINATED_COMMENT);
1875                 goto error;
1876             }
1877             ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
1878             goto retry;
1879         }
1880 
1881         if (ts->flags & TSF_OPERAND) {
1882             JSObject *obj;
1883             uintN flags;
1884             JSBool inCharClass = JS_FALSE;
1885 
1886             INIT_TOKENBUF();
1887             for (;;) {
1888                 c = GetChar(ts);
1889                 if (c == '\n' || c == EOF) {
1890                     UngetChar(ts, c);
1891                     js_ReportCompileErrorNumber(cx, ts,
1892                                                 JSREPORT_TS | JSREPORT_ERROR,
1893                                                 JSMSG_UNTERMINATED_REGEXP);
1894                     goto error;
1895                 }
1896                 if (c == '\\') {
1897                     ADD_TO_TOKENBUF(c);
1898                     c = GetChar(ts);
1899                 } else if (c == '[') {
1900                     inCharClass = JS_TRUE;
1901                 } else if (c == ']') {
1902                     inCharClass = JS_FALSE;
1903                 } else if (c == '/' && !inCharClass) {
1904                     /* For compat with IE, allow unescaped / in char classes. */
1905                     break;
1906                 }
1907                 ADD_TO_TOKENBUF(c);
1908             }
1909             for (flags = 0; ; ) {
1910                 if (MatchChar(ts, 'g'))
1911                     flags |= JSREG_GLOB;
1912                 else if (MatchChar(ts, 'i'))
1913                     flags |= JSREG_FOLD;
1914                 else if (MatchChar(ts, 'm'))
1915                     flags |= JSREG_MULTILINE;
1916                 else
1917                     break;
1918             }
1919             c = PeekChar(ts);
1920             if (JS7_ISLET(c)) {
1921                 tp->ptr = ts->linebuf.ptr - 1;
1922                 js_ReportCompileErrorNumber(cx, ts,
1923                                             JSREPORT_TS | JSREPORT_ERROR,
1924                                             JSMSG_BAD_REGEXP_FLAG);
1925                 (void) GetChar(ts);
1926                 goto error;
1927             }
1928             /* XXXbe fix jsregexp.c so it doesn't depend on NUL termination */
1929             if (!TOKENBUF_OK())
1930                 goto error;
1931             NUL_TERM_TOKENBUF();
1932             obj = js_NewRegExpObject(cx, ts,
1933                                      TOKENBUF_BASE(),
1934                                      TOKENBUF_LENGTH(),
1935                                      flags);
1936             if (!obj)
1937                 goto error;
1938             atom = js_AtomizeObject(cx, obj, 0);
1939             if (!atom)
1940                 goto error;
1941 
1942             /*
1943              * If the regexp's script is one-shot, we can avoid the extra
1944              * fork-on-exec costs of JSOP_REGEXP by selecting JSOP_OBJECT.
1945              * Otherwise, to avoid incorrect proto, parent, and lastIndex
1946              * sharing among threads and sequentially across re-execution,
1947              * select JSOP_REGEXP.
1948              */
1949             tp->t_op = (cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))
1950                        ? JSOP_OBJECT
1951                        : JSOP_REGEXP;
1952             tp->t_atom = atom;
1953             tt = TOK_OBJECT;
1954             break;
1955         }
1956 
1957         tp->t_op = JSOP_DIV;
1958         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
1959         break;
1960 
1961       case '%':
1962         tp->t_op = JSOP_MOD;
1963         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
1964         break;
1965 
1966       case '~':
1967         tp->t_op = JSOP_BITNOT;
1968         tt = TOK_UNARYOP;
1969         break;
1970 
1971       case '+':
1972         if (MatchChar(ts, '=')) {
1973             tp->t_op = JSOP_ADD;
1974             tt = TOK_ASSIGN;
1975         } else if (MatchChar(ts, c)) {
1976             tt = TOK_INC;
1977         } else {
1978             tp->t_op = JSOP_POS;
1979             tt = TOK_PLUS;
1980         }
1981         break;
1982 
1983       case '-':
1984         if (MatchChar(ts, '=')) {
1985             tp->t_op = JSOP_SUB;
1986             tt = TOK_ASSIGN;
1987         } else if (MatchChar(ts, c)) {
1988             if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE)) {
1989                 ts->flags &= ~TSF_IN_HTML_COMMENT;
1990                 goto skipline;
1991             }
1992             tt = TOK_DEC;
1993         } else {
1994             tp->t_op = JSOP_NEG;
1995             tt = TOK_MINUS;
1996         }
1997         break;
1998 
1999 #if JS_HAS_SHARP_VARS
2000       case '#':
2001       {
2002         uint32 n;
2003 
2004         c = GetChar(ts);
2005         if (!JS7_ISDEC(c)) {
2006             UngetChar(ts, c);
2007             goto badchar;
2008         }
2009         n = (uint32)JS7_UNDEC(c);
2010         for (;;) {
2011             c = GetChar(ts);
2012             if (!JS7_ISDEC(c))
2013                 break;
2014             n = 10 * n + JS7_UNDEC(c);
2015             if (n >= UINT16_LIMIT) {
2016                 js_ReportCompileErrorNumber(cx, ts,
2017                                             JSREPORT_TS | JSREPORT_ERROR,
2018                                             JSMSG_SHARPVAR_TOO_BIG);
2019                 goto error;
2020             }
2021         }
2022         tp->t_dval = (jsdouble) n;
2023         if (JS_HAS_STRICT_OPTION(cx) &&
2024             (c == '=' || c == '#')) {
2025             char buf[20];
2026             JS_snprintf(buf, sizeof buf, "#%u%c", n, c);
2027             if (!js_ReportCompileErrorNumber(cx, ts,
2028                                              JSREPORT_TS |
2029                                              JSREPORT_WARNING |
2030                                              JSREPORT_STRICT,
2031                                              JSMSG_DEPRECATED_USAGE,
2032                                              buf)) {
2033                 goto error;
2034             }
2035         }
2036         if (c == '=')
2037             tt = TOK_DEFSHARP;
2038         else if (c == '#')
2039             tt = TOK_USESHARP;
2040         else
2041             goto badchar;
2042         break;
2043       }
2044 #endif /* JS_HAS_SHARP_VARS */
2045 
2046 #if JS_HAS_SHARP_VARS || JS_HAS_XML_SUPPORT
2047       badchar:
2048 #endif
2049 
2050       default:
2051         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2052                                     JSMSG_ILLEGAL_CHARACTER);
2053         goto error;
2054     }
2055 
2056 out:
2057     JS_ASSERT(tt != TOK_EOL);
2058     ts->flags |= TSF_DIRTYLINE;
2059 
2060 eol_out:
2061     if (!STRING_BUFFER_OK(&ts->tokenbuf))
2062         tt = TOK_ERROR;
2063     JS_ASSERT(tt < TOK_LIMIT);
2064     tp->pos.end.index = ts->linepos +
2065                         PTRDIFF(ts->linebuf.ptr, ts->linebuf.base, jschar) -
2066                         ts->ungetpos;
2067     tp->type = tt;
2068     return tt;
2069 
2070 error:
2071     tt = TOK_ERROR;
2072     ts->flags |= TSF_ERROR;
2073     goto out;
2074 
2075 #undef INIT_TOKENBUF
2076 #undef TOKENBUF_LENGTH
2077 #undef TOKENBUF_OK
2078 #undef TOKENBUF_TO_ATOM
2079 #undef ADD_TO_TOKENBUF
2080 #undef TOKENBUF_BASE
2081 #undef TOKENBUF_CHAR
2082 #undef TRIM_TOKENBUF
2083 #undef NUL_TERM_TOKENBUF
2084 }
2085 
2086 void
js_UngetToken(JSTokenStream * ts)2087 js_UngetToken(JSTokenStream *ts)
2088 {
2089     JS_ASSERT(ts->lookahead < NTOKENS_MASK);
2090     ts->lookahead++;
2091     ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
2092 }
2093 
2094 JSBool
js_MatchToken(JSContext * cx,JSTokenStream * ts,JSTokenType tt)2095 js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
2096 {
2097     if (js_GetToken(cx, ts) == tt)
2098         return JS_TRUE;
2099     js_UngetToken(ts);
2100     return JS_FALSE;
2101 }
2102