PageRenderTime 70ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsscan.cpp

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