diff --git a/js/rhino/src/org/mozilla/javascript/Context.java b/js/rhino/src/org/mozilla/javascript/Context.java
index b57e3e277aa..112bf19250f 100644
--- a/js/rhino/src/org/mozilla/javascript/Context.java
+++ b/js/rhino/src/org/mozilla/javascript/Context.java
@@ -779,14 +779,12 @@ public class Context {
Object securityDomain)
throws JavaScriptException
{
- try {
- Reader in = new StringReader(source);
- return evaluateReader(scope, in, sourceName, lineno,
- securityDomain);
- }
- catch (IOException ioe) {
- // Should never occur because we just made the reader from a String
- throw new RuntimeException();
+ Script script = compileString(scope, source, sourceName, lineno,
+ securityDomain);
+ if (script != null) {
+ return script.exec(this, scope);
+ } else {
+ return null;
}
}
@@ -816,10 +814,11 @@ public class Context {
{
Script script = compileReader(scope, in, sourceName, lineno,
securityDomain);
- if (script != null)
+ if (script != null) {
return script.exec(this, scope);
- else
+ } else {
return null;
+ }
}
/**
@@ -840,10 +839,9 @@ public class Context {
*/
synchronized public boolean stringIsCompilableUnit(String source)
{
- Reader in = new StringReader(source);
// no source name or source text manager, because we're just
// going to throw away the result.
- TokenStream ts = new TokenStream(in, null, null, 1);
+ TokenStream ts = new TokenStream(null, source, null, null, 1);
// Temporarily set error reporter to always be the exception-throwing
// DefaultErrorReporter. (This is why the method is synchronized...)
@@ -896,10 +894,41 @@ public class Context {
int lineno, Object securityDomain)
throws IOException
{
- return (Script) compile(scope, in, sourceName, lineno, securityDomain,
- false);
+ return (Script) compile(scope, in, null, sourceName, lineno,
+ securityDomain, false);
}
+ /**
+ * Compiles the source in the given string.
+ *
+ * Returns a script that may later be executed.
+ *
+ * @param scope if nonnull, will be the scope in which the script object
+ * is created. The script object will be a valid JavaScript object
+ * as if it were created using the JavaScript1.3 Script constructor
+ * @param source the source string
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number for reporting errors
+ * @param securityDomain an arbitrary object that specifies security
+ * information about the origin or owner of the script. For
+ * implementations that don't care about security, this value
+ * may be null.
+ * @return a script that may later be executed
+ * @see org.mozilla.javascript.Script#exec
+ * @exception IOException if an IOException was generated by the Reader
+ */
+ public Script compileString(Scriptable scope, String source,
+ String sourceName, int lineno,
+ Object securityDomain)
+ {
+ try {
+ return (Script) compile(scope, null, source, sourceName, lineno,
+ securityDomain, false);
+ } catch (IOException ex) {
+ // Should not happen when dealing with source as string
+ throw new RuntimeException();
+ }
+ }
/**
* Compile a JavaScript function.
@@ -922,9 +951,8 @@ public class Context {
String sourceName, int lineno,
Object securityDomain)
{
- Reader in = new StringReader(source);
try {
- return (Function) compile(scope, in, sourceName, lineno,
+ return (Function) compile(scope, null, source, sourceName, lineno,
securityDomain, true);
}
catch (IOException ioe) {
@@ -1904,6 +1932,24 @@ public class Context {
return formatter.format(arguments);
}
+ private static String readReader(Reader r)
+ throws IOException
+ {
+ char[] buffer = new char[512];
+ int cursor = 0;
+ for (;;) {
+ int n = r.read(buffer, cursor, buffer.length - cursor);
+ if (n < 0) { break; }
+ cursor += n;
+ if (cursor == buffer.length) {
+ char[] tmp = new char[buffer.length];
+ System.arraycopy(buffer, 0, tmp, 0, cursor);
+ buffer = tmp;
+ }
+ }
+ return new String(buffer, 0, cursor);
+ }
+
// debug flags
static final boolean printTrees = false;
static final boolean printICode = false;
@@ -1930,22 +1976,30 @@ public class Context {
* @return a class for the script or function
* @see org.mozilla.javascript.Context#compileReader
*/
- private Object compile(Scriptable scope, Reader in, String sourceName,
- int lineno, Object securityDomain,
- boolean returnFunction)
+ private Object compile(Scriptable scope,
+ Reader sourceReader, String sourceString,
+ String sourceName, int lineno,
+ Object securityDomain, boolean returnFunction)
throws IOException
{
+ // One of sourceReader or sourceString has to be null
+ if (!(sourceReader == null ^ sourceString == null)) Context.codeBug();
+
Object dynamicDoamin = null;
if (securityController != null) {
dynamicDoamin = securityController.
getDynamicSecurityDomain(securityDomain);
}
- if (debugger != null && in != null) {
- in = new DebugReader(in);
+ if (debugger != null) {
+ if (sourceReader != null) {
+ sourceString = readReader(sourceReader);
+ sourceReader = null;
+ }
}
- TokenStream ts = new TokenStream(in, scope, sourceName, lineno);
- return compile(scope, ts, dynamicDoamin, in, returnFunction);
+ TokenStream ts = new TokenStream(sourceReader, sourceString,
+ scope, sourceName, lineno);
+ return compile(scope, ts, dynamicDoamin, sourceString, returnFunction);
}
private static Class codegenClass;
@@ -1978,7 +2032,7 @@ public class Context {
}
private Object compile(Scriptable scope, TokenStream ts,
- Object dynamicSecurityDomain, Reader in,
+ Object dynamicSecurityDomain, String sourceString,
boolean returnFunction)
throws IOException
{
@@ -2010,9 +2064,9 @@ public class Context {
return null;
}
- if (in instanceof DebugReader) {
- DebugReader dr = (DebugReader) in;
- tree.putProp(Node.DEBUGSOURCE_PROP, dr.getSaved());
+ if (debugger != null) {
+ if (sourceString == null) Context.codeBug();
+ tree.putProp(Node.DEBUGSOURCE_PROP, sourceString);
}
Object result = compiler.compile(this, scope, tree,
diff --git a/js/rhino/src/org/mozilla/javascript/LineBuffer.java b/js/rhino/src/org/mozilla/javascript/LineBuffer.java
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/js/rhino/src/org/mozilla/javascript/TokenStream.java b/js/rhino/src/org/mozilla/javascript/TokenStream.java
index 2037769e816..9365e8cc020 100644
--- a/js/rhino/src/org/mozilla/javascript/TokenStream.java
+++ b/js/rhino/src/org/mozilla/javascript/TokenStream.java
@@ -21,6 +21,7 @@
* Contributor(s):
* Roger Lawrence
* Mike McCabe
+ * Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
@@ -613,20 +614,45 @@ public class TokenStream {
return id & 0xff;
}
- public TokenStream(Reader in, Scriptable scope,
- String sourceName, int lineno)
+ public TokenStream(Reader sourceReader, String sourceString,
+ Scriptable scope, String sourceName, int lineno)
{
- this.in = new LineBuffer(in, lineno);
this.scope = scope;
this.pushbackToken = EOF;
this.sourceName = sourceName;
- flags = 0;
+ this.lineno = lineno;
+ this.flags = 0;
+ if (sourceReader != null) {
+ if (sourceString != null) Context.codeBug();
+ this.sourceReader = sourceReader;
+ this.sourceBuffer = new char[512];
+ this.sourceEnd = 0;
+ } else {
+ if (sourceString == null) Context.codeBug();
+ this.sourceString = sourceString;
+ this.sourceEnd = sourceString.length();
+ }
+ this.sourceCursor = 0;
}
public Scriptable getScope() {
return scope;
}
+ public String getSourceName() { return sourceName; }
+
+ public int getLineno() { return lineno; }
+
+ public int getOp() { return op; }
+
+ public String getString() { return string; }
+
+ public double getNumber() { return number; }
+
+ public int getTokenno() { return tokenno; }
+
+ public boolean eof() { return hitEOF; }
+
/* return and pop the token from the stream if it matches...
* otherwise return null
*/
@@ -646,11 +672,8 @@ public class TokenStream {
}
public void ungetToken(int tt) {
- if (this.pushbackToken != EOF && tt != ERROR) {
- String message = Context.getMessage2("msg.token.replaces.pushback",
- tokenToString(tt), tokenToString(this.pushbackToken));
- throw new RuntimeException(message);
- }
+ // Can not unread more then one token
+ if (this.pushbackToken != EOF && tt != ERROR) Context.codeBug();
this.pushbackToken = tt;
tokenno--;
}
@@ -674,68 +697,6 @@ public class TokenStream {
return result;
}
- protected static boolean isJSIdentifier(String s) {
- int length = s.length();
-
- if (length == 0 || !Character.isJavaIdentifierStart(s.charAt(0)))
- return false;
-
- for (int i=1; i= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z'));
- }
-
- static boolean isDigit(int c) {
- return (c >= '0' && c <= '9');
- }
-
- static int xDigitToInt(int c) {
- if ('0' <= c && c <= '9') { return c - '0'; }
- if ('a' <= c && c <= 'f') { return c - ('a' - 10); }
- if ('A' <= c && c <= 'F') { return c - ('A' - 10); }
- return -1;
- }
-
- /* As defined in ECMA. jsscan.c uses C isspace() (which allows
- * \v, I think.) note that code in in.read() implicitly accepts
- * '\r' == \u000D as well.
- */
- public static boolean isJSSpace(int c) {
- return (c == '\u0020' || c == '\u0009'
- || c == '\u000C' || c == '\u000B'
- || c == '\u00A0'
- || Character.getType((char)c) == Character.SPACE_SEPARATOR);
- }
-
- public static boolean isJSLineTerminator(int c) {
- return (c == '\n' || c == '\r'
- || c == 0x2028 || c == 0x2029);
- }
-
- private void skipLine() throws IOException {
- // skip to end of line
- int c;
- while ((c = in.read()) != EOF_CHAR && c != '\n') { }
- in.unread();
- }
-
public int getToken() throws IOException {
int c;
tokenno++;
@@ -748,34 +709,37 @@ public class TokenStream {
}
// Eat whitespace, possibly sensitive to newlines.
- do {
- c = in.read();
- if (c == '\n') {
+ for (;;) {
+ c = getChar();
+ if (c == EOF_CHAR) {
+ return EOF;
+ } else if (c == '\n') {
flags &= ~TSF_DIRTYLINE;
- if ((flags & TSF_NEWLINES) != 0)
+ if ((flags & TSF_NEWLINES) != 0) {
break;
+ }
+ } else if (!isJSSpace(c)) {
+ if (c != '-') {
+ flags |= TSF_DIRTYLINE;
+ }
+ break;
}
- } while (isJSSpace(c) || c == '\n');
-
- if (c == EOF_CHAR)
- return EOF;
- if (c != '-' && c != '\n')
- flags |= TSF_DIRTYLINE;
+ }
// identifier/keyword/instanceof?
// watch out for starting with a
boolean identifierStart;
boolean isUnicodeEscapeStart = false;
if (c == '\\') {
- c = in.read();
+ c = getChar();
if (c == 'u') {
identifierStart = true;
isUnicodeEscapeStart = true;
stringBufferTop = 0;
} else {
identifierStart = false;
+ ungetChar(c);
c = '\\';
- in.unread();
}
} else {
identifierStart = Character.isJavaIdentifierStart((char)c);
@@ -797,7 +761,7 @@ public class TokenStream {
// an error here.
int escapeVal = 0;
for (int i = 0; i != 4; ++i) {
- c = in.read();
+ c = getChar();
escapeVal = (escapeVal << 4) | xDigitToInt(c);
// Next check takes care about c < 0 and bad escape
if (escapeVal < 0) { break; }
@@ -809,9 +773,9 @@ public class TokenStream {
addToString(escapeVal);
isUnicodeEscapeStart = false;
} else {
- c = in.read();
+ c = getChar();
if (c == '\\') {
- c = in.read();
+ c = getChar();
if (c == 'u') {
isUnicodeEscapeStart = true;
containsEscape = true;
@@ -820,16 +784,18 @@ public class TokenStream {
return ERROR;
}
} else {
- if (!Character.isJavaIdentifierPart((char)c)) {
+ if (c == EOF_CHAR
+ || !Character.isJavaIdentifierPart((char)c))
+ {
break;
}
addToString(c);
}
}
}
- in.unread();
+ ungetChar(c);
- String str = getStringFromBuffer();
+ String str = getStringFromBuffer();
if (!containsEscape) {
// OPT we shouldn't have to make a string (object!) to
// check if it's a keyword.
@@ -854,21 +820,21 @@ public class TokenStream {
}
}
}
- this.string = str;
+ this.string = (String)allStrings.intern(str);
return NAME;
}
// is it a number?
- if (isDigit(c) || (c == '.' && isDigit(in.peek()))) {
+ if (isDigit(c) || (c == '.' && isDigit(peekChar()))) {
stringBufferTop = 0;
int base = 10;
if (c == '0') {
- c = in.read();
+ c = getChar();
if (c == 'x' || c == 'X') {
base = 16;
- c = in.read();
+ c = getChar();
} else if (isDigit(c)) {
base = 8;
} else {
@@ -879,7 +845,7 @@ public class TokenStream {
if (base == 16) {
while (0 <= xDigitToInt(c)) {
addToString(c);
- c = in.read();
+ c = getChar();
}
} else {
while ('0' <= c && c <= '9') {
@@ -895,7 +861,7 @@ public class TokenStream {
base = 10;
}
addToString(c);
- c = in.read();
+ c = getChar();
}
}
@@ -906,15 +872,15 @@ public class TokenStream {
if (c == '.') {
do {
addToString(c);
- c = in.read();
+ c = getChar();
} while (isDigit(c));
}
if (c == 'e' || c == 'E') {
addToString(c);
- c = in.read();
+ c = getChar();
if (c == '+' || c == '-') {
addToString(c);
- c = in.read();
+ c = getChar();
}
if (!isDigit(c)) {
reportSyntaxError("msg.missing.exponent", null);
@@ -922,11 +888,11 @@ public class TokenStream {
}
do {
addToString(c);
- c = in.read();
+ c = getChar();
} while (isDigit(c));
}
}
- in.unread();
+ ungetChar(c);
String numString = getStringFromBuffer();
double dval;
@@ -959,10 +925,10 @@ public class TokenStream {
int val = 0;
stringBufferTop = 0;
- c = in.read();
+ c = getChar();
strLoop: while (c != quoteChar) {
if (c == '\n' || c == EOF_CHAR) {
- in.unread();
+ ungetChar(c);
reportSyntaxError("msg.unterminated.string.lit", null);
return ERROR;
}
@@ -970,7 +936,7 @@ public class TokenStream {
if (c == '\\') {
// We've hit an escaped character
- c = in.read();
+ c = getChar();
switch (c) {
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
@@ -992,7 +958,7 @@ public class TokenStream {
addToString('u');
int escapeVal = 0;
for (int i = 0; i != 4; ++i) {
- c = in.read();
+ c = getChar();
escapeVal = (escapeVal << 4) | xDigitToInt(c);
if (escapeVal < 0) {
continue strLoop;
@@ -1009,14 +975,14 @@ public class TokenStream {
/* Get 2 hex digits, defaulting to 'x' + literal
* sequence, as above.
*/
- c = in.read();
+ c = getChar();
int escapeVal = xDigitToInt(c);
if (escapeVal < 0) {
addToString('x');
continue strLoop;
} else {
int c1 = c;
- c = in.read();
+ c = getChar();
escapeVal = (escapeVal << 4) | xDigitToInt(c);
if (escapeVal < 0) {
addToString('x');
@@ -1031,27 +997,28 @@ public class TokenStream {
default: if ('0' <= c && c < '8') {
val = c - '0';
- c = in.read();
+ c = getChar();
if ('0' <= c && c < '8') {
val = 8 * val + c - '0';
- c = in.read();
+ c = getChar();
if ('0' <= c && c < '8' && val <= 037) {
// c is 3rd char of octal sequence only if
// the resulting val <= 0377
val = 8 * val + c - '0';
- c = in.read();
+ c = getChar();
}
}
- in.unread();
+ ungetChar(c);
c = val;
}
}
}
addToString(c);
- c = in.read();
+ c = getChar();
}
- this.string = getStringFromBuffer();
+ String str = getStringFromBuffer();
+ this.string = (String)allStrings.intern(str);
return STRING;
}
@@ -1071,9 +1038,9 @@ public class TokenStream {
case '.': return DOT;
case '|':
- if (in.match('|')) {
+ if (matchChar('|')) {
return OR;
- } else if (in.match('=')) {
+ } else if (matchChar('=')) {
this.op = BITOR;
return ASSIGN;
} else {
@@ -1081,7 +1048,7 @@ public class TokenStream {
}
case '^':
- if (in.match('=')) {
+ if (matchChar('=')) {
this.op = BITXOR;
return ASSIGN;
} else {
@@ -1089,9 +1056,9 @@ public class TokenStream {
}
case '&':
- if (in.match('&')) {
+ if (matchChar('&')) {
return AND;
- } else if (in.match('=')) {
+ } else if (matchChar('=')) {
this.op = BITAND;
return ASSIGN;
} else {
@@ -1099,8 +1066,8 @@ public class TokenStream {
}
case '=':
- if (in.match('=')) {
- if (in.match('='))
+ if (matchChar('=')) {
+ if (matchChar('='))
this.op = SHEQ;
else
this.op = EQ;
@@ -1111,8 +1078,8 @@ public class TokenStream {
}
case '!':
- if (in.match('=')) {
- if (in.match('='))
+ if (matchChar('=')) {
+ if (matchChar('='))
this.op = SHNE;
else
this.op = NE;
@@ -1124,18 +1091,18 @@ public class TokenStream {
case '<':
/* NB:treat HTML begin-comment as comment-till-eol */
- if (in.match('!')) {
- if (in.match('-')) {
- if (in.match('-')) {
+ if (matchChar('!')) {
+ if (matchChar('-')) {
+ if (matchChar('-')) {
skipLine();
return getToken(); // in place of 'goto retry'
}
- in.unread();
+ ungetChar('-');
}
- in.unread();
+ ungetChar('!');
}
- if (in.match('<')) {
- if (in.match('=')) {
+ if (matchChar('<')) {
+ if (matchChar('=')) {
this.op = LSH;
return ASSIGN;
} else {
@@ -1143,7 +1110,7 @@ public class TokenStream {
return SHOP;
}
} else {
- if (in.match('=')) {
+ if (matchChar('=')) {
this.op = LE;
return RELOP;
} else {
@@ -1153,9 +1120,9 @@ public class TokenStream {
}
case '>':
- if (in.match('>')) {
- if (in.match('>')) {
- if (in.match('=')) {
+ if (matchChar('>')) {
+ if (matchChar('>')) {
+ if (matchChar('=')) {
this.op = URSH;
return ASSIGN;
} else {
@@ -1163,7 +1130,7 @@ public class TokenStream {
return SHOP;
}
} else {
- if (in.match('=')) {
+ if (matchChar('=')) {
this.op = RSH;
return ASSIGN;
} else {
@@ -1172,7 +1139,7 @@ public class TokenStream {
}
}
} else {
- if (in.match('=')) {
+ if (matchChar('=')) {
this.op = GE;
return RELOP;
} else {
@@ -1182,7 +1149,7 @@ public class TokenStream {
}
case '*':
- if (in.match('=')) {
+ if (matchChar('=')) {
this.op = MUL;
return ASSIGN;
} else {
@@ -1191,13 +1158,13 @@ public class TokenStream {
case '/':
// is it a // comment?
- if (in.match('/')) {
+ if (matchChar('/')) {
skipLine();
return getToken();
}
- if (in.match('*')) {
- while ((c = in.read()) != -1 &&
- !(c == '*' && in.match('/'))) {
+ if (matchChar('*')) {
+ while ((c = getChar()) != -1 &&
+ !(c == '*' && matchChar('/'))) {
; // empty loop body
}
if (c == EOF_CHAR) {
@@ -1210,15 +1177,15 @@ public class TokenStream {
// is it a regexp?
if ((flags & TSF_REGEXP) != 0) {
stringBufferTop = 0;
- while ((c = in.read()) != '/') {
+ while ((c = getChar()) != '/') {
if (c == '\n' || c == EOF_CHAR) {
- in.unread();
+ ungetChar(c);
reportSyntaxError("msg.unterminated.re.lit", null);
return ERROR;
}
if (c == '\\') {
addToString(c);
- c = in.read();
+ c = getChar();
}
addToString(c);
@@ -1226,17 +1193,17 @@ public class TokenStream {
int reEnd = stringBufferTop;
while (true) {
- if (in.match('g'))
+ if (matchChar('g'))
addToString('g');
- else if (in.match('i'))
+ else if (matchChar('i'))
addToString('i');
- else if (in.match('m'))
+ else if (matchChar('m'))
addToString('m');
else
break;
}
- if (isAlpha(in.peek())) {
+ if (isAlpha(peekChar())) {
reportSyntaxError("msg.invalid.re.flag", null);
return ERROR;
}
@@ -1248,7 +1215,7 @@ public class TokenStream {
}
- if (in.match('=')) {
+ if (matchChar('=')) {
this.op = DIV;
return ASSIGN;
} else {
@@ -1257,7 +1224,7 @@ public class TokenStream {
case '%':
this.op = MOD;
- if (in.match('=')) {
+ if (matchChar('=')) {
return ASSIGN;
} else {
return MOD;
@@ -1268,24 +1235,24 @@ public class TokenStream {
return UNARYOP;
case '+':
- if (in.match('=')) {
+ if (matchChar('=')) {
this.op = ADD;
return ASSIGN;
- } else if (in.match('+')) {
+ } else if (matchChar('+')) {
return INC;
} else {
return ADD;
}
case '-':
- if (in.match('=')) {
+ if (matchChar('=')) {
this.op = SUB;
c = ASSIGN;
- } else if (in.match('-')) {
+ } else if (matchChar('-')) {
if (0 == (flags & TSF_DIRTYLINE)) {
// treat HTML end-comment after possible whitespace
// after line start as comment-utill-eol
- if (in.match('>')) {
+ if (matchChar('>')) {
skipLine();
return getToken();
}
@@ -1303,6 +1270,53 @@ public class TokenStream {
}
}
+ private static boolean isAlpha(int c) {
+ // Use 'Z' < 'a'
+ if (c <= 'Z') {
+ return 'A' <= c;
+ } else {
+ return 'a' <= c && c <= 'z';
+ }
+ }
+
+ static boolean isDigit(int c) {
+ return '0' <= c && c <= '9';
+ }
+
+ static int xDigitToInt(int c) {
+ // Use 0..9 < A..Z < a..z
+ if (c <= '9') {
+ c -= '0';
+ if (0 <= c) { return c; }
+ } else if (c <= 'F') {
+ if ('A' <= c) { return c - ('A' - 10); }
+ } else if (c <= 'f') {
+ if ('a' <= c) { return c - ('a' - 10); }
+ }
+ return -1;
+ }
+
+ /* As defined in ECMA. jsscan.c uses C isspace() (which allows
+ * \v, I think.) note that code in getChar() implicitly accepts
+ * '\r' == \u000D as well.
+ */
+ public static boolean isJSSpace(int c) {
+ if (c <= 127) {
+ return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB;
+ } else {
+ return c == 0xA0
+ || Character.getType((char)c) == Character.SPACE_SEPARATOR;
+ }
+ }
+
+ public static boolean isJSLineTerminator(int c) {
+ return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
+ }
+
+ private static boolean isJSFormatChar(int c) {
+ return c > 127 && Character.getType((char)c) == Character.FORMAT;
+ }
+
private String getStringFromBuffer() {
return new String(stringBuffer, 0, stringBufferTop);
}
@@ -1336,19 +1350,161 @@ public class TokenStream {
getLineno(), getLine(), getOffset());
}
- public String getSourceName() { return sourceName; }
- public int getLineno() { return in.getLineno(); }
- public int getOp() { return op; }
- public String getString() { return string; }
- public double getNumber() { return number; }
- public String getLine() { return in.getLine(); }
- public int getOffset() { return in.getOffset(); }
- public int getTokenno() { return tokenno; }
- public boolean eof() { return in.eof(); }
+ private void ungetChar(int c) {
+ // can not unread past across line boundary
+ if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n')
+ Context.codeBug();
+ ungetBuffer[ungetCursor++] = c;
+ }
- // instance variables
- private LineBuffer in;
+ private boolean matchChar(int test) throws IOException {
+ int c = getChar();
+ if (c == test) {
+ return true;
+ } else {
+ ungetChar(c);
+ return false;
+ }
+ }
+ private int peekChar() throws IOException {
+ int c = getChar();
+ ungetChar(c);
+ return c;
+ }
+
+ private int getChar() throws IOException {
+ if (ungetCursor != 0) {
+ return ungetBuffer[--ungetCursor];
+ }
+
+ for(;;) {
+ int c;
+ if (sourceString != null) {
+ if (sourceCursor == sourceEnd) {
+ hitEOF = true;
+ return EOF_CHAR;
+ }
+ c = sourceString.charAt(sourceCursor++);
+ } else {
+ if (sourceCursor == sourceEnd) {
+ if (!fillSourceBuffer()) {
+ hitEOF = true;
+ return EOF_CHAR;
+ }
+ }
+ c = sourceBuffer[sourceCursor++];
+ }
+
+ if (lineEndChar >= 0) {
+ if (lineEndChar == '\r' && c == '\n') {
+ lineEndChar = '\n';
+ continue;
+ }
+ lineEndChar = -1;
+ lineStart = sourceCursor - 1;
+ lineno++;
+ }
+
+ if (c <= 127) {
+ if (c == '\n' || c == '\r') {
+ lineEndChar = c;
+ c = '\n';
+ }
+ } else {
+ if (isJSFormatChar(c)) {
+ continue;
+ }
+ if ((c & EOL_HINT_MASK) == 0 && isJSLineTerminator(c)) {
+ lineEndChar = c;
+ c = '\n';
+ }
+ }
+ return c;
+ }
+ }
+
+ private void skipLine() throws IOException {
+ // skip to end of line
+ int c;
+ while ((c = getChar()) != EOF_CHAR && c != '\n') { }
+ ungetChar(c);
+ }
+
+ public int getOffset() {
+ int n = sourceCursor - lineStart;
+ if (lineEndChar >= 0) { --n; }
+ return n;
+ }
+
+ public String getLine() {
+ if (sourceString != null) {
+ // String case
+ int lineEnd = sourceCursor;
+ if (lineEndChar >= 0) {
+ --lineEnd;
+ } else {
+ for (; lineEnd != sourceEnd; ++lineEnd) {
+ int c = sourceString.charAt(lineEnd);
+ if ((c & EOL_HINT_MASK) == 0 && isJSLineTerminator(c)) {
+ break;
+ }
+ }
+ }
+ return sourceString.substring(lineStart, lineEnd);
+ } else {
+ // Reader case
+ int lineLength = sourceCursor - lineStart;
+ if (lineEndChar >= 0) {
+ --lineLength;
+ } else {
+ // Read until the end of line
+ for (;; ++lineLength) {
+ int i = lineStart + lineLength;
+ if (i == sourceEnd) {
+ try {
+ if (!fillSourceBuffer()) { break; }
+ } catch (IOException ioe) {
+ // ignore it, we're already displaying an error...
+ break;
+ }
+ // i recalculuation as fillSourceBuffer can move saved
+ // line buffer and change lineStart
+ i = lineStart + lineLength;
+ }
+ int c = sourceBuffer[i];
+ if ((c & EOL_HINT_MASK) == 0 && isJSLineTerminator(c)) {
+ break;
+ }
+ }
+ }
+ return new String(sourceBuffer, lineStart, lineLength);
+ }
+ }
+
+ private boolean fillSourceBuffer() throws IOException {
+ if (sourceString != null) Context.codeBug();
+ if (sourceEnd == sourceBuffer.length) {
+ if (lineStart != 0) {
+ System.arraycopy(sourceBuffer, lineStart, sourceBuffer, 0,
+ sourceEnd - lineStart);
+ sourceEnd -= lineStart;
+ sourceCursor -= lineStart;
+ lineStart = 0;
+ } else {
+ char[] tmp = new char[sourceBuffer.length * 2];
+ System.arraycopy(sourceBuffer, 0, tmp, 0, sourceEnd);
+ sourceBuffer = tmp;
+ }
+ }
+ int n = sourceReader.read(sourceBuffer, sourceEnd,
+ sourceBuffer.length - sourceEnd);
+ if (n < 0) {
+ return false;
+ }
+ sourceEnd += n;
+ return true;
+ }
/* for TSF_REGEXP, etc.
* should this be manipulated by gettor/settor functions?
@@ -1374,4 +1530,25 @@ public class TokenStream {
private char[] stringBuffer = new char[128];
private int stringBufferTop;
+ private ObjToIntMap allStrings = new ObjToIntMap(50);
+
+ // Room to backtrace from to < on failed match of the last - in