Back to index

lightning-sunbird  0.9+nobinonly
jsscan.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set sw=4 ts=8 et tw=78:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS lexical scanner.
00043  */
00044 #include "jsstddef.h"
00045 #include <stdio.h>      /* first to avoid trouble on some systems */
00046 #include <errno.h>
00047 #include <limits.h>
00048 #include <math.h>
00049 #ifdef HAVE_MEMORY_H
00050 #include <memory.h>
00051 #endif
00052 #include <stdarg.h>
00053 #include <stdlib.h>
00054 #include <string.h>
00055 #include "jstypes.h"
00056 #include "jsarena.h" /* Added by JSIFY */
00057 #include "jsutil.h" /* Added by JSIFY */
00058 #include "jsdtoa.h"
00059 #include "jsprf.h"
00060 #include "jsapi.h"
00061 #include "jsatom.h"
00062 #include "jscntxt.h"
00063 #include "jsconfig.h"
00064 #include "jsemit.h"
00065 #include "jsexn.h"
00066 #include "jsnum.h"
00067 #include "jsopcode.h"
00068 #include "jsregexp.h"
00069 #include "jsscan.h"
00070 #include "jsscript.h"
00071 
00072 #if JS_HAS_XML_SUPPORT
00073 #include "jsparse.h"
00074 #include "jsxml.h"
00075 #endif
00076 
00077 #define JS_KEYWORD(keyword, type, op, version) \
00078     const char js_##keyword##_str[] = #keyword;
00079 #include "jskeyword.tbl"
00080 #undef JS_KEYWORD
00081 
00082 struct keyword {
00083     const char  *chars;         /* C string with keyword text */
00084     JSTokenType tokentype;      /* JSTokenType */
00085     JSOp        op;             /* JSOp */
00086     JSVersion   version;        /* JSVersion */
00087 };
00088 
00089 static const struct keyword keyword_defs[] = {
00090 #define JS_KEYWORD(keyword, type, op, version) \
00091     {js_##keyword##_str, type, op, version},
00092 #include "jskeyword.tbl"
00093 #undef JS_KEYWORD
00094 };
00095 
00096 #define KEYWORD_COUNT (sizeof keyword_defs / sizeof keyword_defs[0])
00097 
00098 static const struct keyword *
00099 FindKeyword(const jschar *s, size_t length)
00100 {
00101     register size_t i;
00102     const struct keyword *kw;
00103     const char *chars;
00104 
00105     JS_ASSERT(length != 0);
00106 
00107 #define JSKW_LENGTH()           length
00108 #define JSKW_AT(column)         s[column]
00109 #define JSKW_GOT_MATCH(index)   i = (index); goto got_match;
00110 #define JSKW_TEST_GUESS(index)  i = (index); goto test_guess;
00111 #define JSKW_NO_MATCH()         goto no_match;
00112 #include "jsautokw.h"
00113 #undef JSKW_NO_MATCH
00114 #undef JSKW_TEST_GUESS
00115 #undef JSKW_GOT_MATCH
00116 #undef JSKW_AT
00117 #undef JSKW_LENGTH
00118 
00119   got_match:
00120     return &keyword_defs[i];
00121 
00122   test_guess:
00123     kw = &keyword_defs[i];
00124     chars = kw->chars;
00125     do {
00126         if (*s++ != (unsigned char)(*chars++))
00127             goto no_match;
00128     } while (--length != 0);
00129     return kw;
00130 
00131   no_match:
00132     return NULL;
00133 }
00134 
00135 JSTokenType
00136 js_CheckKeyword(const jschar *str, size_t length)
00137 {
00138     const struct keyword *kw;
00139 
00140     JS_ASSERT(length != 0);
00141     kw = FindKeyword(str, length);
00142     return kw ? kw->tokentype : TOK_EOF;
00143 }
00144 
00145 JS_FRIEND_API(void)
00146 js_MapKeywords(void (*mapfun)(const char *))
00147 {
00148     size_t i;
00149 
00150     for (i = 0; i != KEYWORD_COUNT; ++i)
00151         mapfun(keyword_defs[i].chars);
00152 }
00153 
00154 JSTokenStream *
00155 js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,
00156                   const char *filename, uintN lineno,
00157                   JSPrincipals *principals)
00158 {
00159     JSTokenStream *ts;
00160 
00161     ts = js_NewBufferTokenStream(cx, base, length);
00162     if (!ts)
00163         return NULL;
00164     ts->filename = filename;
00165     ts->lineno = lineno;
00166     if (principals)
00167         JSPRINCIPALS_HOLD(cx, principals);
00168     ts->principals = principals;
00169     return ts;
00170 }
00171 
00172 #define TBMIN   64
00173 
00174 static JSBool
00175 GrowTokenBuf(JSStringBuffer *sb, size_t newlength)
00176 {
00177     JSContext *cx;
00178     jschar *base;
00179     ptrdiff_t offset, length;
00180     size_t tbsize;
00181     JSArenaPool *pool;
00182 
00183     cx = sb->data;
00184     base = sb->base;
00185     offset = PTRDIFF(sb->ptr, base, jschar);
00186     pool = &cx->tempPool;
00187     if (!base) {
00188         tbsize = TBMIN * sizeof(jschar);
00189         length = TBMIN - 1;
00190         JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize);
00191     } else {
00192         length = PTRDIFF(sb->limit, base, jschar);
00193         if ((size_t)length >= ~(size_t)0 / sizeof(jschar)) {
00194             base = NULL;
00195         } else {
00196             tbsize = (length + 1) * sizeof(jschar);
00197             length += length + 1;
00198             JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize);
00199         }
00200     }
00201     if (!base) {
00202         JS_ReportOutOfMemory(cx);
00203         sb->base = STRING_BUFFER_ERROR_BASE;
00204         return JS_FALSE;
00205     }
00206     sb->base = base;
00207     sb->limit = base + length;
00208     sb->ptr = base + offset;
00209     return JS_TRUE;
00210 }
00211 
00212 JS_FRIEND_API(JSTokenStream *)
00213 js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
00214 {
00215     size_t nb;
00216     JSTokenStream *ts;
00217 
00218     nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);
00219     JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb);
00220     if (!ts) {
00221         JS_ReportOutOfMemory(cx);
00222         return NULL;
00223     }
00224     memset(ts, 0, nb);
00225     ts->lineno = 1;
00226     ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);
00227     ts->userbuf.base = (jschar *)base;
00228     ts->userbuf.limit = (jschar *)base + length;
00229     ts->userbuf.ptr = (jschar *)base;
00230     ts->tokenbuf.grow = GrowTokenBuf;
00231     ts->tokenbuf.data = cx;
00232     ts->listener = cx->runtime->sourceHandler;
00233     ts->listenerData = cx->runtime->sourceHandlerData;
00234     return ts;
00235 }
00236 
00237 JS_FRIEND_API(JSTokenStream *)
00238 js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp)
00239 {
00240     jschar *base;
00241     JSTokenStream *ts;
00242     FILE *file;
00243 
00244     JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool,
00245                            JS_LINE_LIMIT * sizeof(jschar));
00246     if (!base)
00247         return NULL;
00248     ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT);
00249     if (!ts)
00250         return NULL;
00251     if (!filename || strcmp(filename, "-") == 0) {
00252         file = defaultfp;
00253     } else {
00254         file = fopen(filename, "r");
00255         if (!file) {
00256             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
00257                                  filename, "No such file or directory");
00258             return NULL;
00259         }
00260     }
00261     ts->userbuf.ptr = ts->userbuf.limit;
00262     ts->file = file;
00263     ts->filename = filename;
00264     return ts;
00265 }
00266 
00267 JS_FRIEND_API(JSBool)
00268 js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)
00269 {
00270     if (ts->flags & TSF_OWNFILENAME)
00271         JS_free(cx, (void *) ts->filename);
00272     if (ts->principals)
00273         JSPRINCIPALS_DROP(cx, ts->principals);
00274     return !ts->file || fclose(ts->file) == 0;
00275 }
00276 
00277 JS_FRIEND_API(int)
00278 js_fgets(char *buf, int size, FILE *file)
00279 {
00280     int n, i, c;
00281     JSBool crflag;
00282 
00283     n = size - 1;
00284     if (n < 0)
00285         return -1;
00286 
00287     crflag = JS_FALSE;
00288     for (i = 0; i < n && (c = getc(file)) != EOF; i++) {
00289         buf[i] = c;
00290         if (c == '\n') {        /* any \n ends a line */
00291             i++;                /* keep the \n; we know there is room for \0 */
00292             break;
00293         }
00294         if (crflag) {           /* \r not followed by \n ends line at the \r */
00295             ungetc(c, file);
00296             break;              /* and overwrite c in buf with \0 */
00297         }
00298         crflag = (c == '\r');
00299     }
00300 
00301     buf[i] = '\0';
00302     return i;
00303 }
00304 
00305 static int32
00306 GetChar(JSTokenStream *ts)
00307 {
00308     int32 c;
00309     ptrdiff_t i, j, len, olen;
00310     JSBool crflag;
00311     char cbuf[JS_LINE_LIMIT];
00312     jschar *ubuf, *nl;
00313 
00314     if (ts->ungetpos != 0) {
00315         c = ts->ungetbuf[--ts->ungetpos];
00316     } else {
00317         if (ts->linebuf.ptr == ts->linebuf.limit) {
00318             len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
00319             if (len <= 0) {
00320                 if (!ts->file) {
00321                     ts->flags |= TSF_EOF;
00322                     return EOF;
00323                 }
00324 
00325                 /* Fill ts->userbuf so that \r and \r\n convert to \n. */
00326                 crflag = (ts->flags & TSF_CRFLAG) != 0;
00327                 len = js_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file);
00328                 if (len <= 0) {
00329                     ts->flags |= TSF_EOF;
00330                     return EOF;
00331                 }
00332                 olen = len;
00333                 ubuf = ts->userbuf.base;
00334                 i = 0;
00335                 if (crflag) {
00336                     ts->flags &= ~TSF_CRFLAG;
00337                     if (cbuf[0] != '\n') {
00338                         ubuf[i++] = '\n';
00339                         len++;
00340                         ts->linepos--;
00341                     }
00342                 }
00343                 for (j = 0; i < len; i++, j++)
00344                     ubuf[i] = (jschar) (unsigned char) cbuf[j];
00345                 ts->userbuf.limit = ubuf + len;
00346                 ts->userbuf.ptr = ubuf;
00347             }
00348             if (ts->listener) {
00349                 ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len,
00350                              &ts->listenerTSData, ts->listenerData);
00351             }
00352 
00353             nl = ts->saveEOL;
00354             if (!nl) {
00355                 /*
00356                  * Any one of \n, \r, or \r\n ends a line (the longest
00357                  * match wins).  Also allow the Unicode line and paragraph
00358                  * separators.
00359                  */
00360                 for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) {
00361                     /*
00362                      * Try to prevent value-testing on most characters by
00363                      * filtering out characters that aren't 000x or 202x.
00364                      */
00365                     if ((*nl & 0xDFD0) == 0) {
00366                         if (*nl == '\n')
00367                             break;
00368                         if (*nl == '\r') {
00369                             if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
00370                                 nl++;
00371                             break;
00372                         }
00373                         if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR)
00374                             break;
00375                     }
00376                 }
00377             }
00378 
00379             /*
00380              * If there was a line terminator, copy thru it into linebuf.
00381              * Else copy JS_LINE_LIMIT-1 bytes into linebuf.
00382              */
00383             if (nl < ts->userbuf.limit)
00384                 len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;
00385             if (len >= JS_LINE_LIMIT) {
00386                 len = JS_LINE_LIMIT - 1;
00387                 ts->saveEOL = nl;
00388             } else {
00389                 ts->saveEOL = NULL;
00390             }
00391             js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);
00392             ts->userbuf.ptr += len;
00393             olen = len;
00394 
00395             /*
00396              * Make sure linebuf contains \n for EOL (don't do this in
00397              * userbuf because the user's string might be readonly).
00398              */
00399             if (nl < ts->userbuf.limit) {
00400                 if (*nl == '\r') {
00401                     if (ts->linebuf.base[len-1] == '\r') {
00402                         /*
00403                          * Does the line segment end in \r?  We must check
00404                          * for a \n at the front of the next segment before
00405                          * storing a \n into linebuf.  This case matters
00406                          * only when we're reading from a file.
00407                          */
00408                         if (nl + 1 == ts->userbuf.limit && ts->file) {
00409                             len--;
00410                             ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */
00411                             if (len == 0) {
00412                                 /*
00413                                  * This can happen when a segment ends in
00414                                  * \r\r.  Start over.  ptr == limit in this
00415                                  * case, so we'll fall into buffer-filling
00416                                  * code.
00417                                  */
00418                                 return GetChar(ts);
00419                             }
00420                         } else {
00421                             ts->linebuf.base[len-1] = '\n';
00422                         }
00423                     }
00424                 } else if (*nl == '\n') {
00425                     if (nl > ts->userbuf.base &&
00426                         nl[-1] == '\r' &&
00427                         ts->linebuf.base[len-2] == '\r') {
00428                         len--;
00429                         JS_ASSERT(ts->linebuf.base[len] == '\n');
00430                         ts->linebuf.base[len-1] = '\n';
00431                     }
00432                 } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) {
00433                     ts->linebuf.base[len-1] = '\n';
00434                 }
00435             }
00436 
00437             /* Reset linebuf based on adjusted segment length. */
00438             ts->linebuf.limit = ts->linebuf.base + len;
00439             ts->linebuf.ptr = ts->linebuf.base;
00440 
00441             /* Update position of linebuf within physical userbuf line. */
00442             if (!(ts->flags & TSF_NLFLAG))
00443                 ts->linepos += ts->linelen;
00444             else
00445                 ts->linepos = 0;
00446             if (ts->linebuf.limit[-1] == '\n')
00447                 ts->flags |= TSF_NLFLAG;
00448             else
00449                 ts->flags &= ~TSF_NLFLAG;
00450 
00451             /* Update linelen from original segment length. */
00452             ts->linelen = olen;
00453         }
00454         c = *ts->linebuf.ptr++;
00455     }
00456     if (c == '\n')
00457         ts->lineno++;
00458     return c;
00459 }
00460 
00461 static void
00462 UngetChar(JSTokenStream *ts, int32 c)
00463 {
00464     if (c == EOF)
00465         return;
00466     JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
00467     if (c == '\n')
00468         ts->lineno--;
00469     ts->ungetbuf[ts->ungetpos++] = (jschar)c;
00470 }
00471 
00472 static int32
00473 PeekChar(JSTokenStream *ts)
00474 {
00475     int32 c;
00476 
00477     c = GetChar(ts);
00478     UngetChar(ts, c);
00479     return c;
00480 }
00481 
00482 /*
00483  * Peek n chars ahead into ts.  Return true if n chars were read, false if
00484  * there weren't enough characters in the input stream.  This function cannot
00485  * be used to peek into or past a newline.
00486  */
00487 static JSBool
00488 PeekChars(JSTokenStream *ts, intN n, jschar *cp)
00489 {
00490     intN i, j;
00491     int32 c;
00492 
00493     for (i = 0; i < n; i++) {
00494         c = GetChar(ts);
00495         if (c == EOF)
00496             break;
00497         if (c == '\n') {
00498             UngetChar(ts, c);
00499             break;
00500         }
00501         cp[i] = (jschar)c;
00502     }
00503     for (j = i - 1; j >= 0; j--)
00504         UngetChar(ts, cp[j]);
00505     return i == n;
00506 }
00507 
00508 static void
00509 SkipChars(JSTokenStream *ts, intN n)
00510 {
00511     while (--n >= 0)
00512         GetChar(ts);
00513 }
00514 
00515 static JSBool
00516 MatchChar(JSTokenStream *ts, int32 expect)
00517 {
00518     int32 c;
00519 
00520     c = GetChar(ts);
00521     if (c == expect)
00522         return JS_TRUE;
00523     UngetChar(ts, c);
00524     return JS_FALSE;
00525 }
00526 
00527 static JSBool
00528 ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags,
00529                          uintN errorNumber, JSErrorReport *report,
00530                          JSBool charArgs, va_list ap)
00531 {
00532     JSTempValueRooter linetvr;
00533     JSString *linestr = NULL;
00534     JSTokenStream *ts = NULL;
00535     JSCodeGenerator *cg = NULL;
00536     JSParseNode *pn = NULL;
00537     JSErrorReporter onError;
00538     JSTokenPos *tp;
00539     JSStackFrame *fp;
00540     uintN index;
00541     char *message;
00542     JSBool warning;
00543 
00544     memset(report, 0, sizeof (struct JSErrorReport));
00545     report->flags = flags;
00546     report->errorNumber = errorNumber;
00547     message = NULL;
00548 
00549     if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL,
00550                                  errorNumber, &message, report, &warning,
00551                                  charArgs, ap)) {
00552         return JS_FALSE;
00553     }
00554 
00555     JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &linetvr);
00556 
00557     switch (flags & JSREPORT_HANDLE) {
00558       case JSREPORT_TS:
00559         ts = handle;
00560         break;
00561       case JSREPORT_CG:
00562         cg = handle;
00563         break;
00564       case JSREPORT_PN:
00565         pn = handle;
00566         ts = pn->pn_ts;
00567         break;
00568     }
00569 
00570     JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);
00571     /*
00572      * We are typically called with non-null ts and null cg from jsparse.c.
00573      * We can be called with null ts from the regexp compilation functions.
00574      * The code generator (jsemit.c) may pass null ts and non-null cg.
00575      */
00576     do {
00577         if (ts) {
00578             report->filename = ts->filename;
00579             if (pn) {
00580                 report->lineno = pn->pn_pos.begin.lineno;
00581                 if (report->lineno != ts->lineno)
00582                     break;
00583             }
00584             report->lineno = ts->lineno;
00585             linestr = js_NewStringCopyN(cx, ts->linebuf.base,
00586                                         PTRDIFF(ts->linebuf.limit,
00587                                                 ts->linebuf.base,
00588                                                 jschar),
00589                                         0);
00590             linetvr.u.string = linestr;
00591             report->linebuf = linestr
00592                               ? JS_GetStringBytes(linestr)
00593                               : NULL;
00594             tp = &ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos;
00595             if (pn)
00596                 tp = &pn->pn_pos;
00597 
00598             /*
00599              * FIXME: What should instead happen here is that we should
00600              * find error-tokens in userbuf, if !ts->file.  That will
00601              * allow us to deliver a more helpful error message, which
00602              * includes all or part of the bad string or bad token.  The
00603              * code here yields something that looks truncated.
00604              * See https://bugzilla.mozilla.org/show_bug.cgi?id=352970
00605              */
00606             index = 0;
00607             if (tp->begin.lineno == tp->end.lineno) {
00608                 if (tp->begin.index < ts->linepos)
00609                     break;
00610 
00611                 index = tp->begin.index - ts->linepos;
00612             }
00613 
00614             report->tokenptr = linestr ? report->linebuf + index : NULL;
00615             report->uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL;
00616             report->uctokenptr = linestr ? report->uclinebuf + index : NULL;
00617             break;
00618         }
00619 
00620         if (cg) {
00621             report->filename = cg->filename;
00622             report->lineno = CG_CURRENT_LINE(cg);
00623             break;
00624         }
00625 
00626         /*
00627          * If we can't find out where the error was based on the current
00628          * frame, see if the next frame has a script/pc combo we can use.
00629          */
00630         for (fp = cx->fp; fp; fp = fp->down) {
00631             if (fp->script && fp->pc) {
00632                 report->filename = fp->script->filename;
00633                 report->lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
00634                 break;
00635             }
00636         }
00637     } while (0);
00638 
00639     /*
00640      * If there's a runtime exception type associated with this error
00641      * number, set that as the pending exception.  For errors occuring at
00642      * compile time, this is very likely to be a JSEXN_SYNTAXERR.
00643      *
00644      * If an exception is thrown but not caught, the JSREPORT_EXCEPTION
00645      * flag will be set in report.flags.  Proper behavior for an error
00646      * reporter is to ignore a report with this flag for all but top-level
00647      * compilation errors.  The exception will remain pending, and so long
00648      * as the non-top-level "load", "eval", or "compile" native function
00649      * returns false, the top-level reporter will eventually receive the
00650      * uncaught exception report.
00651      *
00652      * XXX it'd probably be best if there was only one call to this
00653      * function, but there seem to be two error reporter call points.
00654      */
00655     onError = cx->errorReporter;
00656 
00657     /*
00658      * Try to raise an exception only if there isn't one already set --
00659      * otherwise the exception will describe the last compile-time error,
00660      * which is likely spurious.
00661      */
00662     if (!ts || !(ts->flags & TSF_ERROR)) {
00663         if (js_ErrorToException(cx, message, report))
00664             onError = NULL;
00665     }
00666 
00667     /*
00668      * Suppress any compile-time errors that don't occur at the top level.
00669      * This may still fail, as interplevel may be zero in contexts where we
00670      * don't really want to call the error reporter, as when js is called
00671      * by other code which could catch the error.
00672      */
00673     if (cx->interpLevel != 0 && !JSREPORT_IS_WARNING(flags))
00674         onError = NULL;
00675 
00676     if (onError) {
00677         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
00678 
00679         /*
00680          * If debugErrorHook is present then we give it a chance to veto
00681          * sending the error on to the regular error reporter.
00682          */
00683         if (hook && !hook(cx, message, report,
00684                           cx->runtime->debugErrorHookData)) {
00685             onError = NULL;
00686         }
00687     }
00688     if (onError)
00689         (*onError)(cx, message, report);
00690 
00691     if (message)
00692         JS_free(cx, message);
00693     if (report->ucmessage)
00694         JS_free(cx, (void *)report->ucmessage);
00695 
00696     JS_POP_TEMP_ROOT(cx, &linetvr);
00697 
00698     if (ts && !JSREPORT_IS_WARNING(flags)) {
00699         /* Set the error flag to suppress spurious reports. */
00700         ts->flags |= TSF_ERROR;
00701     }
00702 
00703     return warning;
00704 }
00705 
00706 JSBool
00707 js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags,
00708                             uintN errorNumber, ...)
00709 {
00710     va_list ap;
00711     JSErrorReport report;
00712     JSBool warning;
00713 
00714     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
00715         return JS_TRUE;
00716 
00717     va_start(ap, errorNumber);
00718     warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber,
00719                                        &report, JS_TRUE, ap);
00720     va_end(ap);
00721 
00722     /*
00723      * We have to do this here because js_ReportCompileErrorNumberUC doesn't
00724      * need to do this.
00725      */
00726     if (report.messageArgs) {
00727         int i = 0;
00728         while (report.messageArgs[i])
00729             JS_free(cx, (void *)report.messageArgs[i++]);
00730         JS_free(cx, (void *)report.messageArgs);
00731     }
00732 
00733     return warning;
00734 }
00735 
00736 JSBool
00737 js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags,
00738                               uintN errorNumber, ...)
00739 {
00740     va_list ap;
00741     JSErrorReport report;
00742     JSBool warning;
00743 
00744     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
00745         return JS_TRUE;
00746 
00747     va_start(ap, errorNumber);
00748     warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber,
00749                                        &report, JS_FALSE, ap);
00750     va_end(ap);
00751 
00752     if (report.messageArgs)
00753         JS_free(cx, (void *)report.messageArgs);
00754 
00755     return warning;
00756 }
00757 
00758 static JSBool
00759 GrowStringBuffer(JSStringBuffer *sb, size_t newlength)
00760 {
00761     ptrdiff_t offset;
00762     jschar *bp;
00763 
00764     offset = PTRDIFF(sb->ptr, sb->base, jschar);
00765     JS_ASSERT(offset >= 0);
00766     newlength += offset + 1;
00767     if ((size_t)offset < newlength && newlength < ~(size_t)0 / sizeof(jschar))
00768         bp = realloc(sb->base, newlength * sizeof(jschar));
00769     else
00770         bp = NULL;
00771     if (!bp) {
00772         free(sb->base);
00773         sb->base = STRING_BUFFER_ERROR_BASE;
00774         return JS_FALSE;
00775     }
00776     sb->base = bp;
00777     sb->ptr = bp + offset;
00778     sb->limit = bp + newlength - 1;
00779     return JS_TRUE;
00780 }
00781 
00782 static void
00783 FreeStringBuffer(JSStringBuffer *sb)
00784 {
00785     JS_ASSERT(STRING_BUFFER_OK(sb));
00786     if (sb->base)
00787         free(sb->base);
00788 }
00789 
00790 void
00791 js_InitStringBuffer(JSStringBuffer *sb)
00792 {
00793     sb->base = sb->limit = sb->ptr = NULL;
00794     sb->data = NULL;
00795     sb->grow = GrowStringBuffer;
00796     sb->free = FreeStringBuffer;
00797 }
00798 
00799 void
00800 js_FinishStringBuffer(JSStringBuffer *sb)
00801 {
00802     sb->free(sb);
00803 }
00804 
00805 #define ENSURE_STRING_BUFFER(sb,n) \
00806     ((sb)->ptr + (n) <= (sb)->limit || sb->grow(sb, n))
00807 
00808 static void
00809 FastAppendChar(JSStringBuffer *sb, jschar c)
00810 {
00811     if (!STRING_BUFFER_OK(sb))
00812         return;
00813     if (!ENSURE_STRING_BUFFER(sb, 1))
00814         return;
00815     *sb->ptr++ = c;
00816 }
00817 
00818 void
00819 js_AppendChar(JSStringBuffer *sb, jschar c)
00820 {
00821     jschar *bp;
00822 
00823     if (!STRING_BUFFER_OK(sb))
00824         return;
00825     if (!ENSURE_STRING_BUFFER(sb, 1))
00826         return;
00827     bp = sb->ptr;
00828     *bp++ = c;
00829     *bp = 0;
00830     sb->ptr = bp;
00831 }
00832 
00833 #if JS_HAS_XML_SUPPORT
00834 
00835 void
00836 js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count)
00837 {
00838     jschar *bp;
00839 
00840     if (!STRING_BUFFER_OK(sb) || count == 0)
00841         return;
00842     if (!ENSURE_STRING_BUFFER(sb, count))
00843         return;
00844     for (bp = sb->ptr; count; --count)
00845         *bp++ = c;
00846     *bp = 0;
00847     sb->ptr = bp;
00848 }
00849 
00850 void
00851 js_AppendCString(JSStringBuffer *sb, const char *asciiz)
00852 {
00853     size_t length;
00854     jschar *bp;
00855 
00856     if (!STRING_BUFFER_OK(sb) || *asciiz == '\0')
00857         return;
00858     length = strlen(asciiz);
00859     if (!ENSURE_STRING_BUFFER(sb, length))
00860         return;
00861     for (bp = sb->ptr; length; --length)
00862         *bp++ = (jschar) *asciiz++;
00863     *bp = 0;
00864     sb->ptr = bp;
00865 }
00866 
00867 void
00868 js_AppendJSString(JSStringBuffer *sb, JSString *str)
00869 {
00870     size_t length;
00871     jschar *bp;
00872 
00873     if (!STRING_BUFFER_OK(sb))
00874         return;
00875     length = JSSTRING_LENGTH(str);
00876     if (length == 0 || !ENSURE_STRING_BUFFER(sb, length))
00877         return;
00878     bp = sb->ptr;
00879     js_strncpy(bp, JSSTRING_CHARS(str), length);
00880     bp += length;
00881     *bp = 0;
00882     sb->ptr = bp;
00883 }
00884 
00885 static JSBool
00886 GetXMLEntity(JSContext *cx, JSTokenStream *ts)
00887 {
00888     ptrdiff_t offset, length, i;
00889     int32 c, d;
00890     JSBool ispair;
00891     jschar *bp, digit;
00892     char *bytes;
00893     JSErrNum msg;
00894 
00895     /* Put the entity, including the '&' already scanned, in ts->tokenbuf. */
00896     offset = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar);
00897     FastAppendChar(&ts->tokenbuf, '&');
00898     while ((c = GetChar(ts)) != ';') {
00899         if (c == EOF || c == '\n') {
00900             js_ReportCompileErrorNumber(cx, ts,
00901                                         JSREPORT_TS | JSREPORT_ERROR,
00902                                         JSMSG_END_OF_XML_ENTITY);
00903             return JS_FALSE;
00904         }
00905         FastAppendChar(&ts->tokenbuf, (jschar) c);
00906     }
00907 
00908     /* Let length be the number of jschars after the '&', including the ';'. */
00909     length = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) - offset;
00910     bp = ts->tokenbuf.base + offset;
00911     c = d = 0;
00912     ispair = JS_FALSE;
00913     if (length > 2 && bp[1] == '#') {
00914         /* Match a well-formed XML Character Reference. */
00915         i = 2;
00916         if (length > 3 && JS_TOLOWER(bp[i]) == 'x') {
00917             if (length > 9)     /* at most 6 hex digits allowed */
00918                 goto badncr;
00919             while (++i < length) {
00920                 digit = bp[i];
00921                 if (!JS7_ISHEX(digit))
00922                     goto badncr;
00923                 c = (c << 4) + JS7_UNHEX(digit);
00924             }
00925         } else {
00926             while (i < length) {
00927                 digit = bp[i++];
00928                 if (!JS7_ISDEC(digit))
00929                     goto badncr;
00930                 c = (c * 10) + JS7_UNDEC(digit);
00931                 if (c < 0)
00932                     goto badncr;
00933             }
00934         }
00935 
00936         if (0x10000 <= c && c <= 0x10FFFF) {
00937             /* Form a surrogate pair (c, d) -- c is the high surrogate. */
00938             d = 0xDC00 + (c & 0x3FF);
00939             c = 0xD7C0 + (c >> 10);
00940             ispair = JS_TRUE;
00941         } else {
00942             /* Enforce the http://www.w3.org/TR/REC-xml/#wf-Legalchar WFC. */
00943             if (c != 0x9 && c != 0xA && c != 0xD &&
00944                 !(0x20 <= c && c <= 0xD7FF) &&
00945                 !(0xE000 <= c && c <= 0xFFFD)) {
00946                 goto badncr;
00947             }
00948         }
00949     } else {
00950         /* Try to match one of the five XML 1.0 predefined entities. */
00951         switch (length) {
00952           case 3:
00953             if (bp[2] == 't') {
00954                 if (bp[1] == 'l')
00955                     c = '<';
00956                 else if (bp[1] == 'g')
00957                     c = '>';
00958             }
00959             break;
00960           case 4:
00961             if (bp[1] == 'a' && bp[2] == 'm' && bp[3] == 'p')
00962                 c = '&';
00963             break;
00964           case 5:
00965             if (bp[3] == 'o') {
00966                 if (bp[1] == 'a' && bp[2] == 'p' && bp[4] == 's')
00967                     c = '\'';
00968                 else if (bp[1] == 'q' && bp[2] == 'u' && bp[4] == 't')
00969                     c = '"';
00970             }
00971             break;
00972         }
00973         if (c == 0) {
00974             msg = JSMSG_UNKNOWN_XML_ENTITY;
00975             goto bad;
00976         }
00977     }
00978 
00979     /* If we matched, retract ts->tokenbuf and store the entity's value. */
00980     *bp++ = (jschar) c;
00981     if (ispair)
00982         *bp++ = (jschar) d;
00983     *bp = 0;
00984     ts->tokenbuf.ptr = bp;
00985     return JS_TRUE;
00986 
00987 badncr:
00988     msg = JSMSG_BAD_XML_NCR;
00989 bad:
00990     /* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */
00991     bytes = js_DeflateString(cx, bp + 1,
00992                              PTRDIFF(ts->tokenbuf.ptr, bp, jschar) - 1);
00993     if (bytes) {
00994         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
00995                                     msg, bytes);
00996         JS_free(cx, bytes);
00997     }
00998     return JS_FALSE;
00999 }
01000 
01001 #endif /* JS_HAS_XML_SUPPORT */
01002 
01003 JSTokenType
01004 js_PeekToken(JSContext *cx, JSTokenStream *ts)
01005 {
01006     JSTokenType tt;
01007 
01008     if (ts->lookahead != 0) {
01009         tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type;
01010     } else {
01011         tt = js_GetToken(cx, ts);
01012         js_UngetToken(ts);
01013     }
01014     return tt;
01015 }
01016 
01017 JSTokenType
01018 js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)
01019 {
01020     JSTokenType tt;
01021 
01022     if (!ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos))
01023         return TOK_EOL;
01024     ts->flags |= TSF_NEWLINES;
01025     tt = js_PeekToken(cx, ts);
01026     ts->flags &= ~TSF_NEWLINES;
01027     return tt;
01028 }
01029 
01030 /*
01031  * We have encountered a '\': check for a Unicode escape sequence after it,
01032  * returning the character code value if we found a Unicode escape sequence.
01033  * Otherwise, non-destructively return the original '\'.
01034  */
01035 static int32
01036 GetUnicodeEscape(JSTokenStream *ts)
01037 {
01038     jschar cp[5];
01039     int32 c;
01040 
01041     if (PeekChars(ts, 5, cp) && cp[0] == 'u' &&
01042         JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&
01043         JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4]))
01044     {
01045         c = (((((JS7_UNHEX(cp[1]) << 4)
01046                 + JS7_UNHEX(cp[2])) << 4)
01047               + JS7_UNHEX(cp[3])) << 4)
01048             + JS7_UNHEX(cp[4]);
01049         SkipChars(ts, 5);
01050         return c;
01051     }
01052     return '\\';
01053 }
01054 
01055 static JSToken *
01056 NewToken(JSTokenStream *ts, ptrdiff_t adjust)
01057 {
01058     JSToken *tp;
01059 
01060     ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
01061     tp = &CURRENT_TOKEN(ts);
01062     tp->ptr = ts->linebuf.ptr + adjust;
01063     tp->pos.begin.index = ts->linepos +
01064                           PTRDIFF(tp->ptr, ts->linebuf.base, jschar) -
01065                           ts->ungetpos;
01066     tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno;
01067     return tp;
01068 }
01069 
01070 static JS_INLINE JSBool
01071 ScanAsSpace(jschar c)
01072 {
01073     /* Treat little- and big-endian BOMs as whitespace for compatibility. */
01074     if (JS_ISSPACE(c) || c == 0xfffe || c == 0xfeff)
01075         return JS_TRUE;
01076     return JS_FALSE;
01077 }
01078 
01079 JSTokenType
01080 js_GetToken(JSContext *cx, JSTokenStream *ts)
01081 {
01082     JSTokenType tt;
01083     int32 c, qc;
01084     JSToken *tp;
01085     JSAtom *atom;
01086     JSBool hadUnicodeEscape;
01087     const struct keyword *kw;
01088 
01089 #define INIT_TOKENBUF()     (ts->tokenbuf.ptr = ts->tokenbuf.base)
01090 #define TOKENBUF_LENGTH()   PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar)
01091 #define TOKENBUF_OK()       STRING_BUFFER_OK(&ts->tokenbuf)
01092 #define TOKENBUF_TO_ATOM()  (TOKENBUF_OK()                                    \
01093                              ? js_AtomizeChars(cx,                            \
01094                                                TOKENBUF_BASE(),               \
01095                                                TOKENBUF_LENGTH(),             \
01096                                                0)                             \
01097                              : NULL)
01098 #define ADD_TO_TOKENBUF(c)  FastAppendChar(&ts->tokenbuf, (jschar) (c))
01099 
01100 /* The following 4 macros should only be used when TOKENBUF_OK() is true. */
01101 #define TOKENBUF_BASE()     (ts->tokenbuf.base)
01102 #define TOKENBUF_CHAR(i)    (ts->tokenbuf.base[i])
01103 #define TRIM_TOKENBUF(i)    (ts->tokenbuf.ptr = ts->tokenbuf.base + i)
01104 #define NUL_TERM_TOKENBUF() (*ts->tokenbuf.ptr = 0)
01105 
01106     /* Check for a pushed-back token resulting from mismatching lookahead. */
01107     while (ts->lookahead != 0) {
01108         JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE));
01109         ts->lookahead--;
01110         ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
01111         tt = CURRENT_TOKEN(ts).type;
01112         if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))
01113             return tt;
01114     }
01115 
01116     /* If there was a fatal error, keep returning TOK_ERROR. */
01117     if (ts->flags & TSF_ERROR)
01118         return TOK_ERROR;
01119 
01120 #if JS_HAS_XML_SUPPORT
01121     if (ts->flags & TSF_XMLTEXTMODE) {
01122         tt = TOK_XMLSPACE;      /* veto if non-space, return TOK_XMLTEXT */
01123         tp = NewToken(ts, 0);
01124         INIT_TOKENBUF();
01125         qc = (ts->flags & TSF_XMLONLYMODE) ? '<' : '{';
01126 
01127         while ((c = GetChar(ts)) != qc && c != '<' && c != EOF) {
01128             if (c == '&' && qc == '<') {
01129                 if (!GetXMLEntity(cx, ts))
01130                     goto error;
01131                 tt = TOK_XMLTEXT;
01132                 continue;
01133             }
01134 
01135             if (!JS_ISXMLSPACE(c))
01136                 tt = TOK_XMLTEXT;
01137             ADD_TO_TOKENBUF(c);
01138         }
01139         UngetChar(ts, c);
01140 
01141         if (TOKENBUF_LENGTH() == 0) {
01142             atom = NULL;
01143         } else {
01144             atom = TOKENBUF_TO_ATOM();
01145             if (!atom)
01146                 goto error;
01147         }
01148         tp->pos.end.lineno = (uint16)ts->lineno;
01149         tp->t_op = JSOP_STRING;
01150         tp->t_atom = atom;
01151         goto out;
01152     }
01153 
01154     if (ts->flags & TSF_XMLTAGMODE) {
01155         tp = NewToken(ts, 0);
01156         c = GetChar(ts);
01157         if (JS_ISXMLSPACE(c)) {
01158             do {
01159                 c = GetChar(ts);
01160             } while (JS_ISXMLSPACE(c));
01161             UngetChar(ts, c);
01162             tt = TOK_XMLSPACE;
01163             goto out;
01164         }
01165 
01166         if (c == EOF) {
01167             tt = TOK_EOF;
01168             goto out;
01169         }
01170 
01171         INIT_TOKENBUF();
01172         if (JS_ISXMLNSSTART(c)) {
01173             JSBool sawColon = JS_FALSE;
01174 
01175             ADD_TO_TOKENBUF(c);
01176             while ((c = GetChar(ts)) != EOF && JS_ISXMLNAME(c)) {
01177                 if (c == ':') {
01178                     int nextc;
01179 
01180                     if (sawColon ||
01181                         (nextc = PeekChar(ts),
01182                          ((ts->flags & TSF_XMLONLYMODE) || nextc != '{') &&
01183                          !JS_ISXMLNAME(nextc))) {
01184                         js_ReportCompileErrorNumber(cx, ts,
01185                                                     JSREPORT_TS |
01186                                                     JSREPORT_ERROR,
01187                                                     JSMSG_BAD_XML_QNAME);
01188                         goto error;
01189                     }
01190                     sawColon = JS_TRUE;
01191                 }
01192 
01193                 ADD_TO_TOKENBUF(c);
01194             }
01195 
01196             UngetChar(ts, c);
01197             atom = TOKENBUF_TO_ATOM();
01198             if (!atom)
01199                 goto error;
01200             tp->t_op = JSOP_STRING;
01201             tp->t_atom = atom;
01202             tt = TOK_XMLNAME;
01203             goto out;
01204         }
01205 
01206         switch (c) {
01207           case '{':
01208             if (ts->flags & TSF_XMLONLYMODE)
01209                 goto bad_xml_char;
01210             tt = TOK_LC;
01211             goto out;
01212 
01213           case '=':
01214             tt = TOK_ASSIGN;
01215             goto out;
01216 
01217           case '"':
01218           case '\'':
01219             qc = c;
01220             while ((c = GetChar(ts)) != qc) {
01221                 if (c == EOF) {
01222                     js_ReportCompileErrorNumber(cx, ts,
01223                                                 JSREPORT_TS | JSREPORT_ERROR,
01224                                                 JSMSG_UNTERMINATED_STRING);
01225                     goto error;
01226                 }
01227 
01228                 /*
01229                  * XML attribute values are double-quoted when pretty-printed,
01230                  * so escape " if it is expressed directly in a single-quoted
01231                  * attribute value.
01232                  */
01233                 if (c == '"' && !(ts->flags & TSF_XMLONLYMODE)) {
01234                     JS_ASSERT(qc == '\'');
01235                     js_AppendCString(&ts->tokenbuf, js_quot_entity_str);
01236                     continue;
01237                 }
01238 
01239                 if (c == '&' && (ts->flags & TSF_XMLONLYMODE)) {
01240                     if (!GetXMLEntity(cx, ts))
01241                         goto error;
01242                     continue;
01243                 }
01244 
01245                 ADD_TO_TOKENBUF(c);
01246             }
01247             atom = TOKENBUF_TO_ATOM();
01248             if (!atom)
01249                 goto error;
01250             tp->pos.end.lineno = (uint16)ts->lineno;
01251             tp->t_op = JSOP_STRING;
01252             tp->t_atom = atom;
01253             tt = TOK_XMLATTR;
01254             goto out;
01255 
01256           case '>':
01257             tt = TOK_XMLTAGC;
01258             goto out;
01259 
01260           case '/':
01261             if (MatchChar(ts, '>')) {
01262                 tt = TOK_XMLPTAGC;
01263                 goto out;
01264             }
01265             /* FALL THROUGH */
01266 
01267           bad_xml_char:
01268           default:
01269             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
01270                                         JSMSG_BAD_XML_CHARACTER);
01271             goto error;
01272         }
01273         /* NOTREACHED */
01274     }
01275 #endif /* JS_HAS_XML_SUPPORT */
01276 
01277 retry:
01278     do {
01279         c = GetChar(ts);
01280         if (c == '\n') {
01281             ts->flags &= ~TSF_DIRTYLINE;
01282             if (ts->flags & TSF_NEWLINES)
01283                 break;
01284         }
01285     } while (ScanAsSpace(c));
01286 
01287     tp = NewToken(ts, -1);
01288     if (c == EOF) {
01289         tt = TOK_EOF;
01290         goto out;
01291     }
01292 
01293     hadUnicodeEscape = JS_FALSE;
01294     if (JS_ISIDSTART(c) ||
01295         (c == '\\' &&
01296          (c = GetUnicodeEscape(ts),
01297           hadUnicodeEscape = JS_ISIDSTART(c)))) {
01298         INIT_TOKENBUF();
01299         for (;;) {
01300             ADD_TO_TOKENBUF(c);
01301             c = GetChar(ts);
01302             if (c == '\\') {
01303                 c = GetUnicodeEscape(ts);
01304                 if (!JS_ISIDENT(c))
01305                     break;
01306                 hadUnicodeEscape = JS_TRUE;
01307             } else {
01308                 if (!JS_ISIDENT(c))
01309                     break;
01310             }
01311         }
01312         UngetChar(ts, c);
01313 
01314         /*
01315          * Check for keywords unless we saw Unicode escape or parser asks
01316          * to ignore keywords.
01317          */
01318         if (!hadUnicodeEscape &&
01319             !(ts->flags & TSF_KEYWORD_IS_NAME) &&
01320             TOKENBUF_OK() &&
01321             (kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) {
01322             if (kw->tokentype == TOK_RESERVED) {
01323                 if (!js_ReportCompileErrorNumber(cx, ts,
01324                                                  JSREPORT_TS |
01325                                                  JSREPORT_WARNING |
01326                                                  JSREPORT_STRICT,
01327                                                  JSMSG_RESERVED_ID,
01328                                                  kw->chars)) {
01329                     goto error;
01330                 }
01331             } else if (kw->version <= JSVERSION_NUMBER(cx)) {
01332                 tt = kw->tokentype;
01333                 tp->t_op = (JSOp) kw->op;
01334                 goto out;
01335             }
01336         }
01337 
01338         atom = TOKENBUF_TO_ATOM();
01339         if (!atom)
01340             goto error;
01341         tp->t_op = JSOP_NAME;
01342         tp->t_atom = atom;
01343         tt = TOK_NAME;
01344         goto out;
01345     }
01346 
01347     if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
01348         jsint radix;
01349         const jschar *endptr;
01350         jsdouble dval;
01351 
01352         radix = 10;
01353         INIT_TOKENBUF();
01354 
01355         if (c == '0') {
01356             ADD_TO_TOKENBUF(c);
01357             c = GetChar(ts);
01358             if (JS_TOLOWER(c) == 'x') {
01359                 ADD_TO_TOKENBUF(c);
01360                 c = GetChar(ts);
01361                 radix = 16;
01362             } else if (JS7_ISDEC(c)) {
01363                 radix = 8;
01364             }
01365         }
01366 
01367         while (JS7_ISHEX(c)) {
01368             if (radix < 16) {
01369                 if (JS7_ISLET(c))
01370                     break;
01371 
01372                 /*
01373                  * We permit 08 and 09 as decimal numbers, which makes our
01374                  * behaviour a superset of the ECMA numeric grammar.  We might
01375                  * not always be so permissive, so we warn about it.
01376                  */
01377                 if (radix == 8 && c >= '8') {
01378                     if (!js_ReportCompileErrorNumber(cx, ts,
01379                                                      JSREPORT_TS |
01380                                                      JSREPORT_WARNING,
01381                                                      JSMSG_BAD_OCTAL,
01382                                                      c == '8' ? "08" : "09")) {
01383                         goto error;
01384                     }
01385                     radix = 10;
01386                 }
01387             }
01388             ADD_TO_TOKENBUF(c);
01389             c = GetChar(ts);
01390         }
01391 
01392         if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
01393             if (c == '.') {
01394                 do {
01395                     ADD_TO_TOKENBUF(c);
01396                     c = GetChar(ts);
01397                 } while (JS7_ISDEC(c));
01398             }
01399             if (JS_TOLOWER(c) == 'e') {
01400                 ADD_TO_TOKENBUF(c);
01401                 c = GetChar(ts);
01402                 if (c == '+' || c == '-') {
01403                     ADD_TO_TOKENBUF(c);
01404                     c = GetChar(ts);
01405                 }
01406                 if (!JS7_ISDEC(c)) {
01407                     js_ReportCompileErrorNumber(cx, ts,
01408                                                 JSREPORT_TS | JSREPORT_ERROR,
01409                                                 JSMSG_MISSING_EXPONENT);
01410                     goto error;
01411                 }
01412                 do {
01413                     ADD_TO_TOKENBUF(c);
01414                     c = GetChar(ts);
01415                 } while (JS7_ISDEC(c));
01416             }
01417         }
01418 
01419         /* Put back the next char and NUL-terminate tokenbuf for js_strto*. */
01420         UngetChar(ts, c);
01421         ADD_TO_TOKENBUF(0);
01422 
01423         if (!TOKENBUF_OK())
01424             goto error;
01425         if (radix == 10) {
01426             if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) {
01427                 js_ReportCompileErrorNumber(cx, ts,
01428                                             JSREPORT_TS | JSREPORT_ERROR,
01429                                             JSMSG_OUT_OF_MEMORY);
01430                 goto error;
01431             }
01432         } else {
01433             if (!js_strtointeger(cx, TOKENBUF_BASE(), &endptr, radix, &dval)) {
01434                 js_ReportCompileErrorNumber(cx, ts,
01435                                             JSREPORT_TS | JSREPORT_ERROR,
01436                                             JSMSG_OUT_OF_MEMORY);
01437                 goto error;
01438             }
01439         }
01440         tp->t_dval = dval;
01441         tt = TOK_NUMBER;
01442         goto out;
01443     }
01444 
01445     if (c == '"' || c == '\'') {
01446         qc = c;
01447         INIT_TOKENBUF();
01448         while ((c = GetChar(ts)) != qc) {
01449             if (c == '\n' || c == EOF) {
01450                 UngetChar(ts, c);
01451                 js_ReportCompileErrorNumber(cx, ts,
01452                                             JSREPORT_TS | JSREPORT_ERROR,
01453                                             JSMSG_UNTERMINATED_STRING);
01454                 goto error;
01455             }
01456             if (c == '\\') {
01457                 switch (c = GetChar(ts)) {
01458                   case 'b': c = '\b'; break;
01459                   case 'f': c = '\f'; break;
01460                   case 'n': c = '\n'; break;
01461                   case 'r': c = '\r'; break;
01462                   case 't': c = '\t'; break;
01463                   case 'v': c = '\v'; break;
01464 
01465                   default:
01466                     if ('0' <= c && c < '8') {
01467                         int32 val = JS7_UNDEC(c);
01468 
01469                         c = PeekChar(ts);
01470                         if ('0' <= c && c < '8') {
01471                             val = 8 * val + JS7_UNDEC(c);
01472                             GetChar(ts);
01473                             c = PeekChar(ts);
01474                             if ('0' <= c && c < '8') {
01475                                 int32 save = val;
01476                                 val = 8 * val + JS7_UNDEC(c);
01477                                 if (val <= 0377)
01478                                     GetChar(ts);
01479                                 else
01480                                     val = save;
01481                             }
01482                         }
01483 
01484                         c = (jschar)val;
01485                     } else if (c == 'u') {
01486                         jschar cp[4];
01487                         if (PeekChars(ts, 4, cp) &&
01488                             JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
01489                             JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
01490                             c = (((((JS7_UNHEX(cp[0]) << 4)
01491                                     + JS7_UNHEX(cp[1])) << 4)
01492                                   + JS7_UNHEX(cp[2])) << 4)
01493                                 + JS7_UNHEX(cp[3]);
01494                             SkipChars(ts, 4);
01495                         }
01496                     } else if (c == 'x') {
01497                         jschar cp[2];
01498                         if (PeekChars(ts, 2, cp) &&
01499                             JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
01500                             c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
01501                             SkipChars(ts, 2);
01502                         }
01503                     } else if (c == '\n' && JS_VERSION_IS_ECMA(cx)) {
01504                         /* ECMA follows C by removing escaped newlines. */
01505                         continue;
01506                     }
01507                     break;
01508                 }
01509             }
01510             ADD_TO_TOKENBUF(c);
01511         }
01512         atom = TOKENBUF_TO_ATOM();
01513         if (!atom)
01514             goto error;
01515         tp->pos.end.lineno = (uint16)ts->lineno;
01516         tp->t_op = JSOP_STRING;
01517         tp->t_atom = atom;
01518         tt = TOK_STRING;
01519         goto out;
01520     }
01521 
01522     switch (c) {
01523       case '\n': tt = TOK_EOL; goto eol_out;
01524       case ';':  tt = TOK_SEMI; break;
01525       case '[':  tt = TOK_LB; break;
01526       case ']':  tt = TOK_RB; break;
01527       case '{':  tt = TOK_LC; break;
01528       case '}':  tt = TOK_RC; break;
01529       case '(':  tt = TOK_LP; break;
01530       case ')':  tt = TOK_RP; break;
01531       case ',':  tt = TOK_COMMA; break;
01532       case '?':  tt = TOK_HOOK; break;
01533 
01534       case '.':
01535 #if JS_HAS_XML_SUPPORT
01536         if (MatchChar(ts, c))
01537             tt = TOK_DBLDOT;
01538         else
01539 #endif
01540             tt = TOK_DOT;
01541         break;
01542 
01543       case ':':
01544 #if JS_HAS_XML_SUPPORT
01545         if (MatchChar(ts, c)) {
01546             tt = TOK_DBLCOLON;
01547             break;
01548         }
01549 #endif
01550         /*
01551          * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an
01552          * object initializer, likewise for setter.
01553          */
01554         tp->t_op = JSOP_NOP;
01555         tt = TOK_COLON;
01556         break;
01557 
01558       case '|':
01559         if (MatchChar(ts, c)) {
01560             tt = TOK_OR;
01561         } else if (MatchChar(ts, '=')) {
01562             tp->t_op = JSOP_BITOR;
01563             tt = TOK_ASSIGN;
01564         } else {
01565             tt = TOK_BITOR;
01566         }
01567         break;
01568 
01569       case '^':
01570         if (MatchChar(ts, '=')) {
01571             tp->t_op = JSOP_BITXOR;
01572             tt = TOK_ASSIGN;
01573         } else {
01574             tt = TOK_BITXOR;
01575         }
01576         break;
01577 
01578       case '&':
01579         if (MatchChar(ts, c)) {
01580             tt = TOK_AND;
01581         } else if (MatchChar(ts, '=')) {
01582             tp->t_op = JSOP_BITAND;
01583             tt = TOK_ASSIGN;
01584         } else {
01585             tt = TOK_BITAND;
01586         }
01587         break;
01588 
01589       case '=':
01590         if (MatchChar(ts, c)) {
01591             tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq;
01592             tt = TOK_EQOP;
01593         } else {
01594             tp->t_op = JSOP_NOP;
01595             tt = TOK_ASSIGN;
01596         }
01597         break;
01598 
01599       case '!':
01600         if (MatchChar(ts, '=')) {
01601             tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;
01602             tt = TOK_EQOP;
01603         } else {
01604             tp->t_op = JSOP_NOT;
01605             tt = TOK_UNARYOP;
01606         }
01607         break;
01608 
01609 #if JS_HAS_XML_SUPPORT
01610       case '@':
01611         tt = TOK_AT;
01612         break;
01613 #endif
01614 
01615       case '<':
01616 #if JS_HAS_XML_SUPPORT
01617         /*
01618          * After much testing, it's clear that Postel's advice to protocol
01619          * designers ("be liberal in what you accept, and conservative in what
01620          * you send") invites a natural-law repercussion for JS as "protocol":
01621          *
01622          * "If you are liberal in what you accept, others will utterly fail to
01623          *  be conservative in what they send."
01624          *
01625          * Which means you will get <!-- comments to end of line in the middle
01626          * of .js files, and after if conditions whose then statements are on
01627          * the next line, and other wonders.  See at least the following bugs:
01628          * https://bugzilla.mozilla.org/show_bug.cgi?id=309242
01629          * https://bugzilla.mozilla.org/show_bug.cgi?id=309712
01630          * https://bugzilla.mozilla.org/show_bug.cgi?id=310993
01631          *
01632          * So without JSOPTION_XML, we never scan an XML comment or CDATA
01633          * literal.  We always scan <! as the start of an HTML comment hack
01634          * to end of line, used since Netscape 2 to hide script tag content
01635          * from script-unaware browsers.
01636          */
01637         if ((ts->flags & TSF_OPERAND) &&
01638             (JS_HAS_XML_OPTION(cx) || PeekChar(ts) != '!')) {
01639             /* Check for XML comment or CDATA section. */
01640             if (MatchChar(ts, '!')) {
01641                 INIT_TOKENBUF();
01642 
01643                 /* Scan XML comment. */
01644                 if (MatchChar(ts, '-')) {
01645                     if (!MatchChar(ts, '-'))
01646                         goto bad_xml_markup;
01647                     while ((c = GetChar(ts)) != '-' || !MatchChar(ts, '-')) {
01648                         if (c == EOF)
01649                             goto bad_xml_markup;
01650                         ADD_TO_TOKENBUF(c);
01651                     }
01652                     tt = TOK_XMLCOMMENT;
01653                     tp->t_op = JSOP_XMLCOMMENT;
01654                     goto finish_xml_markup;
01655                 }
01656 
01657                 /* Scan CDATA section. */
01658                 if (MatchChar(ts, '[')) {
01659                     jschar cp[6];
01660                     if (PeekChars(ts, 6, cp) &&
01661                         cp[0] == 'C' &&
01662                         cp[1] == 'D' &&
01663                         cp[2] == 'A' &&
01664                         cp[3] == 'T' &&
01665                         cp[4] == 'A' &&
01666                         cp[5] == '[') {
01667                         SkipChars(ts, 6);
01668                         while ((c = GetChar(ts)) != ']' ||
01669                                !PeekChars(ts, 2, cp) ||
01670                                cp[0] != ']' ||
01671                                cp[1] != '>') {
01672                             if (c == EOF)
01673                                 goto bad_xml_markup;
01674                             ADD_TO_TOKENBUF(c);
01675                         }
01676                         GetChar(ts);            /* discard ] but not > */
01677                         tt = TOK_XMLCDATA;
01678                         tp->t_op = JSOP_XMLCDATA;
01679                         goto finish_xml_markup;
01680                     }
01681                     goto bad_xml_markup;
01682                 }
01683             }
01684 
01685             /* Check for processing instruction. */
01686             if (MatchChar(ts, '?')) {
01687                 JSBool inTarget = JS_TRUE;
01688                 size_t targetLength = 0;
01689                 ptrdiff_t contentIndex = -1;
01690 
01691                 INIT_TOKENBUF();
01692                 while ((c = GetChar(ts)) != '?' || PeekChar(ts) != '>') {
01693                     if (c == EOF)
01694                         goto bad_xml_markup;
01695                     if (inTarget) {
01696                         if (JS_ISXMLSPACE(c)) {
01697                             if (TOKENBUF_LENGTH() == 0)
01698                                 goto bad_xml_markup;
01699                             inTarget = JS_FALSE;
01700                         } else {
01701                             if (!((TOKENBUF_LENGTH() == 0)
01702                                   ? JS_ISXMLNSSTART(c)
01703                                   : JS_ISXMLNS(c))) {
01704                                 goto bad_xml_markup;
01705                             }
01706                             ++targetLength;
01707                         }
01708                     } else {
01709                         if (contentIndex < 0 && !JS_ISXMLSPACE(c))
01710                             contentIndex = TOKENBUF_LENGTH();
01711                     }
01712                     ADD_TO_TOKENBUF(c);
01713                 }
01714                 if (targetLength == 0)
01715                     goto bad_xml_markup;
01716                 if (!TOKENBUF_OK())
01717                     goto error;
01718                 if (contentIndex < 0) {
01719                     atom = cx->runtime->atomState.emptyAtom;
01720                 } else {
01721                     atom = js_AtomizeChars(cx,
01722                                            &TOKENBUF_CHAR(contentIndex),
01723                                            TOKENBUF_LENGTH() - contentIndex,
01724                                            0);
01725                     if (!atom)
01726                         goto error;
01727                 }
01728                 TRIM_TOKENBUF(targetLength);
01729                 tp->t_atom2 = atom;
01730                 tt = TOK_XMLPI;
01731 
01732         finish_xml_markup:
01733                 if (!MatchChar(ts, '>'))
01734                     goto bad_xml_markup;
01735                 atom = TOKENBUF_TO_ATOM();
01736                 if (!atom)
01737                     goto error;
01738                 tp->t_atom = atom;
01739                 tp->pos.end.lineno = (uint16)ts->lineno;
01740                 goto out;
01741             }
01742 
01743             /* An XML start-of-tag character. */
01744             tt = MatchChar(ts, '/') ? TOK_XMLETAGO : TOK_XMLSTAGO;
01745             goto out;
01746 
01747         bad_xml_markup:
01748             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
01749                                         JSMSG_BAD_XML_MARKUP);
01750             goto error;
01751         }
01752 #endif /* JS_HAS_XML_SUPPORT */
01753 
01754         /* NB: treat HTML begin-comment as comment-till-end-of-line */
01755         if (MatchChar(ts, '!')) {
01756             if (MatchChar(ts, '-')) {
01757                 if (MatchChar(ts, '-')) {
01758                     ts->flags |= TSF_IN_HTML_COMMENT;
01759                     goto skipline;
01760                 }
01761                 UngetChar(ts, '-');
01762             }
01763             UngetChar(ts, '!');
01764         }
01765         if (MatchChar(ts, c)) {
01766             tp->t_op = JSOP_LSH;
01767             tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
01768         } else {
01769             tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
01770             tt = TOK_RELOP;
01771         }
01772         break;
01773 
01774       case '>':
01775         if (MatchChar(ts, c)) {
01776             tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;
01777             tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
01778         } else {
01779             tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
01780             tt = TOK_RELOP;
01781         }
01782         break;
01783 
01784       case '*':
01785         tp->t_op = JSOP_MUL;
01786         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;
01787         break;
01788 
01789       case '/':
01790         if (MatchChar(ts, '/')) {
01791             /*
01792              * Hack for source filters such as the Mozilla XUL preprocessor:
01793              * "//@line 123\n" sets the number of the *next* line after the
01794              * comment to 123.
01795              */
01796             if (JS_HAS_ATLINE_OPTION(cx)) {
01797                 jschar cp[5];
01798                 uintN i, line, temp;
01799                 char filename[1024];
01800 
01801                 if (PeekChars(ts, 5, cp) &&
01802                     cp[0] == '@' &&
01803                     cp[1] == 'l' &&
01804                     cp[2] == 'i' &&
01805                     cp[3] == 'n' &&
01806                     cp[4] == 'e') {
01807                     SkipChars(ts, 5);
01808                     while ((c = GetChar(ts)) != '\n' && ScanAsSpace(c))
01809                         continue;
01810                     if (JS7_ISDEC(c)) {
01811                         line = JS7_UNDEC(c);
01812                         while ((c = GetChar(ts)) != EOF && JS7_ISDEC(c)) {
01813                             temp = 10 * line + JS7_UNDEC(c);
01814                             if (temp < line) {
01815                                 /* Ignore overlarge line numbers. */
01816                                 goto skipline;
01817                             }
01818                             line = temp;
01819                         }
01820                         while (c != '\n' && ScanAsSpace(c))
01821                             c = GetChar(ts);
01822                         i = 0;
01823                         if (c == '"') {
01824                             while ((c = GetChar(ts)) != EOF && c != '"') {
01825                                 if (c == '\n') {
01826                                     UngetChar(ts, c);
01827                                     goto skipline;
01828                                 }
01829                                 if ((c >> 8) != 0 || i >= sizeof filename - 1)
01830                                     goto skipline;
01831                                 filename[i++] = (char) c;
01832                             }
01833                             if (c == '"') {
01834                                 while ((c = GetChar(ts)) != '\n' &&
01835                                        ScanAsSpace(c)) {
01836                                     continue;
01837                                 }
01838                             }
01839                         }
01840                         filename[i] = '\0';
01841                         if (c == '\n') {
01842                             if (i > 0) {
01843                                 if (ts->flags & TSF_OWNFILENAME)
01844                                     JS_free(cx, (void *) ts->filename);
01845                                 ts->filename = JS_strdup(cx, filename);
01846                                 if (!ts->filename)
01847                                     goto error;
01848                                 ts->flags |= TSF_OWNFILENAME;
01849                             }
01850                             ts->lineno = line;
01851                         }
01852                     }
01853                     UngetChar(ts, c);
01854                 }
01855             }
01856 
01857 skipline:
01858             /* Optimize line skipping if we are not in an HTML comment. */
01859             if (ts->flags & TSF_IN_HTML_COMMENT) {
01860                 while ((c = GetChar(ts)) != EOF && c != '\n') {
01861                     if (c == '-' && MatchChar(ts, '-') && MatchChar(ts, '>'))
01862                         ts->flags &= ~TSF_IN_HTML_COMMENT;
01863                 }
01864             } else {
01865                 while ((c = GetChar(ts)) != EOF && c != '\n')
01866                     continue;
01867             }
01868             UngetChar(ts, c);
01869             ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
01870             goto retry;
01871         }
01872 
01873         if (MatchChar(ts, '*')) {
01874             while ((c = GetChar(ts)) != EOF &&
01875                    !(c == '*' && MatchChar(ts, '/'))) {
01876                 /* Ignore all characters until comment close. */
01877             }
01878             if (c == EOF) {
01879                 js_ReportCompileErrorNumber(cx, ts,
01880                                             JSREPORT_TS | JSREPORT_ERROR,
01881                                             JSMSG_UNTERMINATED_COMMENT);
01882                 goto error;
01883             }
01884             ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
01885             goto retry;
01886         }
01887 
01888         if (ts->flags & TSF_OPERAND) {
01889             JSObject *obj;
01890             uintN flags;
01891             JSBool inCharClass = JS_FALSE;
01892 
01893             INIT_TOKENBUF();
01894             for (;;) {
01895                 c = GetChar(ts);
01896                 if (c == '\n' || c == EOF) {
01897                     UngetChar(ts, c);
01898                     js_ReportCompileErrorNumber(cx, ts,
01899                                                 JSREPORT_TS | JSREPORT_ERROR,
01900                                                 JSMSG_UNTERMINATED_REGEXP);
01901                     goto error;
01902                 }
01903                 if (c == '\\') {
01904                     ADD_TO_TOKENBUF(c);
01905                     c = GetChar(ts);
01906                 } else if (c == '[') {
01907                     inCharClass = JS_TRUE;
01908                 } else if (c == ']') {
01909                     inCharClass = JS_FALSE;
01910                 } else if (c == '/' && !inCharClass) {
01911                     /* For compat with IE, allow unescaped / in char classes. */
01912                     break;
01913                 }
01914                 ADD_TO_TOKENBUF(c);
01915             }
01916             for (flags = 0; ; ) {
01917                 if (MatchChar(ts, 'g'))
01918                     flags |= JSREG_GLOB;
01919                 else if (MatchChar(ts, 'i'))
01920                     flags |= JSREG_FOLD;
01921                 else if (MatchChar(ts, 'm'))
01922                     flags |= JSREG_MULTILINE;
01923                 else
01924                     break;
01925             }
01926             c = PeekChar(ts);
01927             if (JS7_ISLET(c)) {
01928                 tp->ptr = ts->linebuf.ptr - 1;
01929                 js_ReportCompileErrorNumber(cx, ts,
01930                                             JSREPORT_TS | JSREPORT_ERROR,
01931                                             JSMSG_BAD_REGEXP_FLAG);
01932                 (void) GetChar(ts);
01933                 goto error;
01934             }
01935             /* XXXbe fix jsregexp.c so it doesn't depend on NUL termination */
01936             if (!TOKENBUF_OK())
01937                 goto error;
01938             NUL_TERM_TOKENBUF();
01939             obj = js_NewRegExpObject(cx, ts,
01940                                      TOKENBUF_BASE(),
01941                                      TOKENBUF_LENGTH(),
01942                                      flags);
01943             if (!obj)
01944                 goto error;
01945             atom = js_AtomizeObject(cx, obj, 0);
01946             if (!atom)
01947                 goto error;
01948 
01949             /*
01950              * If the regexp's script is one-shot, we can avoid the extra
01951              * fork-on-exec costs of JSOP_REGEXP by selecting JSOP_OBJECT.
01952              * Otherwise, to avoid incorrect proto, parent, and lastIndex
01953              * sharing among threads and sequentially across re-execution,
01954              * select JSOP_REGEXP.
01955              */
01956             tp->t_op = (cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))
01957                        ? JSOP_OBJECT
01958                        : JSOP_REGEXP;
01959             tp->t_atom = atom;
01960             tt = TOK_OBJECT;
01961             break;
01962         }
01963 
01964         tp->t_op = JSOP_DIV;
01965         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
01966         break;
01967 
01968       case '%':
01969         tp->t_op = JSOP_MOD;
01970         tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
01971         break;
01972 
01973       case '~':
01974         tp->t_op = JSOP_BITNOT;
01975         tt = TOK_UNARYOP;
01976         break;
01977 
01978       case '+':
01979         if (MatchChar(ts, '=')) {
01980             tp->t_op = JSOP_ADD;
01981             tt = TOK_ASSIGN;
01982         } else if (MatchChar(ts, c)) {
01983             tt = TOK_INC;
01984         } else {
01985             tp->t_op = JSOP_POS;
01986             tt = TOK_PLUS;
01987         }
01988         break;
01989 
01990       case '-':
01991         if (MatchChar(ts, '=')) {
01992             tp->t_op = JSOP_SUB;
01993             tt = TOK_ASSIGN;
01994         } else if (MatchChar(ts, c)) {
01995             if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE)) {
01996                 ts->flags &= ~TSF_IN_HTML_COMMENT;
01997                 goto skipline;
01998             }
01999             tt = TOK_DEC;
02000         } else {
02001             tp->t_op = JSOP_NEG;
02002             tt = TOK_MINUS;
02003         }
02004         break;
02005 
02006 #if JS_HAS_SHARP_VARS
02007       case '#':
02008       {
02009         uint32 n;
02010 
02011         c = GetChar(ts);
02012         if (!JS7_ISDEC(c)) {
02013             UngetChar(ts, c);
02014             goto badchar;
02015         }
02016         n = (uint32)JS7_UNDEC(c);
02017         for (;;) {
02018             c = GetChar(ts);
02019             if (!JS7_ISDEC(c))
02020                 break;
02021             n = 10 * n + JS7_UNDEC(c);
02022             if (n >= UINT16_LIMIT) {
02023                 js_ReportCompileErrorNumber(cx, ts,
02024                                             JSREPORT_TS | JSREPORT_ERROR,
02025                                             JSMSG_SHARPVAR_TOO_BIG);
02026                 goto error;
02027             }
02028         }
02029         tp->t_dval = (jsdouble) n;
02030         if (JS_HAS_STRICT_OPTION(cx) &&
02031             (c == '=' || c == '#')) {
02032             char buf[20];
02033             JS_snprintf(buf, sizeof buf, "#%u%c", n, c);
02034             if (!js_ReportCompileErrorNumber(cx, ts,
02035                                              JSREPORT_TS |
02036                                              JSREPORT_WARNING |
02037                                              JSREPORT_STRICT,
02038                                              JSMSG_DEPRECATED_USAGE,
02039                                              buf)) {
02040                 goto error;
02041             }
02042         }
02043         if (c == '=')
02044             tt = TOK_DEFSHARP;
02045         else if (c == '#')
02046             tt = TOK_USESHARP;
02047         else
02048             goto badchar;
02049         break;
02050       }
02051 #endif /* JS_HAS_SHARP_VARS */
02052 
02053 #if JS_HAS_SHARP_VARS || JS_HAS_XML_SUPPORT
02054       badchar:
02055 #endif
02056 
02057       default:
02058         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
02059                                     JSMSG_ILLEGAL_CHARACTER);
02060         goto error;
02061     }
02062 
02063 out:
02064     JS_ASSERT(tt != TOK_EOL);
02065     ts->flags |= TSF_DIRTYLINE;
02066 
02067 eol_out:
02068     if (!STRING_BUFFER_OK(&ts->tokenbuf))
02069         tt = TOK_ERROR;
02070     JS_ASSERT(tt < TOK_LIMIT);
02071     tp->pos.end.index = ts->linepos +
02072                         PTRDIFF(ts->linebuf.ptr, ts->linebuf.base, jschar) -
02073                         ts->ungetpos;
02074     tp->type = tt;
02075     return tt;
02076 
02077 error:
02078     tt = TOK_ERROR;
02079     ts->flags |= TSF_ERROR;
02080     goto out;
02081 
02082 #undef INIT_TOKENBUF
02083 #undef TOKENBUF_LENGTH
02084 #undef TOKENBUF_OK
02085 #undef TOKENBUF_TO_ATOM
02086 #undef ADD_TO_TOKENBUF
02087 #undef TOKENBUF_BASE
02088 #undef TOKENBUF_CHAR
02089 #undef TRIM_TOKENBUF
02090 #undef NUL_TERM_TOKENBUF
02091 }
02092 
02093 void
02094 js_UngetToken(JSTokenStream *ts)
02095 {
02096     JS_ASSERT(ts->lookahead < NTOKENS_MASK);
02097     ts->lookahead++;
02098     ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
02099 }
02100 
02101 JSBool
02102 js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
02103 {
02104     if (js_GetToken(cx, ts) == tt)
02105         return JS_TRUE;
02106     js_UngetToken(ts);
02107     return JS_FALSE;
02108 }