1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
39 * ***** END LICENSE BLOCK ***** */
44 #include <stdio.h> /* first to avoid trouble on some systems */
56 #include "jsarena.h" /* Added by JSIFY */
58 #include "jsutil.h" /* Added by JSIFY */
64 #include "jsversion.h"
73 #include "jsstaticcheck.h"
76 #if JS_HAS_XML_SUPPORT
82 #define JS_KEYWORD(keyword, type, op, version) \
83 const char js_##keyword##_str[] = #keyword;
84 #include "jskeyword.tbl"
88 const char *chars
; /* C string with keyword text */
91 JSVersion version
; /* JSVersion */
94 static const struct keyword keyword_defs
[] = {
95 #define JS_KEYWORD(keyword, type, op, version) \
96 {js_##keyword##_str, type, op, version},
97 #include "jskeyword.tbl"
101 #define KEYWORD_COUNT JS_ARRAY_LENGTH(keyword_defs)
103 static const struct keyword
*
104 FindKeyword(const jschar
*s
, size_t length
)
107 const struct keyword
*kw
;
110 JS_ASSERT(length
!= 0);
112 #define JSKW_LENGTH() length
113 #define JSKW_AT(column) s[column]
114 #define JSKW_GOT_MATCH(index) i = (index); goto got_match;
115 #define JSKW_TEST_GUESS(index) i = (index); goto test_guess;
116 #define JSKW_NO_MATCH() goto no_match;
117 #include "jsautokw.h"
119 #undef JSKW_TEST_GUESS
120 #undef JSKW_GOT_MATCH
125 return &keyword_defs
[i
];
128 kw
= &keyword_defs
[i
];
131 if (*s
++ != (unsigned char)(*chars
++))
133 } while (--length
!= 0);
141 js_CheckKeyword(const jschar
*str
, size_t length
)
143 const struct keyword
*kw
;
145 JS_ASSERT(length
!= 0);
146 kw
= FindKeyword(str
, length
);
147 return kw
? kw
->tokentype
: TOK_EOF
;
151 js_IsIdentifier(JSString
*str
)
155 const jschar
*chars
, *end
;
157 str
->getCharsAndLength(chars
, length
);
161 if (!JS_ISIDSTART(c
))
163 end
= chars
+ length
;
164 while (++chars
!= end
) {
173 #pragma warning(push)
174 #pragma warning(disable:4351)
177 /* Initialize members that aren't initialized in |init|. */
178 TokenStream::TokenStream(JSContext
*cx
)
179 : cx(cx
), tokens(), cursor(), lookahead(), ungetpos(), ungetbuf(), flags(),
180 linepos(), lineposNext(), file(), listenerTSData(), tokenbuf(cx
)
188 TokenStream::init(const jschar
*base
, size_t length
, FILE *fp
, const char *fn
, uintN ln
)
192 JS_ASSERT_IF(fp
, !base
);
193 JS_ASSERT_IF(!base
, length
== 0);
195 ? 2 * LINE_LIMIT
* sizeof(jschar
)
196 : LINE_LIMIT
* sizeof(jschar
);
197 cx
->tempPool
.allocateCast
<jschar
*>(buf
, nb
);
199 js_ReportOutOfScriptQuota(cx
);
204 /* Initialize members. */
207 linebuf
.base
= linebuf
.limit
= linebuf
.ptr
= buf
;
210 userbuf
.base
= buf
+ LINE_LIMIT
;
211 userbuf
.ptr
= userbuf
.limit
= userbuf
.base
+ LINE_LIMIT
;
213 userbuf
.base
= (jschar
*)base
;
214 userbuf
.limit
= (jschar
*)base
+ length
;
215 userbuf
.ptr
= (jschar
*)base
;
217 listener
= cx
->debugHooks
->sourceHandler
;
218 listenerData
= cx
->debugHooks
->sourceHandlerData
;
225 if (flags
& TSF_OWNFILENAME
)
226 cx
->free((void *) filename
);
229 /* Use the fastest available getc. */
230 #if defined(HAVE_GETC_UNLOCKED)
231 # define fast_getc getc_unlocked
232 #elif defined(HAVE__GETC_NOLOCK)
233 # define fast_getc _getc_nolock
235 # define fast_getc getc
239 js_fgets(char *buf
, int size
, FILE *file
)
249 for (i
= 0; i
< n
&& (c
= fast_getc(file
)) != EOF
; i
++) {
251 if (c
== '\n') { /* any \n ends a line */
252 i
++; /* keep the \n; we know there is room for \0 */
255 if (crflag
) { /* \r not followed by \n ends line at the \r */
257 break; /* and overwrite c in buf with \0 */
259 crflag
= (c
== '\r');
267 * Nb: This does *not* append a terminating '\0'. Returns the number of chars
268 * read from the file.
271 TokenStream::fillUserbuf()
274 * We avoid splitting a \r\n pair, because this makes things much easier
275 * for getChar(). To do this, we only try to fill userbuf up with
276 * LINE_LIMIT-1 chars. Once we've reached that number, if the last one is
277 * \r then we check if the following one is \n; if so we get it too,
278 * knowing that we have space for it.
280 jschar
*buf
= userbuf
.base
;
281 int n
= LINE_LIMIT
- 1; /* reserve space for \n following a \r */
286 int c
= fast_getc(file
);
289 buf
[i
] = (jschar
) (unsigned char) c
;
293 if (buf
[i
- 1] == '\r') {
294 /* Look for a following \n. We know we have space in buf for it. */
299 buf
[i
] = (jschar
) (unsigned char) c
;
303 ungetc(c
, file
); /* \r wasn't followed by \n, unget */
312 TokenStream::getCharFillLinebuf()
314 ptrdiff_t ulen
= userbuf
.limit
- userbuf
.ptr
;
321 /* Fill userbuf so that \r and \r\n convert to \n. */
322 ulen
= fillUserbuf();
323 JS_ASSERT(ulen
>= 0);
328 userbuf
.limit
= userbuf
.base
+ ulen
;
329 userbuf
.ptr
= userbuf
.base
;
332 listener(filename
, lineno
, userbuf
.ptr
, ulen
, &listenerTSData
, listenerData
);
335 * Copy from userbuf to linebuf. Stop when any of these happen:
336 * (a) we reach the end of userbuf;
337 * (b) we reach the end of linebuf;
340 * "EOL" means any of: \r, \n, \r\n, or the Unicode line and paragraph
343 jschar
*from
= userbuf
.ptr
;
344 jschar
*to
= linebuf
.base
;
347 int limit
= JS_MIN(size_t(ulen
), LINE_LIMIT
);
350 /* Copy the jschar from userbuf to linebuf. */
351 jschar d
= to
[i
] = from
[i
];
355 * Normalize the copied jschar if it was a newline. Try to
356 * prevent multiple tests on most characters by first
357 * filtering out characters that aren't 000x or 202x.
359 if ((d
& 0xDFD0) == 0) {
365 to
[i
- 1] = '\n'; /* overwrite with '\n' */
366 if (i
< ulen
&& from
[i
] == '\n') {
367 i
++; /* skip over '\n' */
373 if (d
== LINE_SEPARATOR
|| d
== PARA_SEPARATOR
) {
374 to
[i
- 1] = '\n'; /* overwrite with '\n' */
380 /* At this point 'i' is the index one past the last char copied. */
384 /* Reset linebuf based on normalized length. */
385 linebuf
.ptr
= linebuf
.base
;
386 linebuf
.limit
= linebuf
.base
+ ulen
+ llenAdjust
;
388 /* Update position of linebuf within physical userbuf line. */
389 linepos
= lineposNext
;
390 if (linebuf
.limit
[-1] == '\n')
395 return *linebuf
.ptr
++;
399 * This gets the next char, normalizing all EOL sequences to '\n' as it goes.
402 TokenStream::getChar()
406 c
= ungetbuf
[--ungetpos
];
407 } else if (linebuf
.ptr
== linebuf
.limit
) {
408 c
= getCharFillLinebuf();
418 TokenStream::ungetChar(int32 c
)
422 JS_ASSERT(ungetpos
< JS_ARRAY_LENGTH(ungetbuf
));
425 ungetbuf
[ungetpos
++] = (jschar
)c
;
429 * Peek n chars ahead into ts. Return true if n chars were read, false if
430 * there weren't enough characters in the input stream. This function cannot
431 * be used to peek into or past a newline.
434 TokenStream::peekChars(intN n
, jschar
*cp
)
439 for (i
= 0; i
< n
; i
++) {
449 for (j
= i
- 1; j
>= 0; j
--)
455 TokenStream::reportCompileErrorNumberVA(JSParseNode
*pn
, uintN flags
, uintN errorNumber
,
458 JSErrorReport report
;
467 JSErrorReporter onError
;
469 JS_ASSERT(linebuf
.limit
<= linebuf
.base
+ LINE_LIMIT
);
471 if (JSREPORT_IS_STRICT(flags
) && !JS_HAS_STRICT_OPTION(cx
))
474 warning
= JSREPORT_IS_WARNING(flags
);
475 if (warning
&& JS_HAS_WERROR_OPTION(cx
)) {
476 flags
&= ~JSREPORT_WARNING
;
481 report
.flags
= flags
;
482 report
.errorNumber
= errorNumber
;
487 MUST_FLOW_THROUGH("out");
488 ok
= js_ExpandErrorArguments(cx
, js_GetErrorMessage
, NULL
,
489 errorNumber
, &message
, &report
,
490 !(flags
& JSREPORT_UC
), ap
);
496 report
.filename
= filename
;
499 report
.lineno
= pn
->pn_pos
.begin
.lineno
;
500 if (report
.lineno
!= lineno
)
504 /* Point to the current token, not the next one to get. */
505 tp
= &tokens
[cursor
].pos
;
507 report
.lineno
= lineno
;
508 linelength
= linebuf
.limit
- linebuf
.base
;
509 linechars
= (jschar
*)cx
->malloc((linelength
+ 1) * sizeof(jschar
));
514 memcpy(linechars
, linebuf
.base
, linelength
* sizeof(jschar
));
515 linechars
[linelength
] = 0;
516 linebytes
= js_DeflateString(cx
, linechars
, linelength
);
521 report
.linebuf
= linebytes
;
524 * FIXME: What should instead happen here is that we should
525 * find error-tokens in userbuf, if !file. That will
526 * allow us to deliver a more helpful error message, which
527 * includes all or part of the bad string or bad token. The
528 * code here yields something that looks truncated.
529 * See https://bugzilla.mozilla.org/show_bug.cgi?id=352970
532 if (tp
->begin
.lineno
== tp
->end
.lineno
) {
533 if (tp
->begin
.index
< linepos
)
536 index
= tp
->begin
.index
- linepos
;
539 report
.tokenptr
= report
.linebuf
+ index
;
540 report
.uclinebuf
= linechars
;
541 report
.uctokenptr
= report
.uclinebuf
+ index
;
544 * If there's a runtime exception type associated with this error
545 * number, set that as the pending exception. For errors occuring at
546 * compile time, this is very likely to be a JSEXN_SYNTAXERR.
548 * If an exception is thrown but not caught, the JSREPORT_EXCEPTION
549 * flag will be set in report.flags. Proper behavior for an error
550 * reporter is to ignore a report with this flag for all but top-level
551 * compilation errors. The exception will remain pending, and so long
552 * as the non-top-level "load", "eval", or "compile" native function
553 * returns false, the top-level reporter will eventually receive the
554 * uncaught exception report.
556 * XXX it'd probably be best if there was only one call to this
557 * function, but there seem to be two error reporter call points.
560 onError
= cx
->errorReporter
;
563 * Try to raise an exception only if there isn't one already set --
564 * otherwise the exception will describe the last compile-time error,
565 * which is likely spurious.
567 if (!(flags
& TSF_ERROR
)) {
568 if (js_ErrorToException(cx
, message
, &report
, NULL
, NULL
))
573 * Suppress any compile-time errors that don't occur at the top level.
574 * This may still fail, as interplevel may be zero in contexts where we
575 * don't really want to call the error reporter, as when js is called
576 * by other code which could catch the error.
578 if (cx
->interpLevel
!= 0 && !JSREPORT_IS_WARNING(flags
))
582 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
585 * If debugErrorHook is present then we give it a chance to veto
586 * sending the error on to the regular error reporter.
588 if (hook
&& !hook(cx
, message
, &report
,
589 cx
->debugHooks
->debugErrorHookData
)) {
594 (*onError
)(cx
, message
, &report
);
603 if (report
.ucmessage
)
604 cx
->free((void *)report
.ucmessage
);
606 if (report
.messageArgs
) {
607 if (!(flags
& JSREPORT_UC
)) {
609 while (report
.messageArgs
[i
])
610 cx
->free((void *)report
.messageArgs
[i
++]);
612 cx
->free((void *)report
.messageArgs
);
615 if (!JSREPORT_IS_WARNING(flags
)) {
616 /* Set the error flag to suppress spurious reports. */
624 js::ReportStrictModeError(JSContext
*cx
, TokenStream
*ts
, JSTreeContext
*tc
, JSParseNode
*pn
,
625 uintN errorNumber
, ...)
628 JS_ASSERT(cx
== ts
->getContext());
630 /* In strict mode code, this is an error, not just a warning. */
632 if ((tc
&& tc
->flags
& TCF_STRICT_MODE_CODE
) || (ts
&& ts
->isStrictMode()))
633 flags
= JSREPORT_ERROR
;
634 else if (JS_HAS_STRICT_OPTION(cx
))
635 flags
= JSREPORT_WARNING
;
640 va_start(ap
, errorNumber
);
641 bool result
= ts
->reportCompileErrorNumberVA(pn
, flags
, errorNumber
, ap
);
648 js::ReportCompileErrorNumber(JSContext
*cx
, TokenStream
*ts
, JSParseNode
*pn
,
649 uintN flags
, uintN errorNumber
, ...)
654 * We don't accept a JSTreeContext argument, so we can't implement
655 * JSREPORT_STRICT_MODE_ERROR here. Use ReportStrictModeError instead,
656 * or do the checks in the caller and pass plain old JSREPORT_ERROR.
658 JS_ASSERT(!(flags
& JSREPORT_STRICT_MODE_ERROR
));
660 va_start(ap
, errorNumber
);
661 JS_ASSERT(cx
== ts
->getContext());
662 bool result
= ts
->reportCompileErrorNumberVA(pn
, flags
, errorNumber
, ap
);
668 #if JS_HAS_XML_SUPPORT
671 TokenStream::getXMLEntity()
673 ptrdiff_t offset
, length
, i
;
680 JSCharBuffer
&tb
= tokenbuf
;
682 /* Put the entity, including the '&' already scanned, in tokenbuf. */
683 offset
= tb
.length();
686 while ((c
= getChar()) != ';') {
687 if (c
== EOF
|| c
== '\n') {
688 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_END_OF_XML_ENTITY
);
695 /* Let length be the number of jschars after the '&', including the ';'. */
696 length
= tb
.length() - offset
;
697 bp
= tb
.begin() + offset
;
700 if (length
> 2 && bp
[1] == '#') {
701 /* Match a well-formed XML Character Reference. */
703 if (length
> 3 && JS_TOLOWER(bp
[i
]) == 'x') {
704 if (length
> 9) /* at most 6 hex digits allowed */
706 while (++i
< length
) {
708 if (!JS7_ISHEX(digit
))
710 c
= (c
<< 4) + JS7_UNHEX(digit
);
715 if (!JS7_ISDEC(digit
))
717 c
= (c
* 10) + JS7_UNDEC(digit
);
723 if (0x10000 <= c
&& c
<= 0x10FFFF) {
724 /* Form a surrogate pair (c, d) -- c is the high surrogate. */
725 d
= 0xDC00 + (c
& 0x3FF);
726 c
= 0xD7C0 + (c
>> 10);
729 /* Enforce the http://www.w3.org/TR/REC-xml/#wf-Legalchar WFC. */
730 if (c
!= 0x9 && c
!= 0xA && c
!= 0xD &&
731 !(0x20 <= c
&& c
<= 0xD7FF) &&
732 !(0xE000 <= c
&& c
<= 0xFFFD)) {
737 /* Try to match one of the five XML 1.0 predefined entities. */
743 else if (bp
[1] == 'g')
748 if (bp
[1] == 'a' && bp
[2] == 'm' && bp
[3] == 'p')
753 if (bp
[1] == 'a' && bp
[2] == 'p' && bp
[4] == 's')
755 else if (bp
[1] == 'q' && bp
[2] == 'u' && bp
[4] == 't')
761 msg
= JSMSG_UNKNOWN_XML_ENTITY
;
766 /* If we matched, retract tokenbuf and store the entity's value. */
770 tb
.shrinkBy(tb
.end() - bp
);
774 msg
= JSMSG_BAD_XML_NCR
;
776 /* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */
777 JS_ASSERT((tb
.end() - bp
) >= 1);
778 bytes
= js_DeflateString(cx
, bp
+ 1, (tb
.end() - bp
) - 1);
780 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, msg
, bytes
);
786 #endif /* JS_HAS_XML_SUPPORT */
789 * We have encountered a '\': check for a Unicode escape sequence after it,
790 * returning the character code value if we found a Unicode escape sequence.
791 * Otherwise, non-destructively return the original '\'.
794 TokenStream::getUnicodeEscape()
799 if (peekChars(5, cp
) && cp
[0] == 'u' &&
800 JS7_ISHEX(cp
[1]) && JS7_ISHEX(cp
[2]) &&
801 JS7_ISHEX(cp
[3]) && JS7_ISHEX(cp
[4]))
803 c
= (((((JS7_UNHEX(cp
[1]) << 4)
804 + JS7_UNHEX(cp
[2])) << 4)
805 + JS7_UNHEX(cp
[3])) << 4)
814 TokenStream::newToken(ptrdiff_t adjust
)
816 cursor
= (cursor
+ 1) & ntokensMask
;
817 Token
*tp
= &tokens
[cursor
];
818 tp
->ptr
= linebuf
.ptr
+ adjust
;
819 tp
->pos
.begin
.index
= linepos
+ (tp
->ptr
- linebuf
.base
) - ungetpos
;
820 tp
->pos
.begin
.lineno
= tp
->pos
.end
.lineno
= lineno
;
824 static JS_ALWAYS_INLINE JSBool
825 ScanAsSpace(jschar c
)
827 /* Treat little- and big-endian BOMs as whitespace for compatibility. */
828 if (JS_ISSPACE(c
) || c
== 0xfffe || c
== 0xfeff)
833 static JS_ALWAYS_INLINE JSAtom
*
834 atomize(JSContext
*cx
, JSCharBuffer
&cb
)
836 return js_AtomizeChars(cx
, cb
.begin(), cb
.length(), 0);
840 TokenStream::getTokenInternal()
846 JSBool hadUnicodeEscape
;
847 const struct keyword
*kw
;
848 #if JS_HAS_XML_SUPPORT
851 ptrdiff_t contentIndex
;
854 #if JS_HAS_XML_SUPPORT
855 if (flags
& TSF_XMLTEXTMODE
) {
856 tt
= TOK_XMLSPACE
; /* veto if non-space, return TOK_XMLTEXT */
859 qc
= (flags
& TSF_XMLONLYMODE
) ? '<' : '{';
861 while ((c
= getChar()) != qc
&& c
!= '<' && c
!= EOF
) {
862 if (c
== '&' && qc
== '<') {
869 if (!JS_ISXMLSPACE(c
))
871 if (!tokenbuf
.append(c
))
876 if (tokenbuf
.empty()) {
879 atom
= atomize(cx
, tokenbuf
);
883 tp
->pos
.end
.lineno
= lineno
;
884 tp
->t_op
= JSOP_STRING
;
889 if (flags
& TSF_XMLTAGMODE
) {
892 if (JS_ISXMLSPACE(c
)) {
895 } while (JS_ISXMLSPACE(c
));
907 if (JS_ISXMLNSSTART(c
)) {
908 JSBool sawColon
= JS_FALSE
;
910 if (!tokenbuf
.append(c
))
912 while ((c
= getChar()) != EOF
&& JS_ISXMLNAME(c
)) {
918 ((flags
& TSF_XMLONLYMODE
) || nextc
!= '{') &&
919 !JS_ISXMLNAME(nextc
))) {
920 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
,
921 JSMSG_BAD_XML_QNAME
);
927 if (!tokenbuf
.append(c
))
932 atom
= atomize(cx
, tokenbuf
);
935 tp
->t_op
= JSOP_STRING
;
943 if (flags
& TSF_XMLONLYMODE
)
955 while ((c
= getChar()) != qc
) {
957 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
,
958 JSMSG_UNTERMINATED_STRING
);
963 * XML attribute values are double-quoted when pretty-printed,
964 * so escape " if it is expressed directly in a single-quoted
967 if (c
== '"' && !(flags
& TSF_XMLONLYMODE
)) {
968 JS_ASSERT(qc
== '\'');
969 if (!tokenbuf
.append(js_quot_entity_str
,
970 strlen(js_quot_entity_str
)))
975 if (c
== '&' && (flags
& TSF_XMLONLYMODE
)) {
981 if (!tokenbuf
.append(c
))
984 atom
= atomize(cx
, tokenbuf
);
987 tp
->pos
.end
.lineno
= lineno
;
988 tp
->t_op
= JSOP_STRING
;
998 if (matchChar('>')) {
1006 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_BAD_XML_CHARACTER
);
1011 #endif /* JS_HAS_XML_SUPPORT */
1017 flags
&= ~TSF_DIRTYLINE
;
1018 if (flags
& TSF_NEWLINES
)
1021 } while (ScanAsSpace((jschar
)c
));
1029 hadUnicodeEscape
= JS_FALSE
;
1030 if (JS_ISIDSTART(c
) ||
1032 (qc
= getUnicodeEscape(),
1033 hadUnicodeEscape
= JS_ISIDSTART(qc
)))) {
1034 if (hadUnicodeEscape
)
1038 if (!tokenbuf
.append(c
))
1042 qc
= getUnicodeEscape();
1043 if (!JS_ISIDENT(qc
))
1046 hadUnicodeEscape
= JS_TRUE
;
1055 * Check for keywords unless we saw Unicode escape or parser asks
1056 * to ignore keywords.
1058 if (!hadUnicodeEscape
&&
1059 !(flags
& TSF_KEYWORD_IS_NAME
) &&
1060 (kw
= FindKeyword(tokenbuf
.begin(), tokenbuf
.length()))) {
1061 if (kw
->tokentype
== TOK_RESERVED
) {
1062 if (!ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_WARNING
| JSREPORT_STRICT
,
1063 JSMSG_RESERVED_ID
, kw
->chars
)) {
1066 } else if (kw
->version
<= JSVERSION_NUMBER(cx
)) {
1068 tp
->t_op
= (JSOp
) kw
->op
;
1073 atom
= atomize(cx
, tokenbuf
);
1076 tp
->t_op
= JSOP_NAME
;
1082 if (JS7_ISDEC(c
) || (c
== '.' && JS7_ISDEC(peekChar()))) {
1084 const jschar
*endptr
;
1091 if (!tokenbuf
.append(c
))
1094 if (JS_TOLOWER(c
) == 'x') {
1095 if (!tokenbuf
.append(c
))
1099 } else if (JS7_ISDEC(c
)) {
1104 while (JS7_ISHEX(c
)) {
1110 /* Octal integer literals are not permitted in strict mode code. */
1111 if (!ReportStrictModeError(cx
, this, NULL
, NULL
, JSMSG_DEPRECATED_OCTAL
))
1115 * Outside strict mode, we permit 08 and 09 as decimal numbers, which
1116 * makes our behaviour a superset of the ECMA numeric grammar. We
1117 * might not always be so permissive, so we warn about it.
1120 if (!ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_WARNING
,
1121 JSMSG_BAD_OCTAL
, c
== '8' ? "08" : "09")) {
1128 if (!tokenbuf
.append(c
))
1133 if (radix
== 10 && (c
== '.' || JS_TOLOWER(c
) == 'e')) {
1136 if (!tokenbuf
.append(c
))
1139 } while (JS7_ISDEC(c
));
1141 if (JS_TOLOWER(c
) == 'e') {
1142 if (!tokenbuf
.append(c
))
1145 if (c
== '+' || c
== '-') {
1146 if (!tokenbuf
.append(c
))
1150 if (!JS7_ISDEC(c
)) {
1151 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
,
1152 JSMSG_MISSING_EXPONENT
);
1156 if (!tokenbuf
.append(c
))
1159 } while (JS7_ISDEC(c
));
1163 if (JS_ISIDSTART(c
)) {
1164 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_IDSTART_AFTER_NUMBER
);
1168 /* Put back the next char and NUL-terminate tokenbuf for js_strto*. */
1170 if (!tokenbuf
.append(0))
1174 if (!js_strtod(cx
, tokenbuf
.begin(), tokenbuf
.end(), &endptr
, &dval
)) {
1175 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_OUT_OF_MEMORY
);
1179 if (!js_strtointeger(cx
, tokenbuf
.begin(), tokenbuf
.end(),
1180 &endptr
, radix
, &dval
)) {
1181 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_OUT_OF_MEMORY
);
1190 if (c
== '"' || c
== '\'') {
1193 while ((c
= getChar()) != qc
) {
1194 if (c
== '\n' || c
== EOF
) {
1196 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
,
1197 JSMSG_UNTERMINATED_STRING
);
1201 switch (c
= getChar()) {
1202 case 'b': c
= '\b'; break;
1203 case 'f': c
= '\f'; break;
1204 case 'n': c
= '\n'; break;
1205 case 'r': c
= '\r'; break;
1206 case 't': c
= '\t'; break;
1207 case 'v': c
= '\v'; break;
1210 if ('0' <= c
&& c
< '8') {
1211 int32 val
= JS7_UNDEC(c
);
1214 /* Strict mode code allows only \0, then a non-digit. */
1215 if (val
!= 0 || JS7_ISDEC(c
)) {
1216 if (!ReportStrictModeError(cx
, this, NULL
, NULL
,
1217 JSMSG_DEPRECATED_OCTAL
)) {
1221 if ('0' <= c
&& c
< '8') {
1222 val
= 8 * val
+ JS7_UNDEC(c
);
1225 if ('0' <= c
&& c
< '8') {
1227 val
= 8 * val
+ JS7_UNDEC(c
);
1236 } else if (c
== 'u') {
1238 if (peekChars(4, cp
) &&
1239 JS7_ISHEX(cp
[0]) && JS7_ISHEX(cp
[1]) &&
1240 JS7_ISHEX(cp
[2]) && JS7_ISHEX(cp
[3])) {
1241 c
= (((((JS7_UNHEX(cp
[0]) << 4)
1242 + JS7_UNHEX(cp
[1])) << 4)
1243 + JS7_UNHEX(cp
[2])) << 4)
1247 } else if (c
== 'x') {
1249 if (peekChars(2, cp
) &&
1250 JS7_ISHEX(cp
[0]) && JS7_ISHEX(cp
[1])) {
1251 c
= (JS7_UNHEX(cp
[0]) << 4) + JS7_UNHEX(cp
[1]);
1254 } else if (c
== '\n') {
1255 /* ECMA follows C by removing escaped newlines. */
1261 if (!tokenbuf
.append(c
))
1264 atom
= atomize(cx
, tokenbuf
);
1267 tp
->pos
.end
.lineno
= lineno
;
1268 tp
->t_op
= JSOP_STRING
;
1275 case '\n': tt
= TOK_EOL
; goto eol_out
;
1276 case ';': tt
= TOK_SEMI
; break;
1277 case '[': tt
= TOK_LB
; break;
1278 case ']': tt
= TOK_RB
; break;
1279 case '{': tt
= TOK_LC
; break;
1280 case '}': tt
= TOK_RC
; break;
1281 case '(': tt
= TOK_LP
; break;
1282 case ')': tt
= TOK_RP
; break;
1283 case ',': tt
= TOK_COMMA
; break;
1284 case '?': tt
= TOK_HOOK
; break;
1287 #if JS_HAS_XML_SUPPORT
1296 #if JS_HAS_XML_SUPPORT
1303 * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an
1304 * object initializer, likewise for setter.
1306 tp
->t_op
= JSOP_NOP
;
1313 } else if (matchChar('=')) {
1314 tp
->t_op
= JSOP_BITOR
;
1322 if (matchChar('=')) {
1323 tp
->t_op
= JSOP_BITXOR
;
1333 } else if (matchChar('=')) {
1334 tp
->t_op
= JSOP_BITAND
;
1343 tp
->t_op
= matchChar(c
) ? JSOP_STRICTEQ
: JSOP_EQ
;
1346 tp
->t_op
= JSOP_NOP
;
1352 if (matchChar('=')) {
1353 tp
->t_op
= matchChar('=') ? JSOP_STRICTNE
: JSOP_NE
;
1356 tp
->t_op
= JSOP_NOT
;
1361 #if JS_HAS_XML_SUPPORT
1368 #if JS_HAS_XML_SUPPORT
1370 * After much testing, it's clear that Postel's advice to protocol
1371 * designers ("be liberal in what you accept, and conservative in what
1372 * you send") invites a natural-law repercussion for JS as "protocol":
1374 * "If you are liberal in what you accept, others will utterly fail to
1375 * be conservative in what they send."
1377 * Which means you will get <!-- comments to end of line in the middle
1378 * of .js files, and after if conditions whose then statements are on
1379 * the next line, and other wonders. See at least the following bugs:
1380 * https://bugzilla.mozilla.org/show_bug.cgi?id=309242
1381 * https://bugzilla.mozilla.org/show_bug.cgi?id=309712
1382 * https://bugzilla.mozilla.org/show_bug.cgi?id=310993
1384 * So without JSOPTION_XML, we changed around Firefox 1.5 never to scan
1385 * an XML comment or CDATA literal. Instead, we always scan <! as the
1386 * start of an HTML comment hack to end of line, used since Netscape 2
1387 * to hide script tag content from script-unaware browsers.
1389 * But this still leaves XML resources with certain internal structure
1390 * vulnerable to being loaded as script cross-origin, and some internal
1391 * data stolen, so for Firefox 3.5 and beyond, we reject programs whose
1392 * source consists only of XML literals. See:
1394 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
1396 * The check for this is in jsparse.cpp, Compiler::compileScript.
1398 if ((flags
& TSF_OPERAND
) &&
1399 (JS_HAS_XML_OPTION(cx
) || peekChar() != '!')) {
1400 /* Check for XML comment or CDATA section. */
1401 if (matchChar('!')) {
1404 /* Scan XML comment. */
1405 if (matchChar('-')) {
1406 if (!matchChar('-'))
1407 goto bad_xml_markup
;
1408 while ((c
= getChar()) != '-' || !matchChar('-')) {
1410 goto bad_xml_markup
;
1411 if (!tokenbuf
.append(c
))
1414 tt
= TOK_XMLCOMMENT
;
1415 tp
->t_op
= JSOP_XMLCOMMENT
;
1416 goto finish_xml_markup
;
1419 /* Scan CDATA section. */
1420 if (matchChar('[')) {
1422 if (peekChars(6, cp
) &&
1430 while ((c
= getChar()) != ']' ||
1431 !peekChars(2, cp
) ||
1435 goto bad_xml_markup
;
1436 if (!tokenbuf
.append(c
))
1439 getChar(); /* discard ] but not > */
1441 tp
->t_op
= JSOP_XMLCDATA
;
1442 goto finish_xml_markup
;
1444 goto bad_xml_markup
;
1448 /* Check for processing instruction. */
1449 if (matchChar('?')) {
1455 while ((c
= getChar()) != '?' || peekChar() != '>') {
1457 goto bad_xml_markup
;
1459 if (JS_ISXMLSPACE(c
)) {
1460 if (tokenbuf
.empty())
1461 goto bad_xml_markup
;
1462 inTarget
= JS_FALSE
;
1464 if (!(tokenbuf
.empty()
1465 ? JS_ISXMLNSSTART(c
)
1467 goto bad_xml_markup
;
1472 if (contentIndex
< 0 && !JS_ISXMLSPACE(c
))
1473 contentIndex
= tokenbuf
.length();
1475 if (!tokenbuf
.append(c
))
1478 if (targetLength
== 0)
1479 goto bad_xml_markup
;
1480 if (contentIndex
< 0) {
1481 atom
= cx
->runtime
->atomState
.emptyAtom
;
1483 atom
= js_AtomizeChars(cx
,
1484 tokenbuf
.begin() + contentIndex
,
1485 tokenbuf
.length() - contentIndex
,
1490 tokenbuf
.shrinkBy(tokenbuf
.length() - targetLength
);
1495 if (!matchChar('>'))
1496 goto bad_xml_markup
;
1497 atom
= atomize(cx
, tokenbuf
);
1501 tp
->pos
.end
.lineno
= lineno
;
1505 /* An XML start-of-tag character. */
1506 tt
= matchChar('/') ? TOK_XMLETAGO
: TOK_XMLSTAGO
;
1510 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_BAD_XML_MARKUP
);
1513 #endif /* JS_HAS_XML_SUPPORT */
1515 /* NB: treat HTML begin-comment as comment-till-end-of-line */
1516 if (matchChar('!')) {
1517 if (matchChar('-')) {
1518 if (matchChar('-')) {
1519 flags
|= TSF_IN_HTML_COMMENT
;
1527 tp
->t_op
= JSOP_LSH
;
1528 tt
= matchChar('=') ? TOK_ASSIGN
: TOK_SHOP
;
1530 tp
->t_op
= matchChar('=') ? JSOP_LE
: JSOP_LT
;
1537 tp
->t_op
= matchChar(c
) ? JSOP_URSH
: JSOP_RSH
;
1538 tt
= matchChar('=') ? TOK_ASSIGN
: TOK_SHOP
;
1540 tp
->t_op
= matchChar('=') ? JSOP_GE
: JSOP_GT
;
1546 tp
->t_op
= JSOP_MUL
;
1547 tt
= matchChar('=') ? TOK_ASSIGN
: TOK_STAR
;
1551 if (matchChar('/')) {
1553 * Hack for source filters such as the Mozilla XUL preprocessor:
1554 * "//@line 123\n" sets the number of the *next* line after the
1557 if (JS_HAS_ATLINE_OPTION(cx
)) {
1559 uintN i
, line
, temp
;
1560 char filenameBuf
[1024];
1562 if (peekChars(5, cp
) &&
1569 while ((c
= getChar()) != '\n' && ScanAsSpace((jschar
)c
))
1572 line
= JS7_UNDEC(c
);
1573 while ((c
= getChar()) != EOF
&& JS7_ISDEC(c
)) {
1574 temp
= 10 * line
+ JS7_UNDEC(c
);
1576 /* Ignore overlarge line numbers. */
1581 while (c
!= '\n' && ScanAsSpace((jschar
)c
))
1585 while ((c
= getChar()) != EOF
&& c
!= '"') {
1590 if ((c
>> 8) != 0 || i
>= sizeof filenameBuf
- 1)
1592 filenameBuf
[i
++] = (char) c
;
1595 while ((c
= getChar()) != '\n' &&
1596 ScanAsSpace((jschar
)c
)) {
1601 filenameBuf
[i
] = '\0';
1604 if (flags
& TSF_OWNFILENAME
)
1605 cx
->free((void *) filename
);
1606 filename
= JS_strdup(cx
, filenameBuf
);
1609 flags
|= TSF_OWNFILENAME
;
1619 /* Optimize line skipping if we are not in an HTML comment. */
1620 if (flags
& TSF_IN_HTML_COMMENT
) {
1621 while ((c
= getChar()) != EOF
&& c
!= '\n') {
1622 if (c
== '-' && matchChar('-') && matchChar('>'))
1623 flags
&= ~TSF_IN_HTML_COMMENT
;
1626 while ((c
= getChar()) != EOF
&& c
!= '\n')
1630 cursor
= (cursor
- 1) & ntokensMask
;
1634 if (matchChar('*')) {
1635 uintN linenoBefore
= lineno
;
1636 while ((c
= getChar()) != EOF
&&
1637 !(c
== '*' && matchChar('/'))) {
1638 /* Ignore all characters until comment close. */
1641 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
,
1642 JSMSG_UNTERMINATED_COMMENT
);
1645 if ((flags
& TSF_NEWLINES
) && linenoBefore
!= lineno
) {
1646 flags
&= ~TSF_DIRTYLINE
;
1650 cursor
= (cursor
- 1) & ntokensMask
;
1654 if (flags
& TSF_OPERAND
) {
1655 uintN reflags
, length
;
1656 JSBool inCharClass
= JS_FALSE
;
1661 if (c
== '\n' || c
== EOF
) {
1663 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
,
1664 JSMSG_UNTERMINATED_REGEXP
);
1668 if (!tokenbuf
.append(c
))
1671 } else if (c
== '[') {
1672 inCharClass
= JS_TRUE
;
1673 } else if (c
== ']') {
1674 inCharClass
= JS_FALSE
;
1675 } else if (c
== '/' && !inCharClass
) {
1676 /* For compat with IE, allow unescaped / in char classes. */
1679 if (!tokenbuf
.append(c
))
1682 for (reflags
= 0, length
= tokenbuf
.length() + 1; ; length
++) {
1684 if (c
== 'g' && !(reflags
& JSREG_GLOB
))
1685 reflags
|= JSREG_GLOB
;
1686 else if (c
== 'i' && !(reflags
& JSREG_FOLD
))
1687 reflags
|= JSREG_FOLD
;
1688 else if (c
== 'm' && !(reflags
& JSREG_MULTILINE
))
1689 reflags
|= JSREG_MULTILINE
;
1690 else if (c
== 'y' && !(reflags
& JSREG_STICKY
))
1691 reflags
|= JSREG_STICKY
;
1698 char buf
[2] = { '\0' };
1699 tp
->pos
.begin
.index
+= length
+ 1;
1701 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_BAD_REGEXP_FLAG
,
1706 tp
->t_reflags
= reflags
;
1711 tp
->t_op
= JSOP_DIV
;
1712 tt
= matchChar('=') ? TOK_ASSIGN
: TOK_DIVOP
;
1716 tp
->t_op
= JSOP_MOD
;
1717 tt
= matchChar('=') ? TOK_ASSIGN
: TOK_DIVOP
;
1721 tp
->t_op
= JSOP_BITNOT
;
1726 if (matchChar('=')) {
1727 tp
->t_op
= JSOP_ADD
;
1729 } else if (matchChar(c
)) {
1732 tp
->t_op
= JSOP_POS
;
1738 if (matchChar('=')) {
1739 tp
->t_op
= JSOP_SUB
;
1741 } else if (matchChar(c
)) {
1742 if (peekChar() == '>' && !(flags
& TSF_DIRTYLINE
)) {
1743 flags
&= ~TSF_IN_HTML_COMMENT
;
1748 tp
->t_op
= JSOP_NEG
;
1753 #if JS_HAS_SHARP_VARS
1759 if (!JS7_ISDEC(c
)) {
1763 n
= (uint32
)JS7_UNDEC(c
);
1768 n
= 10 * n
+ JS7_UNDEC(c
);
1769 if (n
>= UINT16_LIMIT
) {
1770 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_SHARPVAR_TOO_BIG
);
1774 tp
->t_dval
= (jsdouble
) n
;
1775 if (JS_HAS_STRICT_OPTION(cx
) &&
1776 (c
== '=' || c
== '#')) {
1778 JS_snprintf(buf
, sizeof buf
, "#%u%c", n
, c
);
1779 if (!ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_WARNING
| JSREPORT_STRICT
,
1780 JSMSG_DEPRECATED_USAGE
, buf
)) {
1792 #endif /* JS_HAS_SHARP_VARS */
1794 #if JS_HAS_SHARP_VARS || JS_HAS_XML_SUPPORT
1799 ReportCompileErrorNumber(cx
, this, NULL
, JSREPORT_ERROR
, JSMSG_ILLEGAL_CHARACTER
);
1804 JS_ASSERT(tt
!= TOK_EOL
);
1805 flags
|= TSF_DIRTYLINE
;
1808 JS_ASSERT(tt
< TOK_LIMIT
);
1809 tp
->pos
.end
.index
= linepos
+ (linebuf
.ptr
- linebuf
.base
) - ungetpos
;