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 17 {
182 struct keyword *kw;
183 size_t length;
184 JSAtom *atom;
185
186 1020 for (kw = keywords; kw->name; kw++) {
187 1003 length = strlen(kw->name);
188 JS_ASSERT(length <= MAX_KEYWORD_LENGTH);
189 1003 atom = js_Atomize(cx, kw->name, length, ATOM_PINNED);
190 1003 if (!atom)
191 0 return JS_FALSE;
192 1003 ATOM_SET_KEYWORD(atom, kw);
193 }
194 17 return JS_TRUE;
195 }
196
197 JS_FRIEND_API(void)
198 js_MapKeywords(void (*mapfun)(const char *))
199 0 {
200 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 1226 {
211 JSTokenStream *ts;
212
213 1226 ts = js_NewBufferTokenStream(cx, base, length);
214 1226 if (!ts)
215 0 return NULL;
216 1226 ts->filename = filename;
217 1226 ts->lineno = lineno;
218 1226 if (principals)
219 0 JSPRINCIPALS_HOLD(cx, principals);
220 1226 ts->principals = principals;
221 1226 return ts;
222 }
223
224 #define TBMIN 64
225
226 static JSBool
227 GrowTokenBuf(JSStringBuffer *sb, size_t newlength)
228 5134 {
229 JSContext *cx;
230 jschar *base;
231 ptrdiff_t offset, length;
232 size_t tbsize;
233 JSArenaPool *pool;
234
235 5134 cx = sb->data;
236 5134 base = sb->base;
237 5134 offset = PTRDIFF(sb->ptr, base, jschar);
238 5134 pool = &cx->tempPool;
239 5134 if (!base) {
240 1226 tbsize = TBMIN * sizeof(jschar);
241 1226 length = TBMIN - 1;
242 1226 JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize);
243 } else {
244 3908 length = PTRDIFF(sb->limit, base, jschar);
245 3908 tbsize = (length + 1) * sizeof(jschar);
246 3908 length += length + 1;
247 3908 JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize);
248 }
249 5134 if (!base) {
250 0 JS_ReportOutOfMemory(cx);
251 0 sb->base = STRING_BUFFER_ERROR_BASE;
252 0 return JS_FALSE;
253 }
254 5134 sb->base = base;
255 5134 sb->limit = base + length;
256 5134 sb->ptr = base + offset;
257 5134 return JS_TRUE;
258 }
259
260 JS_FRIEND_API(JSTokenStream *)
261 js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
262 1226 {
263 size_t nb;
264 JSTokenStream *ts;
265
266 1226 nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);
267 1226 JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb);
268 1226 if (!ts) {
269 0 JS_ReportOutOfMemory(cx);
270 0 return NULL;
271 }
272 1226 memset(ts, 0, nb);
273 1226 ts->lineno = 1;
274 1226 ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);
275 1226 ts->userbuf.base = (jschar *)base;
276 1226 ts->userbuf.limit = (jschar *)base + length;
277 1226 ts->userbuf.ptr = (jschar *)base;
278 1226 ts->tokenbuf.grow = GrowTokenBuf;
279 1226 ts->tokenbuf.data = cx;
280 1226 ts->listener = cx->runtime->sourceHandler;
281 1226 ts->listenerData = cx->runtime->sourceHandlerData;
282 1226 return ts;
283 }
284
285 JS_FRIEND_API(JSTokenStream *)
286 js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp)
287 0 {
288 jschar *base;
289 JSTokenStream *ts;
290 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 1226 {
318 1226 if (ts->flags & TSF_OWNFILENAME)
319 0 JS_free(cx, (void *) ts->filename);
320 1226 if (ts->principals)
321 0 JSPRINCIPALS_DROP(cx, ts->principals);
322 1226 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 int n, i, c;
329 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 20399849 {
356 int32 c;
357 ptrdiff_t i, j, len, olen;
358 JSBool crflag;
359 char cbuf[JS_LINE_LIMIT];
360 jschar *ubuf, *nl;
361
362 20399849 if (ts->ungetpos != 0) {
363 781616 c = ts->ungetbuf[--ts->ungetpos];
364 } else {
365 do {
366 19618233 if (ts->linebuf.ptr == ts->linebuf.limit) {
367 666514 len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
368 666514 if (len <= 0) {
369 1226 if (!ts->file) {
370 1226 ts->flags |= TSF_EOF;
371 1226 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 665288 if (ts->listener) {
398 0 ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len,
399 &ts->listenerTSData, ts->listenerData);
400 }
401
402 665288 nl = ts->saveEOL;
403 665288 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 19617007 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 19617007 if ((*nl & 0xDFD0) == 0) {
415 6724489 if (*nl == '\n')
416 6077180 break;
417 6077180 if (*nl == '\r') {
418 1226 if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
419 1226 nl++;
420 break;
421 }
422 6075954 if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR)
423 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 665288 if (nl < ts->userbuf.limit)
433 665288 len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;
434 665288 if (len >= JS_LINE_LIMIT) {
435 16753 len = JS_LINE_LIMIT - 1;
436 16753 ts->saveEOL = nl;
437 } else {
438 648535 ts->saveEOL = NULL;
439 }
440 665288 js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);
441 665288 ts->userbuf.ptr += len;
442 665288 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 665288 if (nl < ts->userbuf.limit) {
449 665288 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 665288 } else if (*nl == '\n') {
474 665288 if (nl > ts->userbuf.base &&
475 nl[-1] == '\r' &&
476 ts->linebuf.base[len-2] == '\r') {
477 1226 len--;
478 JS_ASSERT(ts->linebuf.base[len] == '\n');
479 1226 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 665288 ts->linebuf.limit = ts->linebuf.base + len;
488 665288 ts->linebuf.ptr = ts->linebuf.base;
489
490 /* Update position of linebuf within physical userbuf line. */
491 665288 if (!(ts->flags & TSF_NLFLAG))
492 17979 ts->linepos += ts->linelen;
493 else
494 647309 ts->linepos = 0;
495 665288 if (ts->linebuf.limit[-1] == '\n')
496 648535 ts->flags |= TSF_NLFLAG;
497 else
498 16753 ts->flags &= ~TSF_NLFLAG;
499
500 /* Update linelen from original segment length. */
501 665288 ts->linelen = olen;
502 }
503 19617007 c = *ts->linebuf.ptr++;
504 19617007 } while (JS_ISFORMAT(c));
505 }
506 20398623 if (c == '\n')
507 652135 ts->lineno++;
508 20398623 return c;
509 }
510
511 static void
512 UngetChar(JSTokenStream *ts, int32 c)
513 781616 {
514 781616 if (c == EOF)
515 781616 return;
516 JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
517 781616 if (c == '\n')
518 3600 ts->lineno--;
519 781616 ts->ungetbuf[ts->ungetpos++] = (jschar)c;
520 }
521
522 static int32
523 PeekChar(JSTokenStream *ts)
524 72180 {
525 int32 c;
526
527 72180 c = GetChar(ts);
528 72180 UngetChar(ts, c);
529 72180 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 intN i, j;
541 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 112936 {
568 int32 c;
569
570 112936 c = GetChar(ts);
571 112936 if (c == expect)
572 21426 return JS_TRUE;
573 91510 UngetChar(ts, c);
574 91510 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 JSErrorReporter onError;
589 JSTokenPos *tp;
590 JSStackFrame *fp;
591 uintN index;
592 char *message;
593 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 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 34 {
768 va_list ap;
769 JSErrorReport report;
770 JSBool warning;
771
772 34 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
773 34 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 va_list ap;
799 JSErrorReport report;
800 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 ptrdiff_t offset;
820 jschar *bp;
821
822 0 offset = PTRDIFF(sb->ptr, sb->base, jschar);
823 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 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 15641939 {
869 15641939 if (!STRING_BUFFER_OK(sb))
870 15641939 return;
871 15641939 if (!ENSURE_STRING_BUFFER(sb, 1))
872 15641939 return;
873 15641939 *sb->ptr++ = c;
874 }
875
876 void
877 js_AppendChar(JSStringBuffer *sb, jschar c)
878 0 {
879 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 jschar *bp;
897
898 0 if (!STRING_BUFFER_OK(sb) || count == 0)
899 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 size_t length;
912 jschar *bp;
913
914 0 if (!STRING_BUFFER_OK(sb) || *asciiz == '\0')
915 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 size_t length;
929 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 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 ptrdiff_t offset, length, i;
947 int32 c, d;
948 JSBool ispair;
949 jschar *bp, digit;
950 char *bytes;
951 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 goto badncr;
977 0 while (++i < length) {
978 0 digit = bp[i];
979 0 if (!JS7_ISHEX(digit))
980 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 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 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 break;
1018 case 4:
1019 0 if (bp[1] == 'a' && bp[2] == 'm' && bp[3] == 'p')
1020 0 c = '&';
1021 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 0 badncr:
1046 0 msg = JSMSG_BAD_XML_NCR;
1047 0 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 2204661 {
1064 JSTokenType tt;
1065
1066 2204661 if (ts->lookahead != 0) {
1067 1867450 tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type;
1068 } else {
1069 337211 tt = js_GetToken(cx, ts);
1070 337211 js_UngetToken(ts);
1071 }
1072 2204661 return tt;
1073 }
1074
1075 JSTokenType
1076 js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)
1077 935998 {
1078 JSTokenType tt;
1079
1080 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 935998 ts->flags |= TSF_NEWLINES;
1085 935998 tt = js_PeekToken(cx, ts);
1086 935998 ts->flags &= ~TSF_NEWLINES;
1087 935998 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 jschar cp[5];
1099 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 1851327 {
1118 JSToken *tp;
1119
1120 1851327 ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
1121 1851327 tp = &CURRENT_TOKEN(ts);
1122 1851327 tp->ptr = ts->linebuf.ptr + adjust;
1123 1851327 tp->pos.begin.index = ts->linepos +
1124 PTRDIFF(tp->ptr, ts->linebuf.base, jschar) -
1125 ts->ungetpos;
1126 1851327 tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno;
1127 1851327 return tp;
1128 }
1129
1130 JSTokenType
1131 js_GetToken(JSContext *cx, JSTokenStream *ts)
1132 15405208 {
1133 JSTokenType tt;
1134 int32 c, qc;
1135 JSToken *tp;
1136 JSAtom *atom;
1137 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 15405208 if (ts->flags & TSF_ERROR)
1158 0 return TOK_ERROR;
1159
1160 /* Check for a pushed-back token resulting from mismatching lookahead. */
1161 15405208 while (ts->lookahead != 0) {
1162 JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE));
1163 13554629 ts->lookahead--;
1164 13554629 ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
1165 13554629 tt = CURRENT_TOKEN(ts).type;
1166 13554629 if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))
1167 13554629 return tt;
1168 }
1169
1170 #if JS_HAS_XML_SUPPORT
1171 1850579 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 1850579 if (ts->flags & TSF_XMLTAGMODE) {
1205 0 tp = NewToken(ts, 0);
1206 0 c = GetChar(ts);
1207 0 if (JS_ISXMLSPACE(c)) {
1208 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 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 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 goto error;
1292 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 0 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 2049048 retry:
1328 do {
1329 3899627 c = GetChar(ts);
1330 3899627 if (c == '\n') {
1331 647957 ts->flags &= ~TSF_DIRTYLINE;
1332 647957 if (ts->flags & TSF_NEWLINES)
1333 3899627 break;
1334 }
1335 3899627 } while (JS_ISSPACE(c));
1336
1337 1851327 tp = NewToken(ts, -1);
1338 1851327 if (c == EOF) {
1339 1226 tt = TOK_EOF;
1340 1226 goto out;
1341 }
1342
1343 1850101 hadUnicodeEscape = JS_FALSE;
1344 1850101 if (JS_ISIDSTART(c) ||
1345 (c == '\\' &&
1346 (c = GetUnicodeEscape(ts),
1347 hadUnicodeEscape = JS_ISIDSTART(c)))) {
1348 600716 INIT_TOKENBUF();
1349 for (;;) {
1350 4359080 ADD_TO_TOKENBUF(c);
1351 4359080 c = GetChar(ts);
1352 4359080 if (c == '\\') {
1353 0 c = GetUnicodeEscape(ts);
1354 0 if (!JS_ISIDENT(c))
1355 0 break;
1356 0 hadUnicodeEscape = JS_TRUE;
1357 } else {
1358 4359080 if (!JS_ISIDENT(c))
1359 break;
1360 }
1361 }
1362 600716 UngetChar(ts, c);
1363
1364 600716 atom = TOKENBUF_TO_ATOM();
1365 600716 if (!atom)
1366 600716 goto error;
1367 600716 if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) {
1368 struct keyword *kw;
1369
1370 JS_ASSERT(!(atom->flags & ATOM_HIDDEN));
1371 44190 kw = ATOM_KEYWORD(atom);
1372 44190 if (kw->tokentype == TOK_RESERVED) {
1373 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 goto error;
1385 }
1386 44190 } else if (JS_VERSION_IS_ECMA(cx) ||
1387 kw->version <= (cx->version & JSVERSION_MASK)) {
1388 44190 tt = kw->tokentype;
1389 44190 tp->t_op = (JSOp) kw->op;
1390 44190 goto out;
1391 }
1392 }
1393 556526 tp->t_op = JSOP_NAME;
1394 556526 tp->t_atom = atom;
1395 556526 tt = TOK_NAME;
1396 556526 goto out;
1397 }
1398
1399 1249385 if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
1400 jsint radix;
1401 const jschar *endptr;
1402 jsdouble dval;
1403
1404 17210 radix = 10;
1405 17210 INIT_TOKENBUF();
1406
1407 17210 if (c == '0') {
1408 7370 ADD_TO_TOKENBUF(c);
1409 7370 c = GetChar(ts);
1410 7370 if (JS_TOLOWER(c) == 'x') {
1411 0 ADD_TO_TOKENBUF(c);
1412 0 c = GetChar(ts);
1413 0 radix = 16;
1414 7370 } else if (JS7_ISDEC(c)) {
1415 0 radix = 8;
1416 }
1417 }
1418
1419 27050 while (JS7_ISHEX(c)) {
1420 9840 if (radix < 16) {
1421 9840 if (JS7_ISLET(c))
1422 9840 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 9840 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 9840 ADD_TO_TOKENBUF(c);
1441 9840 c = GetChar(ts);
1442 }
1443
1444 17210 if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
1445 0 if (c == '.') {
1446 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 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 17210 UngetChar(ts, c);
1473 17210 ADD_TO_TOKENBUF(0);
1474
1475 17210 if (!TOKENBUF_OK())
1476 17210 goto error;
1477 17210 if (radix == 10) {
1478 17210 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 17210 tp->t_dval = dval;
1493 17210 tt = TOK_NUMBER;
1494 17210 goto out;
1495 }
1496
1497 1232175 if (c == '"' || c == '\'') {
1498 143089 qc = c;
1499 143089 INIT_TOKENBUF();
1500 11529041 while ((c = GetChar(ts)) != qc) {
1501 11242863 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 11242863 if (c == '\\') {
1509 505094 switch (c = GetChar(ts)) {
1510 0 case 'b': c = '\b'; break;
1511 0 case 'f': c = '\f'; break;
1512 469785 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 35309 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 35309 } else if (c == 'u') {
1538 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 35309 } else if (c == 'x') {
1549 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 35309 } else if (c == '\n' && JS_VERSION_IS_ECMA(cx)) {
1556 /* ECMA follows C by removing escaped newlines. */
1557 continue;
1558 }
1559 break;
1560 }
1561 }
1562 11242863 ADD_TO_TOKENBUF(c);
1563 }
1564 143089 atom = TOKENBUF_TO_ATOM();
1565 143089 if (!atom)
1566 143089 goto error;
1567 143089 tp->pos.end.lineno = (uint16)ts->lineno;
1568 143089 tp->t_op = JSOP_STRING;
1569 143089 tp->t_atom = atom;
1570 143089 tt = TOK_STRING;
1571 143089 goto out;
1572 }
1573
1574 1089086 switch (c) {
1575 0 case '\n': tt = TOK_EOL; goto eol_out;
1576 276617 case ';': tt = TOK_SEMI; break;
1577 27595 case '[': tt = TOK_LB; break;
1578 27595 case ']': tt = TOK_RB; break;
1579 23029 case '{': tt = TOK_LC; break;
1580 23029 case '}': tt = TOK_RC; break;
1581 288436 case '(': tt = TOK_LP; break;
1582 288436 case ')': tt = TOK_RP; break;
1583 49792 case ',': tt = TOK_COMMA; break;
1584 0 case '?': tt = TOK_HOOK; break;
1585
1586 case '.':
1587 #if JS_HAS_XML_SUPPORT
1588 35869 if (MatchChar(ts, c))
1589 0 tt = TOK_DBLDOT;
1590 else
1591 #endif
1592 35869 tt = TOK_DOT;
1593 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 1612 if (MatchChar(ts, c)) {
1612 1612 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 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 break;
1629
1630 case '&':
1631 2625 if (MatchChar(ts, c)) {
1632 2625 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 break;
1640
1641 case '=':
1642 29182 if (MatchChar(ts, c)) {
1643 #if JS_HAS_TRIPLE_EQOPS
1644 11142 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 11142 tt = TOK_EQOP;
1649 } else {
1650 18040 tp->t_op = JSOP_NOP;
1651 18040 tt = TOK_ASSIGN;
1652 }
1653 break;
1654
1655 case '!':
1656 4572 if (MatchChar(ts, '=')) {
1657 #if JS_HAS_TRIPLE_EQOPS
1658 4092 tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;
1659 #else
1660 tp->t_op = cx->jsop_ne;
1661 #endif
1662 4092 tt = TOK_EQOP;
1663 } else {
1664 480 tp->t_op = JSOP_NOT;
1665 480 tt = TOK_UNARYOP;
1666 }
1667 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 136 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 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 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 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 0 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 0 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 136 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 136 if (MatchChar(ts, c)) {
1824 0 tp->t_op = JSOP_LSH;
1825 0 tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
1826 } else {
1827 136 tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
1828 136 tt = TOK_RELOP;
1829 }
1830 break;
1831
1832 case '>':
1833 170 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 170 tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
1838 170 tt = TOK_RELOP;
1839 }
1840 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 1156 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 jschar cp[5];
1856 uintN i, line, temp;
1857 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 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 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 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 0 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 continue;
1925 }
1926 0 UngetChar(ts, c);
1927 0 ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
1928 0 goto retry;
1929 }
1930
1931 1156 if (MatchChar(ts, '*')) {
1932 41786 while ((c = GetChar(ts)) != EOF &&
1933 !(c == '*' && MatchChar(ts, '/'))) {
1934 /* Ignore all characters until comment close. */
1935 }
1936 748 if (c == EOF) {
1937 0 js_ReportCompileErrorNumber(cx, ts,
1938 JSREPORT_TS | JSREPORT_ERROR,
1939 JSMSG_UNTERMINATED_COMMENT);
1940 0 goto error;
1941 }
1942 748 ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
1943 748 goto retry;
1944 }
1945
1946 #if JS_HAS_REGEXPS
1947 408 if (ts->flags & TSF_OPERAND) {
1948 JSObject *obj;
1949 uintN flags;
1950 408 JSBool inCharClass = JS_FALSE;
1951
1952 408 INIT_TOKENBUF();
1953 for (;;) {
1954 5610 c = GetChar(ts);
1955 5610 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 5610 if (c == '\\') {
1963 374 ADD_TO_TOKENBUF(c);
1964 374 c = GetChar(ts);
1965 5236 } else if (c == '[') {
1966 0 inCharClass = JS_TRUE;
1967 5236 } else if (c == ']') {
1968 0 inCharClass = JS_FALSE;
1969 5236 } else if (c == '/' && !inCharClass) {
1970 /* For compat with IE, allow unescaped / in char classes. */
1971 5202 break;
1972 }
1973 5202 ADD_TO_TOKENBUF(c);
1974 5202 }
1975 408 for (flags = 0; ; ) {
1976 408 if (MatchChar(ts, 'g'))
1977 0 flags |= JSREG_GLOB;
1978 408 else if (MatchChar(ts, 'i'))
1979 0 flags |= JSREG_FOLD;
1980 408 else if (MatchChar(ts, 'm'))
1981 0 flags |= JSREG_MULTILINE;
1982 else
1983 0 break;
1984 }
1985 408 c = PeekChar(ts);
1986 408 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 408 if (!TOKENBUF_OK())
1996 408 goto error;
1997 408 NUL_TERM_TOKENBUF();
1998 408 obj = js_NewRegExpObject(cx, ts,
1999 TOKENBUF_BASE(),
2000 TOKENBUF_LENGTH(),
2001 flags);
2002 408 if (!obj)
2003 408 goto error;
2004 408 atom = js_AtomizeObject(cx, obj, 0);
2005 408 if (!atom)
2006 408 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 408 tp->t_op = (cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))
2016 ? JSOP_OBJECT
2017 : JSOP_REGEXP;
2018 408 tp->t_atom = atom;
2019 408 tt = TOK_OBJECT;
2020 408 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 8570 if (MatchChar(ts, '=')) {
2040 17 tp->t_op = JSOP_ADD;
2041 17 tt = TOK_ASSIGN;
2042 8553 } else if (MatchChar(ts, c)) {
2043 408 tt = TOK_INC;
2044 } else {
2045 8145 tp->t_op = JSOP_POS;
2046 8145 tt = TOK_PLUS;
2047 }
2048 break;
2049
2050 case '-':
2051 665 if (MatchChar(ts, '=')) {
2052 0 tp->t_op = JSOP_SUB;
2053 0 tt = TOK_ASSIGN;
2054 665 } else if (MatchChar(ts, c)) {
2055 34 if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE)) {
2056 0 ts->flags &= ~TSF_IN_HTML_COMMENT;
2057 0 goto skipline;
2058 }
2059 34 tt = TOK_DEC;
2060 } else {
2061 631 tp->t_op = JSOP_NEG;
2062 631 tt = TOK_MINUS;
2063 }
2064 break;
2065
2066 #if JS_HAS_SHARP_VARS
2067 case '#':
2068 {
2069 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 for (;;) {
2078 0 c = GetChar(ts);
2079 0 if (!JS7_ISDEC(c))
2080 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 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 break;
2110 }
2111 #endif /* JS_HAS_SHARP_VARS */
2112
2113 #if JS_HAS_SHARP_VARS || JS_HAS_XML_SUPPORT
2114 0 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 1850579 out:
2124 JS_ASSERT(tt != TOK_EOL);
2125 1850579 ts->flags |= TSF_DIRTYLINE;
2126
2127 1850579 eol_out:
2128 1850579 if (!STRING_BUFFER_OK(&ts->tokenbuf))
2129 0 tt = TOK_ERROR;
2130 JS_ASSERT(tt < TOK_LIMIT);
2131 1850579 tp->pos.end.index = ts->linepos +
2132 PTRDIFF(ts->linebuf.ptr, ts->linebuf.base, jschar) -
2133 ts->ungetpos;
2134 1850579 tp->type = tt;
2135 1850579 return tt;
2136
2137 0 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 13554629 {
2156 JS_ASSERT(ts->lookahead < NTOKENS_MASK);
2157 13554629 if (ts->flags & TSF_ERROR)
2158 13554629 return;
2159 13554629 ts->lookahead++;
2160 13554629 ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
2161 }
2162
2163 JSBool
2164 js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
2165 11399362 {
2166 11399362 if (js_GetToken(cx, ts) == tt)
2167 379552 return JS_TRUE;
2168 11019810 js_UngetToken(ts);
2169 11019810 return JS_FALSE;