~vaifrax/inkscape/bugfix170049

« back to all changes in this revision

Viewing changes to src/dom/js/jsscan.c

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * ***** BEGIN LICENSE BLOCK *****
 
4
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
5
 *
 
6
 * The contents of this file are subject to the Mozilla Public License Version
 
7
 * 1.1 (the "License"); you may not use this file except in compliance with
 
8
 * the License. You may obtain a copy of the License at
 
9
 * http://www.mozilla.org/MPL/
 
10
 *
 
11
 * Software distributed under the License is distributed on an "AS IS" basis,
 
12
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
13
 * for the specific language governing rights and limitations under the
 
14
 * License.
 
15
 *
 
16
 * The Original Code is Mozilla Communicator client code, released
 
17
 * March 31, 1998.
 
18
 *
 
19
 * The Initial Developer of the Original Code is
 
20
 * Netscape Communications Corporation.
 
21
 * Portions created by the Initial Developer are Copyright (C) 1998
 
22
 * the Initial Developer. All Rights Reserved.
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * Alternatively, the contents of this file may be used under the terms of
 
27
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 
28
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
29
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
30
 * of those above. If you wish to allow use of your version of this file only
 
31
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
32
 * use your version of this file under the terms of the MPL, indicate your
 
33
 * decision by deleting the provisions above and replace them with the notice
 
34
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
35
 * the provisions above, a recipient may use your version of this file under
 
36
 * the terms of any one of the MPL, the GPL or the LGPL.
 
37
 *
 
38
 * ***** END LICENSE BLOCK ***** */
 
39
 
 
40
/*
 
41
 * JS lexical scanner.
 
42
 */
 
43
#include "jsstddef.h"
 
44
#include <stdio.h>      /* first to avoid trouble on some systems */
 
45
#include <errno.h>
 
46
#include <limits.h>
 
47
#include <math.h>
 
48
#ifdef HAVE_MEMORY_H
 
49
#include <memory.h>
 
50
#endif
 
51
#include <stdarg.h>
 
52
#include <stdlib.h>
 
53
#include <string.h>
 
54
#include "jstypes.h"
 
55
#include "jsarena.h" /* Added by JSIFY */
 
56
#include "jsutil.h" /* Added by JSIFY */
 
57
#include "jsdtoa.h"
 
58
#include "jsprf.h"
 
59
#include "jsapi.h"
 
60
#include "jsatom.h"
 
61
#include "jscntxt.h"
 
62
#include "jsconfig.h"
 
63
#include "jsemit.h"
 
64
#include "jsexn.h"
 
65
#include "jsnum.h"
 
66
#include "jsopcode.h"
 
67
#include "jsregexp.h"
 
68
#include "jsscan.h"
 
69
 
 
70
#define RESERVE_JAVA_KEYWORDS
 
71
#define RESERVE_ECMA_KEYWORDS
 
72
 
 
73
static struct keyword {
 
74
    const char  *name;
 
75
    JSTokenType tokentype;      /* JSTokenType */
 
76
    JSOp        op;             /* JSOp */
 
77
    JSVersion   version;        /* JSVersion */
 
78
} keywords[] = {
 
79
    {"break",           TOK_BREAK,              JSOP_NOP,   JSVERSION_DEFAULT},
 
80
    {"case",            TOK_CASE,               JSOP_NOP,   JSVERSION_DEFAULT},
 
81
    {"continue",        TOK_CONTINUE,           JSOP_NOP,   JSVERSION_DEFAULT},
 
82
    {"default",         TOK_DEFAULT,            JSOP_NOP,   JSVERSION_DEFAULT},
 
83
    {js_delete_str,     TOK_DELETE,             JSOP_NOP,   JSVERSION_DEFAULT},
 
84
    {"do",              TOK_DO,                 JSOP_NOP,   JSVERSION_DEFAULT},
 
85
    {"else",            TOK_ELSE,               JSOP_NOP,   JSVERSION_DEFAULT},
 
86
    {"export",          TOK_EXPORT,             JSOP_NOP,   JSVERSION_1_2},
 
87
    {js_false_str,      TOK_PRIMARY,            JSOP_FALSE, JSVERSION_DEFAULT},
 
88
    {"for",             TOK_FOR,                JSOP_NOP,   JSVERSION_DEFAULT},
 
89
    {js_function_str,   TOK_FUNCTION,           JSOP_NOP,   JSVERSION_DEFAULT},
 
90
    {"if",              TOK_IF,                 JSOP_NOP,   JSVERSION_DEFAULT},
 
91
    {js_in_str,         TOK_IN,                 JSOP_IN,    JSVERSION_DEFAULT},
 
92
    {js_new_str,        TOK_NEW,                JSOP_NEW,   JSVERSION_DEFAULT},
 
93
    {js_null_str,       TOK_PRIMARY,            JSOP_NULL,  JSVERSION_DEFAULT},
 
94
    {"return",          TOK_RETURN,             JSOP_NOP,   JSVERSION_DEFAULT},
 
95
    {"switch",          TOK_SWITCH,             JSOP_NOP,   JSVERSION_DEFAULT},
 
96
    {js_this_str,       TOK_PRIMARY,            JSOP_THIS,  JSVERSION_DEFAULT},
 
97
    {js_true_str,       TOK_PRIMARY,            JSOP_TRUE,  JSVERSION_DEFAULT},
 
98
    {js_typeof_str,     TOK_UNARYOP,            JSOP_TYPEOF,JSVERSION_DEFAULT},
 
99
    {"var",             TOK_VAR,                JSOP_DEFVAR,JSVERSION_DEFAULT},
 
100
    {js_void_str,       TOK_UNARYOP,            JSOP_VOID,  JSVERSION_DEFAULT},
 
101
    {"while",           TOK_WHILE,              JSOP_NOP,   JSVERSION_DEFAULT},
 
102
    {"with",            TOK_WITH,               JSOP_NOP,   JSVERSION_DEFAULT},
 
103
 
 
104
#if JS_HAS_CONST
 
105
    {js_const_str,      TOK_VAR,                JSOP_DEFCONST,JSVERSION_DEFAULT},
 
106
#else
 
107
    {js_const_str,      TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
108
#endif
 
109
 
 
110
#if JS_HAS_EXCEPTIONS
 
111
    {"try",             TOK_TRY,                JSOP_NOP,   JSVERSION_DEFAULT},
 
112
    {"catch",           TOK_CATCH,              JSOP_NOP,   JSVERSION_DEFAULT},
 
113
    {"finally",         TOK_FINALLY,            JSOP_NOP,   JSVERSION_DEFAULT},
 
114
    {"throw",           TOK_THROW,              JSOP_NOP,   JSVERSION_DEFAULT},
 
115
#else
 
116
    {"try",             TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
117
    {"catch",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
118
    {"finally",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
119
    {"throw",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
120
#endif
 
121
 
 
122
#if JS_HAS_INSTANCEOF
 
123
    {js_instanceof_str, TOK_INSTANCEOF,         JSOP_INSTANCEOF,JSVERSION_1_4},
 
124
#else
 
125
    {js_instanceof_str, TOK_RESERVED,           JSOP_NOP,   JSVERSION_1_4},
 
126
#endif
 
127
 
 
128
#ifdef RESERVE_JAVA_KEYWORDS
 
129
    {"abstract",        TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
130
    {"boolean",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
131
    {"byte",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
132
    {"char",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
133
    {"class",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
134
    {"double",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
135
    {"extends",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
136
    {"final",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
137
    {"float",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
138
    {"goto",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
139
    {"implements",      TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
140
    {"import",          TOK_IMPORT,             JSOP_NOP,   JSVERSION_DEFAULT},
 
141
    {"int",             TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
142
    {"interface",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
143
    {"long",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
144
    {"native",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
145
    {"package",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
146
    {"private",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
147
    {"protected",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
148
    {"public",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
149
    {"short",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
150
    {"static",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
151
    {"super",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
152
    {"synchronized",    TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
153
    {"throws",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
154
    {"transient",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
155
    {"volatile",        TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},
 
156
#endif
 
157
 
 
158
#ifdef RESERVE_ECMA_KEYWORDS
 
159
    {"enum",           TOK_RESERVED,            JSOP_NOP,   JSVERSION_1_3},
 
160
#endif
 
161
 
 
162
#if JS_HAS_DEBUGGER_KEYWORD
 
163
    {"debugger",       TOK_DEBUGGER,            JSOP_NOP,   JSVERSION_1_3},
 
164
#elif defined(RESERVE_ECMA_KEYWORDS)
 
165
    {"debugger",       TOK_RESERVED,            JSOP_NOP,   JSVERSION_1_3},
 
166
#endif
 
167
    {0,                TOK_EOF,                 JSOP_NOP,   JSVERSION_DEFAULT}
 
168
};
 
169
 
 
170
JSBool
 
171
js_InitScanner(JSContext *cx)
 
172
{
 
173
    struct keyword *kw;
 
174
    JSAtom *atom;
 
175
 
 
176
    for (kw = keywords; kw->name; kw++) {
 
177
        atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED);
 
178
        if (!atom)
 
179
            return JS_FALSE;
 
180
        ATOM_SET_KEYWORD(atom, kw);
 
181
    }
 
182
    return JS_TRUE;
 
183
}
 
184
 
 
185
JS_FRIEND_API(void)
 
186
js_MapKeywords(void (*mapfun)(const char *))
 
187
{
 
188
    struct keyword *kw;
 
189
 
 
190
    for (kw = keywords; kw->name; kw++)
 
191
        mapfun(kw->name);
 
192
}
 
193
 
 
194
JSTokenStream *
 
195
js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,
 
196
                  const char *filename, uintN lineno,
 
197
                  JSPrincipals *principals)
 
198
{
 
199
    JSTokenStream *ts;
 
200
 
 
201
    ts = js_NewBufferTokenStream(cx, base, length);
 
202
    if (!ts)
 
203
        return NULL;
 
204
    ts->filename = filename;
 
205
    ts->lineno = lineno;
 
206
    if (principals)
 
207
        JSPRINCIPALS_HOLD(cx, principals);
 
208
    ts->principals = principals;
 
209
    return ts;
 
210
}
 
211
 
 
212
JS_FRIEND_API(JSTokenStream *)
 
213
js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
 
214
{
 
215
    size_t nb;
 
216
    JSTokenStream *ts;
 
217
 
 
218
    nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);
 
219
    JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb);
 
220
    if (!ts) {
 
221
        JS_ReportOutOfMemory(cx);
 
222
        return NULL;
 
223
    }
 
224
    memset(ts, 0, nb);
 
225
    ts->lineno = 1;
 
226
    ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);
 
227
    ts->userbuf.base = (jschar *)base;
 
228
    ts->userbuf.limit = (jschar *)base + length;
 
229
    ts->userbuf.ptr = (jschar *)base;
 
230
    ts->listener = cx->runtime->sourceHandler;
 
231
    ts->listenerData = cx->runtime->sourceHandlerData;
 
232
    return ts;
 
233
}
 
234
 
 
235
JS_FRIEND_API(JSTokenStream *)
 
236
js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp)
 
237
{
 
238
    jschar *base;
 
239
    JSTokenStream *ts;
 
240
    FILE *file;
 
241
 
 
242
    JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool,
 
243
                           JS_LINE_LIMIT * sizeof(jschar));
 
244
    if (!base)
 
245
        return NULL;
 
246
    ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT);
 
247
    if (!ts)
 
248
        return NULL;
 
249
    if (!filename || strcmp(filename, "-") == 0) {
 
250
        file = defaultfp;
 
251
    } else {
 
252
        file = fopen(filename, "r");
 
253
        if (!file) {
 
254
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
 
255
                                 filename, "No such file or directory");
 
256
            return NULL;
 
257
        }
 
258
    }
 
259
    ts->userbuf.ptr = ts->userbuf.limit;
 
260
    ts->file = file;
 
261
    ts->filename = filename;
 
262
    return ts;
 
263
}
 
264
 
 
265
JS_FRIEND_API(JSBool)
 
266
js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)
 
267
{
 
268
    if (ts->principals)
 
269
        JSPRINCIPALS_DROP(cx, ts->principals);
 
270
    return !ts->file || fclose(ts->file) == 0;
 
271
}
 
272
 
 
273
static int
 
274
my_fgets(char *buf, int size, FILE *file)
 
275
{
 
276
    int n, i, c;
 
277
    JSBool crflag;
 
278
 
 
279
    n = size - 1;
 
280
    if (n < 0)
 
281
        return -1;
 
282
 
 
283
    crflag = JS_FALSE;
 
284
    for (i = 0; i < n && (c = getc(file)) != EOF; i++) {
 
285
        buf[i] = c;
 
286
        if (c == '\n') {        /* any \n ends a line */
 
287
            i++;                /* keep the \n; we know there is room for \0 */
 
288
            break;
 
289
        }
 
290
        if (crflag) {           /* \r not followed by \n ends line at the \r */
 
291
            ungetc(c, file);
 
292
            break;              /* and overwrite c in buf with \0 */
 
293
        }
 
294
        crflag = (c == '\r');
 
295
    }
 
296
 
 
297
    buf[i] = '\0';
 
298
    return i;
 
299
}
 
300
 
 
301
static int32
 
302
GetChar(JSTokenStream *ts)
 
303
{
 
304
    int32 c;
 
305
    ptrdiff_t i, j, len, olen;
 
306
    JSBool crflag;
 
307
    char cbuf[JS_LINE_LIMIT];
 
308
    jschar *ubuf, *nl;
 
309
 
 
310
    if (ts->ungetpos != 0) {
 
311
        c = ts->ungetbuf[--ts->ungetpos];
 
312
    } else {
 
313
        do {
 
314
            if (ts->linebuf.ptr == ts->linebuf.limit) {
 
315
                len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
 
316
                if (len <= 0) {
 
317
                    if (!ts->file) {
 
318
                        ts->flags |= TSF_EOF;
 
319
                        return EOF;
 
320
                    }
 
321
 
 
322
                    /* Fill ts->userbuf so that \r and \r\n convert to \n. */
 
323
                    crflag = (ts->flags & TSF_CRFLAG) != 0;
 
324
                    len = my_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file);
 
325
                    if (len <= 0) {
 
326
                        ts->flags |= TSF_EOF;
 
327
                        return EOF;
 
328
                    }
 
329
                    olen = len;
 
330
                    ubuf = ts->userbuf.base;
 
331
                    i = 0;
 
332
                    if (crflag) {
 
333
                        ts->flags &= ~TSF_CRFLAG;
 
334
                        if (cbuf[0] != '\n') {
 
335
                            ubuf[i++] = '\n';
 
336
                            len++;
 
337
                            ts->linepos--;
 
338
                        }
 
339
                    }
 
340
                    for (j = 0; i < len; i++, j++)
 
341
                        ubuf[i] = (jschar) (unsigned char) cbuf[j];
 
342
                    ts->userbuf.limit = ubuf + len;
 
343
                    ts->userbuf.ptr = ubuf;
 
344
                }
 
345
                if (ts->listener) {
 
346
                    ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len,
 
347
                                 &ts->listenerTSData, ts->listenerData);
 
348
                }
 
349
 
 
350
                /*
 
351
                 * Any one of \n, \r, or \r\n ends a line (longest match wins).
 
352
                 * Also allow the Unicode line and paragraph separators.
 
353
                 */
 
354
                for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) {
 
355
                    /*
 
356
                     * Try to prevent value-testing on most characters by
 
357
                     * filtering out characters that aren't 000x or 202x.
 
358
                     */
 
359
                    if ((*nl & 0xDFD0) == 0) {
 
360
                        if (*nl == '\n')
 
361
                            break;
 
362
                        if (*nl == '\r') {
 
363
                            if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
 
364
                                nl++;
 
365
                            break;
 
366
                        }
 
367
                        if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR)
 
368
                            break;
 
369
                    }
 
370
                }
 
371
 
 
372
                /*
 
373
                 * If there was a line terminator, copy thru it into linebuf.
 
374
                 * Else copy JS_LINE_LIMIT-1 bytes into linebuf.
 
375
                 */
 
376
                if (nl < ts->userbuf.limit)
 
377
                    len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;
 
378
                if (len >= JS_LINE_LIMIT)
 
379
                    len = JS_LINE_LIMIT - 1;
 
380
                js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);
 
381
                ts->userbuf.ptr += len;
 
382
                olen = len;
 
383
 
 
384
                /*
 
385
                 * Make sure linebuf contains \n for EOL (don't do this in
 
386
                 * userbuf because the user's string might be readonly).
 
387
                 */
 
388
                if (nl < ts->userbuf.limit) {
 
389
                    if (*nl == '\r') {
 
390
                        if (ts->linebuf.base[len-1] == '\r') {
 
391
                            /*
 
392
                             * Does the line segment end in \r?  We must check
 
393
                             * for a \n at the front of the next segment before
 
394
                             * storing a \n into linebuf.  This case matters
 
395
                             * only when we're reading from a file.
 
396
                             */
 
397
                            if (nl + 1 == ts->userbuf.limit && ts->file) {
 
398
                                len--;
 
399
                                ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */
 
400
                                if (len == 0) {
 
401
                                    /*
 
402
                                     * This can happen when a segment ends in
 
403
                                     * \r\r.  Start over.  ptr == limit in this
 
404
                                     * case, so we'll fall into buffer-filling
 
405
                                     * code.
 
406
                                     */
 
407
                                    return GetChar(ts);
 
408
                                }
 
409
                            } else {
 
410
                                ts->linebuf.base[len-1] = '\n';
 
411
                            }
 
412
                        }
 
413
                    } else if (*nl == '\n') {
 
414
                        if (nl > ts->userbuf.base &&
 
415
                            nl[-1] == '\r' &&
 
416
                            ts->linebuf.base[len-2] == '\r') {
 
417
                            len--;
 
418
                            JS_ASSERT(ts->linebuf.base[len] == '\n');
 
419
                            ts->linebuf.base[len-1] = '\n';
 
420
                        }
 
421
                    } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) {
 
422
                        ts->linebuf.base[len-1] = '\n';
 
423
                    }
 
424
                }
 
425
 
 
426
                /* Reset linebuf based on adjusted segment length. */
 
427
                ts->linebuf.limit = ts->linebuf.base + len;
 
428
                ts->linebuf.ptr = ts->linebuf.base;
 
429
 
 
430
                /* Update position of linebuf within physical userbuf line. */
 
431
                if (!(ts->flags & TSF_NLFLAG))
 
432
                    ts->linepos += ts->linelen;
 
433
                else
 
434
                    ts->linepos = 0;
 
435
                if (ts->linebuf.limit[-1] == '\n')
 
436
                    ts->flags |= TSF_NLFLAG;
 
437
                else
 
438
                    ts->flags &= ~TSF_NLFLAG;
 
439
 
 
440
                /* Update linelen from original segment length. */
 
441
                ts->linelen = olen;
 
442
            }
 
443
            c = *ts->linebuf.ptr++;
 
444
        } while (JS_ISFORMAT(c));
 
445
    }
 
446
    if (c == '\n')
 
447
        ts->lineno++;
 
448
    return c;
 
449
}
 
450
 
 
451
static void
 
452
UngetChar(JSTokenStream *ts, int32 c)
 
453
{
 
454
    if (c == EOF)
 
455
        return;
 
456
    JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
 
457
    if (c == '\n')
 
458
        ts->lineno--;
 
459
    ts->ungetbuf[ts->ungetpos++] = (jschar)c;
 
460
}
 
461
 
 
462
static int32
 
463
PeekChar(JSTokenStream *ts)
 
464
{
 
465
    int32 c;
 
466
 
 
467
    c = GetChar(ts);
 
468
    UngetChar(ts, c);
 
469
    return c;
 
470
}
 
471
 
 
472
static JSBool
 
473
PeekChars(JSTokenStream *ts, intN n, jschar *cp)
 
474
{
 
475
    intN i, j;
 
476
    int32 c;
 
477
 
 
478
    for (i = 0; i < n; i++) {
 
479
        c = GetChar(ts);
 
480
        if (c == EOF)
 
481
            break;
 
482
        cp[i] = (jschar)c;
 
483
    }
 
484
    for (j = i - 1; j >= 0; j--)
 
485
        UngetChar(ts, cp[j]);
 
486
    return i == n;
 
487
}
 
488
 
 
489
static void
 
490
SkipChars(JSTokenStream *ts, intN n)
 
491
{
 
492
    while (--n >= 0)
 
493
        GetChar(ts);
 
494
}
 
495
 
 
496
static JSBool
 
497
MatchChar(JSTokenStream *ts, int32 expect)
 
498
{
 
499
    int32 c;
 
500
 
 
501
    c = GetChar(ts);
 
502
    if (c == expect)
 
503
        return JS_TRUE;
 
504
    UngetChar(ts, c);
 
505
    return JS_FALSE;
 
506
}
 
507
 
 
508
JSBool
 
509
js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts,
 
510
                            JSCodeGenerator *cg, uintN flags,
 
511
                            const uintN errorNumber, ...)
 
512
{
 
513
    va_list ap;
 
514
    JSErrorReporter onError;
 
515
    JSErrorReport report;
 
516
    jschar *tokenptr;
 
517
    JSString *linestr = NULL;
 
518
    char *message;
 
519
    JSBool warning;
 
520
 
 
521
    if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
 
522
        return JS_TRUE;
 
523
 
 
524
    memset(&report, 0, sizeof (struct JSErrorReport));
 
525
    report.flags = flags;
 
526
    report.errorNumber = errorNumber;
 
527
    message = NULL;
 
528
 
 
529
    va_start(ap, errorNumber);
 
530
    if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL,
 
531
                                 errorNumber, &message, &report, &warning,
 
532
                                 JS_TRUE, ap)) {
 
533
        return JS_FALSE;
 
534
    }
 
535
    va_end(ap);
 
536
 
 
537
    js_AddRoot(cx, &linestr, "error line buffer");
 
538
 
 
539
    JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);
 
540
    onError = cx->errorReporter;
 
541
    if (onError) {
 
542
        /* 
 
543
         * We are typically called with non-null ts and null cg from jsparse.c.
 
544
         * We can be called with null ts from the regexp compilation functions.
 
545
         * The code generator (jsemit.c) may pass null ts and non-null cg.
 
546
         */
 
547
        if (ts) {
 
548
            report.filename = ts->filename;
 
549
            report.lineno = ts->lineno;
 
550
            linestr = js_NewStringCopyN(cx, ts->linebuf.base,
 
551
                                        ts->linebuf.limit - ts->linebuf.base,
 
552
                                        0);
 
553
            report.linebuf = linestr
 
554
                ? JS_GetStringBytes(linestr)
 
555
                : NULL;
 
556
            tokenptr =
 
557
                ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].ptr;
 
558
            report.tokenptr = linestr
 
559
                ? report.linebuf + (tokenptr - ts->linebuf.base)
 
560
                : NULL;
 
561
            report.uclinebuf = linestr
 
562
                ? JS_GetStringChars(linestr)
 
563
                : NULL;
 
564
            report.uctokenptr = linestr
 
565
                ? report.uclinebuf + (tokenptr - ts->linebuf.base)
 
566
                : NULL;
 
567
        } else if (cg) {
 
568
            report.filename = cg->filename;
 
569
            report.lineno = CG_CURRENT_LINE(cg);
 
570
        }
 
571
 
 
572
#if JS_HAS_ERROR_EXCEPTIONS
 
573
        /*
 
574
         * If there's a runtime exception type associated with this error
 
575
         * number, set that as the pending exception.  For errors occuring at
 
576
         * compile time, this is very likely to be a JSEXN_SYNTAXERR.
 
577
         *
 
578
         * If an exception is thrown but not caught, the JSREPORT_EXCEPTION
 
579
         * flag will be set in report.flags.  Proper behavior for an error
 
580
         * reporter is to ignore a report with this flag for all but top-level
 
581
         * compilation errors.  The exception will remain pending, and so long
 
582
         * as the non-top-level "load", "eval", or "compile" native function
 
583
         * returns false, the top-level reporter will eventually receive the
 
584
         * uncaught exception report.
 
585
         * 
 
586
         * XXX it'd probably be best if there was only one call to this
 
587
         * function, but there seem to be two error reporter call points.
 
588
         */
 
589
 
 
590
        /*
 
591
         * Only try to raise an exception if there isn't one already set -
 
592
         * otherwise the exception will describe only the last compile error,
 
593
         * which is likely spurious.
 
594
         */
 
595
        if (!(ts && (ts->flags & TSF_ERROR)))
 
596
            if (js_ErrorToException(cx, message, &report))
 
597
                onError = NULL;
 
598
 
 
599
        /*
 
600
         * Suppress any compiletime errors that don't occur at the top level.
 
601
         * This may still fail, as interplevel may be zero in contexts where we
 
602
         * don't really want to call the error reporter, as when js is called
 
603
         * by other code which could catch the error.
 
604
         */
 
605
        if (cx->interpLevel != 0)
 
606
            onError = NULL;
 
607
#endif
 
608
        if (cx->runtime->debugErrorHook && onError) {
 
609
            JSDebugErrorHook hook = cx->runtime->debugErrorHook;
 
610
            /* test local in case debugErrorHook changed on another thread */
 
611
            if (hook && !hook(cx, message, &report,
 
612
                              cx->runtime->debugErrorHookData)) {
 
613
                onError = NULL;
 
614
            }
 
615
        }
 
616
        if (onError)
 
617
            (*onError)(cx, message, &report);
 
618
    }
 
619
    if (message)
 
620
        JS_free(cx, message);
 
621
    if (report.messageArgs) {
 
622
        int i = 0;
 
623
        while (report.messageArgs[i])
 
624
            JS_free(cx, (void *)report.messageArgs[i++]);
 
625
        JS_free(cx, (void *)report.messageArgs);
 
626
    }
 
627
    if (report.ucmessage)
 
628
        JS_free(cx, (void *)report.ucmessage);
 
629
 
 
630
    js_RemoveRoot(cx->runtime, &linestr);
 
631
 
 
632
    if (ts && !JSREPORT_IS_WARNING(flags)) {
 
633
        /* Set the error flag to suppress spurious reports. */
 
634
        ts->flags |= TSF_ERROR;
 
635
    }
 
636
    return warning;
 
637
}
 
638
 
 
639
JSTokenType
 
640
js_PeekToken(JSContext *cx, JSTokenStream *ts)
 
641
{
 
642
    JSTokenType tt;
 
643
 
 
644
    if (ts->lookahead != 0) {
 
645
        tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type;
 
646
    } else {
 
647
        tt = js_GetToken(cx, ts);
 
648
        js_UngetToken(ts);
 
649
    }
 
650
    return tt;
 
651
}
 
652
 
 
653
JSTokenType
 
654
js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)
 
655
{
 
656
    JSTokenType tt;
 
657
 
 
658
    JS_ASSERT(ts->lookahead == 0 ||
 
659
              ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos));
 
660
    ts->flags |= TSF_NEWLINES;
 
661
    tt = js_PeekToken(cx, ts);
 
662
    ts->flags &= ~TSF_NEWLINES;
 
663
    return tt;
 
664
}
 
665
 
 
666
#define TBMIN   64
 
667
 
 
668
static JSBool
 
669
GrowTokenBuf(JSContext *cx, JSTokenBuf *tb)
 
670
{
 
671
    jschar *base;
 
672
    ptrdiff_t offset, length;
 
673
    size_t tbsize;
 
674
    JSArenaPool *pool;
 
675
 
 
676
    base = tb->base;
 
677
    offset = PTRDIFF(tb->ptr, base, jschar);
 
678
    pool = &cx->tempPool;
 
679
    if (!base) {
 
680
        tbsize = TBMIN * sizeof(jschar);
 
681
        length = TBMIN;
 
682
        JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize);
 
683
    } else {
 
684
        length = PTRDIFF(tb->limit, base, jschar);
 
685
        tbsize = length * sizeof(jschar);
 
686
        length <<= 1;
 
687
        JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize);
 
688
    }
 
689
    if (!base) {
 
690
        JS_ReportOutOfMemory(cx);
 
691
        return JS_FALSE;
 
692
    }
 
693
    tb->base = base;
 
694
    tb->limit = base + length;
 
695
    tb->ptr = base + offset;
 
696
    return JS_TRUE;
 
697
}
 
698
 
 
699
static JSBool
 
700
AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c)
 
701
{
 
702
    if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb))
 
703
        return JS_FALSE;
 
704
    *tb->ptr++ = c;
 
705
    return JS_TRUE;
 
706
}
 
707
 
 
708
/*
 
709
 * We have encountered a '\': check for a Unicode escape sequence after it,
 
710
 * returning the character code value if we found a Unicode escape sequence.
 
711
 * Otherwise, non-destructively return the original '\'.
 
712
 */
 
713
static int32
 
714
GetUnicodeEscape(JSTokenStream *ts)
 
715
{
 
716
    jschar cp[5];
 
717
    int32 c;
 
718
 
 
719
    if (PeekChars(ts, 5, cp) && cp[0] == 'u' &&
 
720
        JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&
 
721
        JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4]))
 
722
    {
 
723
        c = (((((JS7_UNHEX(cp[1]) << 4)
 
724
                + JS7_UNHEX(cp[2])) << 4)
 
725
              + JS7_UNHEX(cp[3])) << 4)
 
726
            + JS7_UNHEX(cp[4]);
 
727
        SkipChars(ts, 5);
 
728
        return c;
 
729
    }
 
730
    return '\\';
 
731
}
 
732
 
 
733
JSTokenType
 
734
js_GetToken(JSContext *cx, JSTokenStream *ts)
 
735
{
 
736
    JSTokenType tt;
 
737
    JSToken *tp;
 
738
    int32 c;
 
739
    JSAtom *atom;
 
740
    JSBool hadUnicodeEscape;
 
741
 
 
742
#define INIT_TOKENBUF(tb)   ((tb)->ptr = (tb)->base)
 
743
#define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR)
 
744
#define TOKEN_LENGTH(tb)    ((tb)->ptr - (tb)->base - 1)
 
745
#define RETURN(tt)          { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR;    \
 
746
                              tp->pos.end.index = ts->linepos +               \
 
747
                                  (ts->linebuf.ptr - ts->linebuf.base) -      \
 
748
                                  ts->ungetpos;                               \
 
749
                              return (tp->type = tt); }
 
750
 
 
751
    /* If there was a fatal error, keep returning TOK_ERROR. */
 
752
    if (ts->flags & TSF_ERROR)
 
753
        return TOK_ERROR;
 
754
 
 
755
    /* Check for a pushed-back token resulting from mismatching lookahead. */
 
756
    while (ts->lookahead != 0) {
 
757
        ts->lookahead--;
 
758
        ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
 
759
        tt = CURRENT_TOKEN(ts).type;
 
760
        if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))
 
761
            return tt;
 
762
    }
 
763
 
 
764
retry:
 
765
    do {
 
766
        c = GetChar(ts);
 
767
        if (c == '\n') {
 
768
            ts->flags &= ~TSF_DIRTYLINE;
 
769
            if (ts->flags & TSF_NEWLINES)
 
770
                break;
 
771
        }
 
772
    } while (JS_ISSPACE(c));
 
773
 
 
774
    ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;
 
775
    tp = &CURRENT_TOKEN(ts);
 
776
    tp->ptr = ts->linebuf.ptr - 1;
 
777
    tp->pos.begin.index = ts->linepos + (tp->ptr - ts->linebuf.base);
 
778
    tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno;
 
779
 
 
780
    if (c == EOF)
 
781
        RETURN(TOK_EOF);
 
782
    if (c != '-' && c != '\n')
 
783
        ts->flags |= TSF_DIRTYLINE;
 
784
 
 
785
    hadUnicodeEscape = JS_FALSE;
 
786
    if (JS_ISIDENT_START(c) ||
 
787
        (c == '\\' &&
 
788
         (c = GetUnicodeEscape(ts),
 
789
          hadUnicodeEscape = JS_ISIDENT_START(c)))) {
 
790
        INIT_TOKENBUF(&ts->tokenbuf);
 
791
        for (;;) {
 
792
            if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
793
                RETURN(TOK_ERROR);
 
794
            c = GetChar(ts);
 
795
            if (c == '\\') {
 
796
                c = GetUnicodeEscape(ts);
 
797
                if (!JS_ISIDENT(c))
 
798
                    break;
 
799
                hadUnicodeEscape = JS_TRUE;
 
800
            } else {
 
801
                if (!JS_ISIDENT(c))
 
802
                    break;
 
803
            }
 
804
        }
 
805
        UngetChar(ts, c);
 
806
        FINISH_TOKENBUF(&ts->tokenbuf);
 
807
 
 
808
        atom = js_AtomizeChars(cx,
 
809
                               ts->tokenbuf.base,
 
810
                               TOKEN_LENGTH(&ts->tokenbuf),
 
811
                               0);
 
812
        if (!atom)
 
813
            RETURN(TOK_ERROR);
 
814
        if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) {
 
815
            struct keyword *kw = ATOM_KEYWORD(atom);
 
816
 
 
817
            if (JSVERSION_IS_ECMA(cx->version) || kw->version <= cx->version) {
 
818
                tp->t_op = (JSOp) kw->op;
 
819
                RETURN(kw->tokentype);
 
820
            }
 
821
        }
 
822
        tp->t_op = JSOP_NAME;
 
823
        tp->t_atom = atom;
 
824
        RETURN(TOK_NAME);
 
825
    }
 
826
 
 
827
    if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
 
828
        jsint radix;
 
829
        const jschar *endptr;
 
830
        jsdouble dval;
 
831
 
 
832
        radix = 10;
 
833
        INIT_TOKENBUF(&ts->tokenbuf);
 
834
 
 
835
        if (c == '0') {
 
836
            if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
837
                RETURN(TOK_ERROR);
 
838
            c = GetChar(ts);
 
839
            if (JS_TOLOWER(c) == 'x') {
 
840
                if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
841
                    RETURN(TOK_ERROR);
 
842
                c = GetChar(ts);
 
843
                radix = 16;
 
844
            } else if (JS7_ISDEC(c)) {
 
845
                radix = 8;
 
846
            }
 
847
        }
 
848
 
 
849
        while (JS7_ISHEX(c)) {
 
850
            if (radix < 16) {
 
851
                if (JS7_ISLET(c))
 
852
                    break;
 
853
 
 
854
                /*
 
855
                 * We permit 08 and 09 as decimal numbers, which makes our
 
856
                 * behaviour a superset of the ECMA numeric grammar.  We might
 
857
                 * not always be so permissive, so we warn about it.
 
858
                 */
 
859
                if (radix == 8 && c >= '8') {
 
860
                    if (!js_ReportCompileErrorNumber(cx, ts, NULL,
 
861
                                                     JSREPORT_WARNING,
 
862
                                                     JSMSG_BAD_OCTAL,
 
863
                                                     c == '8' ? "08" : "09")) {
 
864
                        RETURN(TOK_ERROR);
 
865
                    }
 
866
                    radix = 10;
 
867
                }
 
868
            }
 
869
            if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
870
                RETURN(TOK_ERROR);
 
871
            c = GetChar(ts);
 
872
        }
 
873
 
 
874
        if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
 
875
            if (c == '.') {
 
876
                do {
 
877
                    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
878
                        RETURN(TOK_ERROR);
 
879
                    c = GetChar(ts);
 
880
                } while (JS7_ISDEC(c));
 
881
            }
 
882
            if (JS_TOLOWER(c) == 'e') {
 
883
                if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
884
                    RETURN(TOK_ERROR);
 
885
                c = GetChar(ts);
 
886
                if (c == '+' || c == '-') {
 
887
                    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
888
                        RETURN(TOK_ERROR);
 
889
                    c = GetChar(ts);
 
890
                }
 
891
                if (!JS7_ISDEC(c)) {
 
892
                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
893
                                                JSMSG_MISSING_EXPONENT);
 
894
                    RETURN(TOK_ERROR);
 
895
                }
 
896
                do {
 
897
                    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
898
                        RETURN(TOK_ERROR);
 
899
                    c = GetChar(ts);
 
900
                } while (JS7_ISDEC(c));
 
901
            }
 
902
        }
 
903
 
 
904
        UngetChar(ts, c);
 
905
        FINISH_TOKENBUF(&ts->tokenbuf);
 
906
 
 
907
        if (radix == 10) {
 
908
            if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) {
 
909
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
910
                                            JSMSG_OUT_OF_MEMORY);
 
911
                RETURN(TOK_ERROR);
 
912
            }
 
913
        } else {
 
914
            if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) {
 
915
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
916
                                            JSMSG_OUT_OF_MEMORY);
 
917
                RETURN(TOK_ERROR);
 
918
            }
 
919
        }
 
920
        tp->t_dval = dval;
 
921
        RETURN(TOK_NUMBER);
 
922
    }
 
923
 
 
924
    if (c == '"' || c == '\'') {
 
925
        int32 val, qc = c;
 
926
 
 
927
        INIT_TOKENBUF(&ts->tokenbuf);
 
928
        while ((c = GetChar(ts)) != qc) {
 
929
            if (c == '\n' || c == EOF) {
 
930
                UngetChar(ts, c);
 
931
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
932
                                            JSMSG_UNTERMINATED_STRING);
 
933
                RETURN(TOK_ERROR);
 
934
            }
 
935
            if (c == '\\') {
 
936
                switch (c = GetChar(ts)) {
 
937
                  case 'b': c = '\b'; break;
 
938
                  case 'f': c = '\f'; break;
 
939
                  case 'n': c = '\n'; break;
 
940
                  case 'r': c = '\r'; break;
 
941
                  case 't': c = '\t'; break;
 
942
                  case 'v': c = '\v'; break;
 
943
 
 
944
                  default:
 
945
                    if ('0' <= c && c < '8') {
 
946
                        val = JS7_UNDEC(c);
 
947
                        c = PeekChar(ts);
 
948
                        if ('0' <= c && c < '8') {
 
949
                            val = 8 * val + JS7_UNDEC(c);
 
950
                            GetChar(ts);
 
951
                            c = PeekChar(ts);
 
952
                            if ('0' <= c && c < '8') {
 
953
                                int32 save = val;
 
954
                                val = 8 * val + JS7_UNDEC(c);
 
955
                                if (val <= 0377)
 
956
                                    GetChar(ts);
 
957
                                else
 
958
                                    val = save;
 
959
                            }
 
960
                        }
 
961
                        c = (jschar)val;
 
962
                    } else if (c == 'u') {
 
963
                        jschar cp[4];
 
964
                        if (PeekChars(ts, 4, cp) &&
 
965
                            JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
 
966
                            JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
 
967
                            c = (((((JS7_UNHEX(cp[0]) << 4)
 
968
                                    + JS7_UNHEX(cp[1])) << 4)
 
969
                                  + JS7_UNHEX(cp[2])) << 4)
 
970
                                + JS7_UNHEX(cp[3]);
 
971
                            SkipChars(ts, 4);
 
972
                        }
 
973
                    } else if (c == 'x') {
 
974
                        jschar cp[2];
 
975
                        if (PeekChars(ts, 2, cp) &&
 
976
                            JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
 
977
                            c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
 
978
                            SkipChars(ts, 2);
 
979
                        }
 
980
                    } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) {
 
981
                        /* ECMA follows C by removing escaped newlines. */
 
982
                        continue;
 
983
                    }
 
984
                    break;
 
985
                }
 
986
            }
 
987
            if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
988
                RETURN(TOK_ERROR);
 
989
        }
 
990
        FINISH_TOKENBUF(&ts->tokenbuf);
 
991
        atom = js_AtomizeChars(cx,
 
992
                               ts->tokenbuf.base,
 
993
                               TOKEN_LENGTH(&ts->tokenbuf),
 
994
                               0);
 
995
        if (!atom)
 
996
            RETURN(TOK_ERROR);
 
997
        tp->pos.end.lineno = (uint16)ts->lineno;
 
998
        tp->t_op = JSOP_STRING;
 
999
        tp->t_atom = atom;
 
1000
        RETURN(TOK_STRING);
 
1001
    }
 
1002
 
 
1003
    switch (c) {
 
1004
      case '\n': 
 
1005
        c = TOK_EOL; 
 
1006
        break;
 
1007
 
 
1008
      case ';': c = TOK_SEMI; break;
 
1009
      case '.': c = TOK_DOT; break;
 
1010
      case '[': c = TOK_LB; break;
 
1011
      case ']': c = TOK_RB; break;
 
1012
      case '{': c = TOK_LC; break;
 
1013
      case '}': c = TOK_RC; break;
 
1014
      case '(': c = TOK_LP; break;
 
1015
      case ')': c = TOK_RP; break;
 
1016
      case ',': c = TOK_COMMA; break;
 
1017
      case '?': c = TOK_HOOK; break;
 
1018
 
 
1019
      case ':':
 
1020
        /*
 
1021
         * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an
 
1022
         * object initializer, likewise for setter.
 
1023
         */
 
1024
        tp->t_op = JSOP_NOP;
 
1025
        c = TOK_COLON;
 
1026
        break;
 
1027
 
 
1028
      case '|':
 
1029
        if (MatchChar(ts, c)) {
 
1030
            c = TOK_OR;
 
1031
        } else if (MatchChar(ts, '=')) {
 
1032
            tp->t_op = JSOP_BITOR;
 
1033
            c = TOK_ASSIGN;
 
1034
        } else {
 
1035
            c = TOK_BITOR;
 
1036
        }
 
1037
        break;
 
1038
 
 
1039
      case '^':
 
1040
        if (MatchChar(ts, '=')) {
 
1041
            tp->t_op = JSOP_BITXOR;
 
1042
            c = TOK_ASSIGN;
 
1043
        } else {
 
1044
            c = TOK_BITXOR;
 
1045
        }
 
1046
        break;
 
1047
 
 
1048
      case '&':
 
1049
        if (MatchChar(ts, c)) {
 
1050
            c = TOK_AND;
 
1051
        } else if (MatchChar(ts, '=')) {
 
1052
            tp->t_op = JSOP_BITAND;
 
1053
            c = TOK_ASSIGN;
 
1054
        } else {
 
1055
            c = TOK_BITAND;
 
1056
        }
 
1057
        break;
 
1058
 
 
1059
      case '=':
 
1060
        if (MatchChar(ts, c)) {
 
1061
#if JS_HAS_TRIPLE_EQOPS
 
1062
            tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq;
 
1063
#else
 
1064
            tp->t_op = cx->jsop_eq;
 
1065
#endif
 
1066
            c = TOK_EQOP;
 
1067
        } else {
 
1068
            tp->t_op = JSOP_NOP;
 
1069
            c = TOK_ASSIGN;
 
1070
        }
 
1071
        break;
 
1072
 
 
1073
      case '!':
 
1074
        if (MatchChar(ts, '=')) {
 
1075
#if JS_HAS_TRIPLE_EQOPS
 
1076
            tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;
 
1077
#else
 
1078
            tp->t_op = cx->jsop_ne;
 
1079
#endif
 
1080
            c = TOK_EQOP;
 
1081
        } else {
 
1082
            tp->t_op = JSOP_NOT;
 
1083
            c = TOK_UNARYOP;
 
1084
        }
 
1085
        break;
 
1086
 
 
1087
      case '<':
 
1088
        /* NB: treat HTML begin-comment as comment-till-end-of-line */
 
1089
        if (MatchChar(ts, '!')) {
 
1090
            if (MatchChar(ts, '-')) {
 
1091
                if (MatchChar(ts, '-'))
 
1092
                    goto skipline;
 
1093
                UngetChar(ts, '-');
 
1094
            }
 
1095
            UngetChar(ts, '!');
 
1096
        }
 
1097
        if (MatchChar(ts, c)) {
 
1098
            tp->t_op = JSOP_LSH;
 
1099
            c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
 
1100
        } else {
 
1101
            tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
 
1102
            c = TOK_RELOP;
 
1103
        }
 
1104
        break;
 
1105
 
 
1106
      case '>':
 
1107
        if (MatchChar(ts, c)) {
 
1108
            tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;
 
1109
            c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
 
1110
        } else {
 
1111
            tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
 
1112
            c = TOK_RELOP;
 
1113
        }
 
1114
        break;
 
1115
 
 
1116
      case '*':
 
1117
        tp->t_op = JSOP_MUL;
 
1118
        c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;
 
1119
        break;
 
1120
 
 
1121
      case '/':
 
1122
        if (MatchChar(ts, '/')) {
 
1123
skipline:
 
1124
            while ((c = GetChar(ts)) != EOF && c != '\n')
 
1125
                /* skip to end of line */;
 
1126
            UngetChar(ts, c);
 
1127
            goto retry;
 
1128
        }
 
1129
        if (MatchChar(ts, '*')) {
 
1130
            while ((c = GetChar(ts)) != EOF &&
 
1131
                   !(c == '*' && MatchChar(ts, '/'))) {
 
1132
                /* Ignore all characters until comment close. */
 
1133
            }
 
1134
            if (c == EOF) {
 
1135
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
1136
                                            JSMSG_UNTERMINATED_COMMENT);
 
1137
                RETURN(TOK_ERROR);
 
1138
            }
 
1139
            goto retry;
 
1140
        }
 
1141
 
 
1142
#if JS_HAS_REGEXPS
 
1143
        if (ts->flags & TSF_REGEXP) {
 
1144
            JSObject *obj;
 
1145
            uintN flags;
 
1146
 
 
1147
            INIT_TOKENBUF(&ts->tokenbuf);
 
1148
            while ((c = GetChar(ts)) != '/') {
 
1149
                if (c == '\n' || c == EOF) {
 
1150
                    UngetChar(ts, c);
 
1151
                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
1152
                                                JSMSG_UNTERMINATED_REGEXP);
 
1153
                    RETURN(TOK_ERROR);
 
1154
                }
 
1155
                if (c == '\\') {
 
1156
                    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
1157
                        RETURN(TOK_ERROR);
 
1158
                    c = GetChar(ts);
 
1159
                }
 
1160
                if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
 
1161
                    RETURN(TOK_ERROR);
 
1162
            }
 
1163
            FINISH_TOKENBUF(&ts->tokenbuf);
 
1164
            for (flags = 0; ; ) {
 
1165
                if (MatchChar(ts, 'g'))
 
1166
                    flags |= JSREG_GLOB;
 
1167
                else if (MatchChar(ts, 'i'))
 
1168
                    flags |= JSREG_FOLD;
 
1169
                else if (MatchChar(ts, 'm'))
 
1170
                    flags |= JSREG_MULTILINE;
 
1171
                else
 
1172
                    break;
 
1173
            }
 
1174
            c = PeekChar(ts);
 
1175
            if (JS7_ISLET(c)) {
 
1176
                tp->ptr = ts->linebuf.ptr - 1;
 
1177
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
1178
                                            JSMSG_BAD_REGEXP_FLAG);
 
1179
                (void) GetChar(ts);
 
1180
                RETURN(TOK_ERROR);
 
1181
            }
 
1182
            obj = js_NewRegExpObject(cx, ts,
 
1183
                                     ts->tokenbuf.base,
 
1184
                                     TOKEN_LENGTH(&ts->tokenbuf),
 
1185
                                     flags);
 
1186
            if (!obj)
 
1187
                RETURN(TOK_ERROR);
 
1188
            atom = js_AtomizeObject(cx, obj, 0);
 
1189
            if (!atom)
 
1190
                RETURN(TOK_ERROR);
 
1191
            tp->t_op = JSOP_OBJECT;
 
1192
            tp->t_atom = atom;
 
1193
            RETURN(TOK_OBJECT);
 
1194
        }
 
1195
#endif /* JS_HAS_REGEXPS */
 
1196
 
 
1197
        tp->t_op = JSOP_DIV;
 
1198
        c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
 
1199
        break;
 
1200
 
 
1201
      case '%':
 
1202
        tp->t_op = JSOP_MOD;
 
1203
        c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
 
1204
        break;
 
1205
 
 
1206
      case '~':
 
1207
        tp->t_op = JSOP_BITNOT;
 
1208
        c = TOK_UNARYOP;
 
1209
        break;
 
1210
 
 
1211
      case '+':
 
1212
        if (MatchChar(ts, '=')) {
 
1213
            tp->t_op = JSOP_ADD;
 
1214
            c = TOK_ASSIGN;
 
1215
        } else if (MatchChar(ts, c)) {
 
1216
            c = TOK_INC;
 
1217
        } else {
 
1218
            tp->t_op = JSOP_POS;
 
1219
            c = TOK_PLUS;
 
1220
        }
 
1221
        break;
 
1222
 
 
1223
      case '-':
 
1224
        if (MatchChar(ts, '=')) {
 
1225
            tp->t_op = JSOP_SUB;
 
1226
            c = TOK_ASSIGN;
 
1227
        } else if (MatchChar(ts, c)) {
 
1228
            if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE))
 
1229
                goto skipline;
 
1230
            c = TOK_DEC;
 
1231
        } else {
 
1232
            tp->t_op = JSOP_NEG;
 
1233
            c = TOK_MINUS;
 
1234
        }
 
1235
        ts->flags |= TSF_DIRTYLINE;
 
1236
        break;
 
1237
 
 
1238
#if JS_HAS_SHARP_VARS
 
1239
      case '#':
 
1240
      {
 
1241
        uint32 n;
 
1242
 
 
1243
        c = GetChar(ts);
 
1244
        if (!JS7_ISDEC(c)) {
 
1245
            UngetChar(ts, c);
 
1246
            goto badchar;
 
1247
        }
 
1248
        n = (uint32)JS7_UNDEC(c);
 
1249
        for (;;) {
 
1250
            c = GetChar(ts);
 
1251
            if (!JS7_ISDEC(c))
 
1252
                break;
 
1253
            n = 10 * n + JS7_UNDEC(c);
 
1254
            if (n >= ATOM_INDEX_LIMIT) {
 
1255
                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
1256
                                            JSMSG_SHARPVAR_TOO_BIG);
 
1257
                RETURN(TOK_ERROR);
 
1258
            }
 
1259
        }
 
1260
        tp->t_dval = (jsdouble) n;
 
1261
        if (JS_HAS_STRICT_OPTION(cx) &&
 
1262
            (c == '=' || c == '#')) {
 
1263
            char buf[20];
 
1264
            JS_snprintf(buf, sizeof buf, "#%u%c", n, c);
 
1265
            if (!js_ReportCompileErrorNumber(cx, ts, NULL,
 
1266
                                             JSREPORT_WARNING |
 
1267
                                             JSREPORT_STRICT,
 
1268
                                             JSMSG_DEPRECATED_USAGE,
 
1269
                                             buf)) {
 
1270
                RETURN(TOK_ERROR);
 
1271
            }
 
1272
        }
 
1273
        if (c == '=')
 
1274
            RETURN(TOK_DEFSHARP);
 
1275
        if (c == '#')
 
1276
            RETURN(TOK_USESHARP);
 
1277
        goto badchar;
 
1278
      }
 
1279
 
 
1280
      badchar:
 
1281
#endif /* JS_HAS_SHARP_VARS */
 
1282
 
 
1283
      default:
 
1284
        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 
1285
                                    JSMSG_ILLEGAL_CHARACTER);
 
1286
        RETURN(TOK_ERROR);
 
1287
    }
 
1288
 
 
1289
    JS_ASSERT(c < TOK_LIMIT);
 
1290
    RETURN((JSTokenType)c);
 
1291
 
 
1292
#undef INIT_TOKENBUF
 
1293
#undef FINISH_TOKENBUF
 
1294
#undef TOKEN_LENGTH
 
1295
#undef RETURN
 
1296
}
 
1297
 
 
1298
void
 
1299
js_UngetToken(JSTokenStream *ts)
 
1300
{
 
1301
    JS_ASSERT(ts->lookahead < NTOKENS_MASK);
 
1302
    if (ts->flags & TSF_ERROR)
 
1303
        return;
 
1304
    ts->lookahead++;
 
1305
    ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;
 
1306
}
 
1307
 
 
1308
JSBool
 
1309
js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
 
1310
{
 
1311
    if (js_GetToken(cx, ts) == tt)
 
1312
        return JS_TRUE;
 
1313
    js_UngetToken(ts);
 
1314
    return JS_FALSE;
 
1315
}