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