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