();
+ mapStatements(stmts, nodeStmts);
+ return stmts;
+ }
+
+ private JsSwitch mapSwitchStatement(Node switchNode) throws JsParserException {
+ SourceInfo info = makeSourceInfo(switchNode);
+ JsSwitch toSwitch = new JsSwitch(info);
+ pushSourceInfo(info);
+
+ // The switch expression.
+ //
+ Node fromSwitchExpr = switchNode.getFirstChild();
+ toSwitch.setExpr(mapExpression(fromSwitchExpr));
+
+ // The members.
+ //
+ Node fromMember = fromSwitchExpr.getNext();
+ while (fromMember != null) {
+ if (fromMember.getType() == TokenStream.CASE) {
+ JsCase toCase = new JsCase(makeSourceInfo(fromMember));
+
+ // Set the case expression. In JS, this can be any expression.
+ //
+ Node fromCaseExpr = fromMember.getFirstChild();
+ toCase.setCaseExpr(mapExpression(fromCaseExpr));
+
+ // Set the case statements.
+ //
+ Node fromCaseBlock = fromCaseExpr.getNext();
+ mapStatements(toCase.getStmts(), fromCaseBlock);
+
+ // Attach the case to the switch.
+ //
+ toSwitch.getCases().add(toCase);
+ } else {
+ // This should be the only default statement.
+ // If more than one is present, we keep the last one.
+ //
+ assert (fromMember.getType() == TokenStream.DEFAULT);
+ JsDefault toDefault = new JsDefault(makeSourceInfo(fromMember));
+
+ // Set the default statements.
+ //
+ Node fromDefaultBlock = fromMember.getFirstChild();
+ mapStatements(toDefault.getStmts(), fromDefaultBlock);
+
+ // Attach the default to the switch.
+ //
+ toSwitch.getCases().add(toDefault);
+ }
+ fromMember = fromMember.getNext();
+ }
+
+ popSourceInfo();
+ return toSwitch;
+ }
+
+ private JsThrow mapThrowStatement(Node throwNode) throws JsParserException {
+ SourceInfo info = makeSourceInfo(throwNode);
+ pushSourceInfo(info);
+
+ // Create, map, and attach.
+ //
+ Node fromExpr = throwNode.getFirstChild();
+ JsThrow toThrow = new JsThrow(info, mapExpression(fromExpr));
+
+ popSourceInfo();
+ return toThrow;
+ }
+
+ private JsTry mapTryStatement(Node tryNode) throws JsParserException {
+ JsTry toTry = new JsTry(makeSourceInfo(tryNode));
+
+ // Map the "try" body.
+ //
+ Node fromTryBody = tryNode.getFirstChild();
+ toTry.setTryBlock(mapBlock(fromTryBody));
+
+ // Map zero or more catch blocks.
+ //
+ Node fromCatchNodes = fromTryBody.getNext();
+ Node fromCatchNode = fromCatchNodes.getFirstChild();
+ while (fromCatchNode != null) {
+ assert (fromCatchNode.getType() == TokenStream.CATCH);
+ // Map the catch variable.
+ //
+ Node fromCatchVarName = fromCatchNode.getFirstChild();
+ JsCatch catchBlock = new JsCatch(makeSourceInfo(fromCatchNode),
+ getScope(), fromCatchVarName.getString());
+
+ // Pre-advance to the next catch block, if any.
+ // We do this here to decide whether or not this is the last one.
+ //
+ fromCatchNode = fromCatchNode.getNext();
+
+ // Map the condition, with a little fixup based on whether or not
+ // this is the last catch block.
+ //
+ Node fromCondition = fromCatchVarName.getNext();
+ JsExpression toCondition = mapExpression(fromCondition);
+ catchBlock.setCondition(toCondition);
+ if (fromCatchNode == null) {
+ if (toCondition instanceof JsBooleanLiteral) {
+ if (((JsBooleanLiteral) toCondition).getValue()) {
+ // Actually, this is an unconditional catch block.
+ // Indicate that by nulling the condition.
+ //
+ catchBlock.setCondition(null);
+ }
+ }
+ }
+
+ // Map the catch body.
+ //
+ Node fromCatchBody = fromCondition.getNext();
+ pushScope(catchBlock.getScope(), catchBlock.getSourceInfo());
+ catchBlock.setBody(mapBlock(fromCatchBody));
+ popScope();
+
+ // Attach it.
+ //
+ toTry.getCatches().add(catchBlock);
+ }
+
+ Node fromFinallyNode = fromCatchNodes.getNext();
+ if (fromFinallyNode != null) {
+ toTry.setFinallyBlock(mapBlock(fromFinallyNode));
+ }
+
+ return toTry;
+ }
+
+ private JsExpression mapUnaryVariant(Node unOp) throws JsParserException {
+ switch (unOp.getIntDatum()) {
+ case TokenStream.SUB:
+ return mapPrefixOperation(JsUnaryOperator.NEG, unOp);
+
+ case TokenStream.NOT:
+ return mapPrefixOperation(JsUnaryOperator.NOT, unOp);
+
+ case TokenStream.BITNOT:
+ return mapPrefixOperation(JsUnaryOperator.BIT_NOT, unOp);
+
+ case TokenStream.TYPEOF:
+ return mapPrefixOperation(JsUnaryOperator.TYPEOF, unOp);
+
+ case TokenStream.ADD:
+ if (unOp.getFirstChild().getType() != TokenStream.NUMBER) {
+ return mapPrefixOperation(JsUnaryOperator.POS, unOp);
+ } else {
+ // Pretend we didn't see it.
+ return mapExpression(unOp.getFirstChild());
+ }
+
+ case TokenStream.VOID:
+ return mapPrefixOperation(JsUnaryOperator.VOID, unOp);
+
+ default:
+ throw createParserException(
+ "Unknown unary operator variant: " + unOp.getIntDatum(), unOp);
+ }
+ }
+
+ private JsVars mapVar(Node varNode) throws JsParserException {
+ SourceInfo info = makeSourceInfo(varNode);
+ pushSourceInfo(info);
+ JsVars toVars = new JsVars(info);
+ Node fromVar = varNode.getFirstChild();
+ while (fromVar != null) {
+ // Use a conservative name allocation strategy that allocates all names
+ // from the function's scope, even the names of properties in field
+ // literals.
+ //
+ String fromName = fromVar.getString();
+ JsName toName = getScope().declareName(fromName);
+ JsVars.JsVar toVar = new JsVars.JsVar(makeSourceInfo(fromVar), toName);
+
+ Node fromInit = fromVar.getFirstChild();
+ if (fromInit != null) {
+ JsExpression toInit = mapExpression(fromInit);
+ toVar.setInitExpr(toInit);
+ }
+ toVars.add(toVar);
+
+ fromVar = fromVar.getNext();
+ }
+
+ popSourceInfo();
+ return toVars;
+ }
+
+ private JsNode mapWithStatement(Node withNode) throws JsParserException {
+ // The "with" statement is unsupported because it introduces ambiguity
+ // related to whether or not a name is obfuscatable that we cannot resolve
+ // statically. This is modified in our copy of the Rhino Parser to provide
+ // detailed source & line info. So, this method should never actually be
+ // called.
+ //
+ throw createParserException("Internal error: unexpected token 'with'",
+ withNode);
+ }
+
+ private void popScope() {
+ scopeStack.pop();
+ sourceInfoStack.pop();
+ }
+
+ private void popSourceInfo() {
+ sourceInfoStack.pop();
+ }
+
+ private void pushScope(JsScope scope, SourceInfo sourceInfo) {
+ scopeStack.push(scope);
+ sourceInfoStack.push(sourceInfo);
+ }
+
+ /**
+ * This should be called when processing any Rhino statement Node that has
+ * line number data so that enclosed expressions will have a useful source
+ * location.
+ *
+ * @see Node#hasLineno
+ */
+ private void pushSourceInfo(SourceInfo sourceInfo) {
+ assert sourceInfo.getStartLine() >= 0 : "Bad SourceInfo line number";
+ sourceInfoStack.push(sourceInfo);
+ }
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/JsParserException.java b/js/js.parser/src/com/google/gwt/dev/js/JsParserException.java
new file mode 100644
index 00000000000..1c42668e9fe
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/JsParserException.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.js;
+
+/**
+ * Indicates inability to parse JavaScript source.
+ */
+public class JsParserException extends Exception {
+
+ /**
+ * Represents the location of a parser exception.
+ */
+ public static class SourceDetail {
+ private final String fileName;
+ private final int line;
+ private final int lineOffset;
+ private final String lineSource;
+
+ public SourceDetail(int line, String lineSource, int lineOffset,
+ String fileName) {
+ this.line = line;
+ this.lineSource = lineSource;
+ this.lineOffset = lineOffset;
+ this.fileName = fileName;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ public int getLineOffset() {
+ return lineOffset;
+ }
+
+ public String getLineSource() {
+ return lineSource;
+ }
+ }
+
+ private static String createMessageWithDetail(String msg,
+ SourceDetail sourceDetail) {
+ if (sourceDetail == null) {
+ return msg;
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.append(sourceDetail.getFileName());
+ sb.append('(');
+ sb.append(sourceDetail.getLine());
+ sb.append(')');
+ sb.append(": ");
+ sb.append(msg);
+ if (sourceDetail.getLineSource() != null) {
+ sb.append("\n> ");
+ sb.append(sourceDetail.getLineSource());
+ sb.append("\n> ");
+ for (int i = 0, n = sourceDetail.getLineOffset(); i < n; ++i) {
+ sb.append('-');
+ }
+ sb.append('^');
+ }
+ return sb.toString();
+ }
+
+ private final SourceDetail sourceDetail;
+
+ public JsParserException(String msg) {
+ this(msg, null);
+ }
+
+ public JsParserException(String msg, int line, String lineSource,
+ int lineOffset, String fileName) {
+ this(msg, new SourceDetail(line, lineSource, lineOffset, fileName));
+ }
+
+ public JsParserException(String msg, SourceDetail sourceDetail) {
+ super(createMessageWithDetail(msg, sourceDetail));
+ this.sourceDetail = sourceDetail;
+ }
+
+ /**
+ * Provides additional source detail in some cases.
+ *
+ * @return additional detail regarding the error, or null if no
+ * additional detail is available
+ */
+ public SourceDetail getSourceDetail() {
+ return sourceDetail;
+ }
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/UncheckedJsParserException.java b/js/js.parser/src/com/google/gwt/dev/js/UncheckedJsParserException.java
new file mode 100644
index 00000000000..a88462c8bb4
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/UncheckedJsParserException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.js;
+
+// An unchecked wrapper exception to interop with Rhino.
+class UncheckedJsParserException extends RuntimeException {
+
+ private final JsParserException parserException;
+
+ public UncheckedJsParserException(JsParserException parserException) {
+ this.parserException = parserException;
+ }
+
+ public JsParserException getParserException() {
+ return parserException;
+ }
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/BinaryDigitReader.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/BinaryDigitReader.java
new file mode 100644
index 00000000000..ca7d7b6459f
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/BinaryDigitReader.java
@@ -0,0 +1,76 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Waldemar Horwat
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+final class BinaryDigitReader {
+ int lgBase; // Logarithm of base of number
+ int digit; // Current digit value in radix given by base
+ int digitPos; // Bit position of last bit extracted from digit
+ String digits; // String containing the digits
+ int start; // Index of the first remaining digit
+ int end; // Index past the last remaining digit
+
+ BinaryDigitReader(int base, String digits, int start, int end) {
+ lgBase = 0;
+ while (base != 1) {
+ lgBase++;
+ base >>= 1;
+ }
+ digitPos = 0;
+ this.digits = digits;
+ this.start = start;
+ this.end = end;
+ }
+
+ /* Return the next binary digit from the number or -1 if done */
+ int getNextBinaryDigit()
+ {
+ if (digitPos == 0) {
+ if (start == end)
+ return -1;
+
+ char c = digits.charAt(start++);
+ if ('0' <= c && c <= '9')
+ digit = c - '0';
+ else if ('a' <= c && c <= 'z')
+ digit = c - 'a' + 10;
+ else digit = c - 'A' + 10;
+ digitPos = lgBase;
+ }
+ return digit >> --digitPos & 1;
+ }
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/COPYING b/js/js.parser/src/com/google/gwt/dev/js/rhino/COPYING
new file mode 100644
index 00000000000..96114e4d9c1
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/COPYING
@@ -0,0 +1,36 @@
+The files in this package are taken from mozilla's Rhino project.
+See http://www.mozilla.org/rhino/
+
+The files modified from Rhino 1.5R3
+(ftp://ftp.mozilla.org/pub/js/rhino15R3.zip).
+
+--
+
+The contents of this package are subject to the Netscape Public
+License Version 1.1 (the "License"); you may not use this file
+except in compliance with the License. You may obtain a copy of
+the License at http://www.mozilla.org/NPL/
+
+Software distributed under the License is distributed on an "AS
+IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+implied. See the License for the specific language governing
+rights and limitations under the License.
+
+The Original Code is Rhino code, released
+May 6, 1999.
+
+The Initial Developer of the Original Code is Netscape
+Communications Corporation. Portions created by Netscape are
+Copyright (C) 1997-2000 Netscape Communications Corporation. All
+Rights Reserved.
+
+Alternatively, the contents of this file may be used under the
+terms of the GNU Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.
+If you wish to allow use of your version of this file only
+under the terms of the GPL and not to allow others to use your
+version of this file under the NPL, indicate your decision by
+deleting the provisions above and replace them with the notice
+and other provisions required by the GPL. If you do not delete
+the provisions above, a recipient may use your version of this
+file under either the NPL or the GPL.
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/Context.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/Context.java
new file mode 100644
index 00000000000..ec41a9204ff
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/Context.java
@@ -0,0 +1,802 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Patrick Beard
+ * Norris Boyd
+ * Igor Bukanov
+ * Brendan Eich
+ * Roger Lawrence
+ * Mike McCabe
+ * Ian D. Stewart
+ * Andi Vajda
+ * Andrew Wason
+ * Kemal Bayram
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+// API class
+
+package com.google.gwt.dev.js.rhino;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.lang.reflect.Method;
+import java.text.MessageFormat;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * This class represents the runtime context of an executing script.
+ *
+ * Before executing a script, an instance of Context must be created
+ * and associated with the thread that will be executing the script.
+ * The Context will be used to store information about the executing
+ * of the script such as the call stack. Contexts are associated with
+ * the current thread using the enter() method.
+ *
+ * The behavior of the execution engine may be altered through methods
+ * such as setErrorReporter.
+ *
+ * Different forms of script execution are supported. Scripts may be
+ * evaluated from the source directly, or first compiled and then later
+ * executed. Interactive execution is also supported.
+ *
+ * Some aspects of script execution, such as type conversions and
+ * object creation, may be accessed directly through methods of
+ * Context.
+ *
+ * @see Scriptable
+ * @author Norris Boyd
+ * @author Brendan Eich
+ */
+
+public class Context {
+ public static final String languageVersionProperty = "language version";
+ public static final String errorReporterProperty = "error reporter";
+
+ /**
+ * Create a new Context.
+ *
+ * Note that the Context must be associated with a thread before
+ * it can be used to execute a script.
+ *
+ * @see org.mozilla.javascript.Context#enter
+ */
+ public Context() {
+ setLanguageVersion(VERSION_DEFAULT);
+ }
+
+ /**
+ * Get a context associated with the current thread, creating
+ * one if need be.
+ *
+ * The Context stores the execution state of the JavaScript
+ * engine, so it is required that the context be entered
+ * before execution may begin. Once a thread has entered
+ * a Context, then getCurrentContext() may be called to find
+ * the context that is associated with the current thread.
+ *
+ * Calling enter() will
+ * return either the Context currently associated with the
+ * thread, or will create a new context and associate it
+ * with the current thread. Each call to enter()
+ * must have a matching call to exit(). For example,
+ *
+ * Context cx = Context.enter();
+ * try {
+ * ...
+ * cx.evaluateString(...);
+ * }
+ * finally { Context.exit(); }
+ *
+ * @return a Context associated with the current thread
+ * @see org.mozilla.javascript.Context#getCurrentContext
+ * @see org.mozilla.javascript.Context#exit
+ */
+ public static Context enter() {
+ return enter(null);
+ }
+
+ /**
+ * Get a Context associated with the current thread, using
+ * the given Context if need be.
+ *
+ * The same as enter() except that cx
+ * is associated with the current thread and returned if
+ * the current thread has no associated context and cx
+ * is not associated with any other thread.
+ * @param cx a Context to associate with the thread if possible
+ * @return a Context associated with the current thread
+ */
+ public static Context enter(Context cx) {
+
+ Context old = getCurrentContext();
+
+ if (cx == null) {
+ if (old != null) {
+ cx = old;
+ } else {
+ cx = new Context();
+ setThreadContext(cx);
+ }
+ } else {
+ if (cx.enterCount != 0) {
+ // The suplied context must be the context for
+ // the current thread if it is already entered
+ if (cx != old) {
+ throw new RuntimeException
+ ("Cannot enter Context active on another thread");
+ }
+ } else {
+ if (old != null) {
+ cx = old;
+ } else {
+ setThreadContext(cx);
+ }
+ }
+ }
+
+ ++cx.enterCount;
+
+ return cx;
+ }
+
+ /**
+ * Exit a block of code requiring a Context.
+ *
+ * Calling exit() will remove the association between
+ * the current thread and a Context if the prior call to
+ * enter() on this thread newly associated a Context
+ * with this thread.
+ * Once the current thread no longer has an associated Context,
+ * it cannot be used to execute JavaScript until it is again associated
+ * with a Context.
+ *
+ * @see org.mozilla.javascript.Context#enter
+ */
+ public static void exit() {
+ boolean released = false;
+ Context cx = getCurrentContext();
+ if (cx == null) {
+ throw new RuntimeException
+ ("Calling Context.exit without previous Context.enter");
+ }
+ if (Context.check && cx.enterCount < 1) Context.codeBug();
+ --cx.enterCount;
+ if (cx.enterCount == 0) {
+ released = true;
+ setThreadContext(null);
+ }
+ }
+
+ /**
+ * Get the current Context.
+ *
+ * The current Context is per-thread; this method looks up
+ * the Context associated with the current thread.
+ *
+ * @return the Context associated with the current thread, or
+ * null if no context is associated with the current
+ * thread.
+ * @see org.mozilla.javascript.Context#enter
+ * @see org.mozilla.javascript.Context#exit
+ */
+ public static Context getCurrentContext() {
+ if (threadLocalCx != null) {
+ try {
+ return (Context)threadLocalGet.invoke(threadLocalCx, (Object[]) null);
+ } catch (Exception ex) { }
+ }
+ Thread t = Thread.currentThread();
+ return (Context) threadContexts.get(t);
+ }
+
+ private static void setThreadContext(Context cx) {
+ if (threadLocalCx != null) {
+ try {
+ threadLocalSet.invoke(threadLocalCx, new Object[] { cx });
+ return;
+ } catch (Exception ex) { }
+ }
+ Thread t = Thread.currentThread();
+ if (cx != null) {
+ threadContexts.put(t, cx);
+ } else {
+ threadContexts.remove(t);
+ }
+ }
+
+ /**
+ * Language versions
+ *
+ * All integral values are reserved for future version numbers.
+ */
+
+ /**
+ * The unknown version.
+ */
+ public static final int VERSION_UNKNOWN = -1;
+
+ /**
+ * The default version.
+ */
+ public static final int VERSION_DEFAULT = 0;
+
+ /**
+ * JavaScript 1.0
+ */
+ public static final int VERSION_1_0 = 100;
+
+ /**
+ * JavaScript 1.1
+ */
+ public static final int VERSION_1_1 = 110;
+
+ /**
+ * JavaScript 1.2
+ */
+ public static final int VERSION_1_2 = 120;
+
+ /**
+ * JavaScript 1.3
+ */
+ public static final int VERSION_1_3 = 130;
+
+ /**
+ * JavaScript 1.4
+ */
+ public static final int VERSION_1_4 = 140;
+
+ /**
+ * JavaScript 1.5
+ */
+ public static final int VERSION_1_5 = 150;
+
+ /**
+ * Get the current language version.
+ *
+ * The language version number affects JavaScript semantics as detailed
+ * in the overview documentation.
+ *
+ * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
+ */
+ public int getLanguageVersion() {
+ return version;
+ }
+
+ /**
+ * Set the language version.
+ *
+ *
+ * Setting the language version will affect functions and scripts compiled
+ * subsequently. See the overview documentation for version-specific
+ * behavior.
+ *
+ * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
+ */
+ public void setLanguageVersion(int version) {
+ this.version = version;
+ }
+
+ /**
+ * Get the implementation version.
+ *
+ *
+ * The implementation version is of the form
+ *
+ * "name langVer release relNum date"
+ *
+ * where name is the name of the product, langVer is
+ * the language version, relNum is the release number, and
+ * date is the release date for that specific
+ * release in the form "yyyy mm dd".
+ *
+ * @return a string that encodes the product, language version, release
+ * number, and date.
+ */
+ public String getImplementationVersion() {
+ return "Rhino 1.5 release 4.1 2003 04 21";
+ }
+
+ /**
+ * Get the current error reporter.
+ *
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public ErrorReporter getErrorReporter() {
+ return errorReporter;
+ }
+
+ /**
+ * Change the current error reporter.
+ *
+ * @return the previous error reporter
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public ErrorReporter setErrorReporter(ErrorReporter reporter) {
+ errorReporter = reporter;
+ return reporter;
+ }
+
+ /**
+ * Get the current locale. Returns the default locale if none has
+ * been set.
+ *
+ * @see java.util.Locale
+ */
+
+ public Locale getLocale() {
+ if (locale == null)
+ locale = Locale.getDefault();
+ return locale;
+ }
+
+ /**
+ * Set the current locale.
+ *
+ * @see java.util.Locale
+ */
+ public Locale setLocale(Locale loc) {
+ Locale result = locale;
+ locale = loc;
+ return result;
+ }
+
+ /**
+ * Notify any registered listeners that a bounded property has changed
+ * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
+ * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
+ * @see java.beans.PropertyChangeListener
+ * @see java.beans.PropertyChangeEvent
+ * @param property the bound property
+ * @param oldValue the old value
+ * @param newVale the new value
+ */
+ void firePropertyChange(String property, Object oldValue,
+ Object newValue)
+ {
+ Object[] array = listeners;
+ if (array != null) {
+ firePropertyChangeImpl(array, property, oldValue, newValue);
+ }
+ }
+
+ private void firePropertyChangeImpl(Object[] array, String property,
+ Object oldValue, Object newValue)
+ {
+ for (int i = array.length; i-- != 0;) {
+ Object obj = array[i];
+ if (obj instanceof PropertyChangeListener) {
+ PropertyChangeListener l = (PropertyChangeListener)obj;
+ l.propertyChange(new PropertyChangeEvent(
+ this, property, oldValue, newValue));
+ }
+ }
+ }
+
+ /**
+ * Report a warning using the error reporter for the current thread.
+ *
+ * @param message the warning message to report
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public static void reportWarning(String message, String sourceName,
+ int lineno, String lineSource,
+ int lineOffset)
+ {
+ Context cx = Context.getContext();
+ cx.getErrorReporter().warning(message, sourceName, lineno,
+ lineSource, lineOffset);
+ }
+
+ /**
+ * Report a warning using the error reporter for the current thread.
+ *
+ * @param message the warning message to report
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ /*
+ public static void reportWarning(String message) {
+ int[] linep = { 0 };
+ String filename = getSourcePositionFromStack(linep);
+ Context.reportWarning(message, filename, linep[0], null, 0);
+ }
+ */
+
+ /**
+ * Report an error using the error reporter for the current thread.
+ *
+ * @param message the error message to report
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public static void reportError(String message, String sourceName,
+ int lineno, String lineSource,
+ int lineOffset)
+ {
+ Context cx = getCurrentContext();
+ if (cx != null) {
+ cx.errorCount++;
+ cx.getErrorReporter().error(message, sourceName, lineno,
+ lineSource, lineOffset);
+ } else {
+ throw new EvaluatorException(message);
+ }
+ }
+
+ /**
+ * Report an error using the error reporter for the current thread.
+ *
+ * @param message the error message to report
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ /*
+ public static void reportError(String message) {
+ int[] linep = { 0 };
+ String filename = getSourcePositionFromStack(linep);
+ Context.reportError(message, filename, linep[0], null, 0);
+ }
+ */
+
+ /**
+ * Report a runtime error using the error reporter for the current thread.
+ *
+ * @param message the error message to report
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ * @return a runtime exception that will be thrown to terminate the
+ * execution of the script
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ /*
+ public static EvaluatorException reportRuntimeError(String message,
+ String sourceName,
+ int lineno,
+ String lineSource,
+ int lineOffset)
+ {
+ Context cx = getCurrentContext();
+ if (cx != null) {
+ cx.errorCount++;
+ return cx.getErrorReporter().
+ runtimeError(message, sourceName, lineno,
+ lineSource, lineOffset);
+ } else {
+ throw new EvaluatorException(message);
+ }
+ }
+
+ static EvaluatorException reportRuntimeError0(String messageId) {
+ return reportRuntimeError(getMessage0(messageId));
+ }
+
+ static EvaluatorException reportRuntimeError1
+ (String messageId, Object arg1)
+ {
+ return reportRuntimeError(getMessage1(messageId, arg1));
+ }
+
+ static EvaluatorException reportRuntimeError2
+ (String messageId, Object arg1, Object arg2)
+ {
+ return reportRuntimeError(getMessage2(messageId, arg1, arg2));
+ }
+
+ static EvaluatorException reportRuntimeError3
+ (String messageId, Object arg1, Object arg2, Object arg3)
+ {
+ return reportRuntimeError(getMessage3(messageId, arg1, arg2, arg3));
+ }
+ */
+
+ /**
+ * Report a runtime error using the error reporter for the current thread.
+ *
+ * @param message the error message to report
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ /*
+ public static EvaluatorException reportRuntimeError(String message) {
+ int[] linep = { 0 };
+ String filename = getSourcePositionFromStack(linep);
+ return Context.reportRuntimeError(message, filename, linep[0], null, 0);
+ }
+ */
+
+ /**
+ * Get a value corresponding to a key.
+ *
+ * Since the Context is associated with a thread it can be
+ * used to maintain values that can be later retrieved using
+ * the current thread.
+ *
+ * Note that the values are maintained with the Context, so
+ * if the Context is disassociated from the thread the values
+ * cannot be retreived. Also, if private data is to be maintained
+ * in this manner the key should be a java.lang.Object
+ * whose reference is not divulged to untrusted code.
+ * @param key the key used to lookup the value
+ * @return a value previously stored using putThreadLocal.
+ */
+ public final Object getThreadLocal(Object key) {
+ if (hashtable == null)
+ return null;
+ return hashtable.get(key);
+ }
+
+ /**
+ * Put a value that can later be retrieved using a given key.
+ *
+ * @param key the key used to index the value
+ * @param value the value to save
+ */
+ public void putThreadLocal(Object key, Object value) {
+ if (hashtable == null)
+ hashtable = new Hashtable();
+ hashtable.put(key, value);
+ }
+
+ /**
+ * Remove values from thread-local storage.
+ * @param key the key for the entry to remove.
+ * @since 1.5 release 2
+ */
+ public void removeThreadLocal(Object key) {
+ if (hashtable == null)
+ return;
+ hashtable.remove(key);
+ }
+
+ /**
+ * Return whether functions are compiled by this context using
+ * dynamic scope.
+ *
+ * If functions are compiled with dynamic scope, then they execute
+ * in the scope of their caller, rather than in their parent scope.
+ * This is useful for sharing functions across multiple scopes.
+ * @since 1.5 Release 1
+ */
+ public final boolean hasCompileFunctionsWithDynamicScope() {
+ return compileFunctionsWithDynamicScopeFlag;
+ }
+
+ /**
+ * Set whether functions compiled by this context should use
+ * dynamic scope.
+ *
+ * @param flag if true, compile functions with dynamic scope
+ * @since 1.5 Release 1
+ */
+ public void setCompileFunctionsWithDynamicScope(boolean flag) {
+ compileFunctionsWithDynamicScopeFlag = flag;
+ }
+
+ /**
+ * if hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true,
+ * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000
+ * in deviation with Ecma B.2.4
+ */
+ public static final int FEATURE_NON_ECMA_GET_YEAR = 1;
+
+ /**
+ * if hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns true,
+ * allow 'function (...) { ... }' to be syntax sugar for
+ * ' = function(...) { ... }', when
+ * is not simply identifier.
+ * See Ecma-262, section 11.2 for definition of
+ */
+ public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2;
+
+ /**
+ * if hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true,
+ * treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary
+ * identifiers but warn about this usage
+ */
+ public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3;
+
+ /**
+ * if hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true,
+ * calling toString on JS objects gives JS source with code to create an
+ * object with all enumeratable fields of the original object instead of
+ * printing "[object ]".
+ * By default {@link #hasFeature(int)} returns true only if
+ * the current JS version is set to {@link #VERSION_1_2}.
+ */
+ public static final int FEATURE_TO_STRING_AS_SOURCE = 4;
+
+ /**
+ * Controls certain aspects of script semantics.
+ * Should be overwritten to alter default behavior.
+ * @param featureIndex feature index to check
+ * @return true if the featureIndex feature is turned on
+ * @see #FEATURE_NON_ECMA_GET_YEAR
+ * @see #FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME
+ * @see #FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER
+ * @see #FEATURE_TO_STRING_AS_SOURCE
+ */
+ public boolean hasFeature(int featureIndex) {
+ switch (featureIndex) {
+ case FEATURE_NON_ECMA_GET_YEAR:
+ /*
+ * During the great date rewrite of 1.3, we tried to track the
+ * evolving ECMA standard, which then had a definition of
+ * getYear which always subtracted 1900. Which we
+ * implemented, not realizing that it was incompatible with
+ * the old behavior... now, rather than thrash the behavior
+ * yet again, we've decided to leave it with the - 1900
+ * behavior and point people to the getFullYear method. But
+ * we try to protect existing scripts that have specified a
+ * version...
+ */
+ return (version == Context.VERSION_1_0
+ || version == Context.VERSION_1_1
+ || version == Context.VERSION_1_2);
+
+ case FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME:
+ return false;
+
+ case FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER:
+ return false;
+
+ case FEATURE_TO_STRING_AS_SOURCE:
+ return version == VERSION_1_2;
+ }
+ // It is a bug to call the method with unknown featureIndex
+ throw new IllegalArgumentException();
+ }
+
+ /********** end of API **********/
+
+ static String getMessage0(String messageId) {
+ return getMessage(messageId, null);
+ }
+
+ static String getMessage1(String messageId, Object arg1) {
+ Object[] arguments = {arg1};
+ return getMessage(messageId, arguments);
+ }
+
+ static String getMessage2(String messageId, Object arg1, Object arg2) {
+ Object[] arguments = {arg1, arg2};
+ return getMessage(messageId, arguments);
+ }
+
+ static String getMessage3
+ (String messageId, Object arg1, Object arg2, Object arg3) {
+ Object[] arguments = {arg1, arg2, arg3};
+ return getMessage(messageId, arguments);
+ }
+ /**
+ * Internal method that reports an error for missing calls to
+ * enter().
+ */
+ static Context getContext() {
+ Context cx = getCurrentContext();
+ if (cx == null) {
+ throw new RuntimeException(
+ "No Context associated with current Thread");
+ }
+ return cx;
+ }
+
+ /* OPT there's a noticable delay for the first error! Maybe it'd
+ * make sense to use a ListResourceBundle instead of a properties
+ * file to avoid (synchronized) text parsing.
+ */
+ // bruce: removed referenced to the initial "java" package name
+ // that used to be there due to a build artifact
+ static final String defaultResource =
+ "com.google.gwt.dev.js.rhino.Messages";
+
+
+ static String getMessage(String messageId, Object[] arguments) {
+ Context cx = getCurrentContext();
+ Locale locale = cx != null ? cx.getLocale() : Locale.getDefault();
+
+ // ResourceBundle does cacheing.
+ ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale);
+
+ String formatString;
+ try {
+ formatString = rb.getString(messageId);
+ } catch (java.util.MissingResourceException mre) {
+ throw new RuntimeException
+ ("no message resource found for message property "+ messageId);
+ }
+
+ /*
+ * It's OK to format the string, even if 'arguments' is null;
+ * we need to format it anyway, to make double ''s collapse to
+ * single 's.
+ */
+ // TODO: MessageFormat is not available on pJava
+ MessageFormat formatter = new MessageFormat(formatString);
+ return formatter.format(arguments);
+ }
+
+ // debug flags
+ static final boolean printTrees = true;
+ static final boolean printICode = true;
+
+ final boolean isVersionECMA1() {
+ return version == VERSION_DEFAULT || version >= VERSION_1_3;
+ }
+
+
+// Rudimentary support for Design-by-Contract
+ static void codeBug() {
+ throw new RuntimeException("FAILED ASSERTION");
+ }
+
+ static final boolean check = true;
+
+ private static Hashtable threadContexts = new Hashtable(11);
+ private static Object threadLocalCx;
+ private static Method threadLocalGet;
+ private static Method threadLocalSet;
+
+ int version;
+ int errorCount;
+
+ private ErrorReporter errorReporter;
+ private Locale locale;
+ private boolean generatingDebug;
+ private boolean generatingDebugChanged;
+ private boolean generatingSource=true;
+ private boolean compileFunctionsWithDynamicScopeFlag;
+ private int enterCount;
+ private Object[] listeners;
+ private Hashtable hashtable;
+ private ClassLoader applicationClassLoader;
+
+ /**
+ * This is the list of names of objects forcing the creation of
+ * function activation records.
+ */
+ private Hashtable activationNames;
+
+ // For instruction counting (interpreter only)
+ int instructionCount;
+ int instructionThreshold;
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/DToA.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/DToA.java
new file mode 100644
index 00000000000..43500db51bb
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/DToA.java
@@ -0,0 +1,1216 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Waldemar Horwat
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+package com.google.gwt.dev.js.rhino;
+
+import java.math.BigInteger;
+
+class DToA {
+
+
+/* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce,
+ * which occurs when printing -5e-324 in binary. We could compute a better estimate of the size of
+ * the output string and malloc fewer bytes depending on d and base, but why bother? */
+
+ static final int DTOBASESTR_BUFFER_SIZE = 1078;
+
+ static char BASEDIGIT(int digit) {
+ return (char)((digit >= 10) ? 'a' - 10 + digit : '0' + digit);
+ }
+
+ static final int
+ DTOSTR_STANDARD = 0, /* Either fixed or exponential format; round-trip */
+ DTOSTR_STANDARD_EXPONENTIAL = 1, /* Always exponential format; round-trip */
+ DTOSTR_FIXED = 2, /* Round to digits after the decimal point; exponential if number is large */
+ DTOSTR_EXPONENTIAL = 3, /* Always exponential format; significant digits */
+ DTOSTR_PRECISION = 4; /* Either fixed or exponential format; significant digits */
+
+
+ static final int Frac_mask = 0xfffff;
+ static final int Exp_shift = 20;
+ static final int Exp_msk1 = 0x100000;
+ static final int Bias = 1023;
+ static final int P = 53;
+
+ static final int Exp_shift1 = 20;
+ static final int Exp_mask = 0x7ff00000;
+ static final int Bndry_mask = 0xfffff;
+ static final int Log2P = 1;
+
+ static final int Sign_bit = 0x80000000;
+ static final int Exp_11 = 0x3ff00000;
+ static final int Ten_pmax = 22;
+ static final int Quick_max = 14;
+ static final int Bletch = 0x10;
+ static final int Frac_mask1 = 0xfffff;
+ static final int Int_max = 14;
+ static final int n_bigtens = 5;
+
+
+ static final double tens[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+ };
+
+ static final double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
+
+ static int lo0bits(int y)
+ {
+ int k;
+ int x = y;
+
+ if ((x & 7) != 0) {
+ if ((x & 1) != 0)
+ return 0;
+ if ((x & 2) != 0) {
+ return 1;
+ }
+ return 2;
+ }
+ k = 0;
+ if ((x & 0xffff) == 0) {
+ k = 16;
+ x >>>= 16;
+ }
+ if ((x & 0xff) == 0) {
+ k += 8;
+ x >>>= 8;
+ }
+ if ((x & 0xf) == 0) {
+ k += 4;
+ x >>>= 4;
+ }
+ if ((x & 0x3) == 0) {
+ k += 2;
+ x >>>= 2;
+ }
+ if ((x & 1) == 0) {
+ k++;
+ x >>>= 1;
+ if ((x & 1) == 0)
+ return 32;
+ }
+ return k;
+ }
+
+ /* Return the number (0 through 32) of most significant zero bits in x. */
+ static int hi0bits(int x)
+ {
+ int k = 0;
+
+ if ((x & 0xffff0000) == 0) {
+ k = 16;
+ x <<= 16;
+ }
+ if ((x & 0xff000000) == 0) {
+ k += 8;
+ x <<= 8;
+ }
+ if ((x & 0xf0000000) == 0) {
+ k += 4;
+ x <<= 4;
+ }
+ if ((x & 0xc0000000) == 0) {
+ k += 2;
+ x <<= 2;
+ }
+ if ((x & 0x80000000) == 0) {
+ k++;
+ if ((x & 0x40000000) == 0)
+ return 32;
+ }
+ return k;
+ }
+
+ static void stuffBits(byte bits[], int offset, int val)
+ {
+ bits[offset] = (byte)(val >> 24);
+ bits[offset + 1] = (byte)(val >> 16);
+ bits[offset + 2] = (byte)(val >> 8);
+ bits[offset + 3] = (byte)(val);
+ }
+
+ /* Convert d into the form b*2^e, where b is an odd integer. b is the returned
+ * Bigint and e is the returned binary exponent. Return the number of significant
+ * bits in b in bits. d must be finite and nonzero. */
+ static BigInteger d2b(double d, int[] e, int[] bits)
+ {
+ byte dbl_bits[];
+ int i, k, y, z, de;
+ long dBits = Double.doubleToLongBits(d);
+ int d0 = (int)(dBits >>> 32);
+ int d1 = (int)(dBits);
+
+ z = d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+
+ if ((de = (int)(d0 >>> Exp_shift)) != 0)
+ z |= Exp_msk1;
+
+ if ((y = d1) != 0) {
+ dbl_bits = new byte[8];
+ k = lo0bits(y);
+ y >>>= k;
+ if (k != 0) {
+ stuffBits(dbl_bits, 4, y | z << (32 - k));
+ z >>= k;
+ }
+ else
+ stuffBits(dbl_bits, 4, y);
+ stuffBits(dbl_bits, 0, z);
+ i = (z != 0) ? 2 : 1;
+ }
+ else {
+ // JS_ASSERT(z);
+ dbl_bits = new byte[4];
+ k = lo0bits(z);
+ z >>>= k;
+ stuffBits(dbl_bits, 0, z);
+ k += 32;
+ i = 1;
+ }
+ if (de != 0) {
+ e[0] = de - Bias - (P-1) + k;
+ bits[0] = P - k;
+ }
+ else {
+ e[0] = de - Bias - (P-1) + 1 + k;
+ bits[0] = 32*i - hi0bits(z);
+ }
+ return new BigInteger(dbl_bits);
+ }
+
+ public static String JS_dtobasestr(int base, double d)
+ {
+ char[] buffer; /* The output string */
+ int p; /* index to current position in the buffer */
+ int pInt; /* index to the beginning of the integer part of the string */
+
+ int q;
+ int digit;
+ double di; /* d truncated to an integer */
+ double df; /* The fractional part of d */
+
+// JS_ASSERT(base >= 2 && base <= 36);
+
+ buffer = new char[DTOBASESTR_BUFFER_SIZE];
+
+ p = 0;
+ if (d < 0.0) {
+ buffer[p++] = '-';
+ d = -d;
+ }
+
+ /* Check for Infinity and NaN */
+ if (Double.isNaN(d))
+ return "NaN";
+ else
+ if (Double.isInfinite(d))
+ return "Infinity";
+
+ /* Output the integer part of d with the digits in reverse order. */
+ pInt = p;
+ di = (int)d;
+ BigInteger b = BigInteger.valueOf((int)di);
+ String intDigits = b.toString(base);
+ intDigits.getChars(0, intDigits.length(), buffer, p);
+ p += intDigits.length();
+
+ df = d - di;
+ if (df != 0.0) {
+ /* We have a fraction. */
+ buffer[p++] = '.';
+
+ long dBits = Double.doubleToLongBits(d);
+ int word0 = (int)(dBits >> 32);
+ int word1 = (int)(dBits);
+
+ int[] e = new int[1];
+ int[] bbits = new int[1];
+
+ b = d2b(df, e, bbits);
+// JS_ASSERT(e < 0);
+ /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */
+
+ int s2 = -(word0 >>> Exp_shift1 & Exp_mask >> Exp_shift1);
+ if (s2 == 0)
+ s2 = -1;
+ s2 += Bias + P;
+ /* 1/2^s2 = (nextDouble(d) - d)/2 */
+// JS_ASSERT(-s2 < e);
+ BigInteger mlo = BigInteger.valueOf(1);
+ BigInteger mhi = mlo;
+ if ((word1 == 0) && ((word0 & Bndry_mask) == 0)
+ && ((word0 & (Exp_mask & Exp_mask << 1)) != 0)) {
+ /* The special case. Here we want to be within a quarter of the last input
+ significant digit instead of one half of it when the output string's value is less than d. */
+ s2 += Log2P;
+ mhi = BigInteger.valueOf(1< df = b/2^s2 > 0;
+ * (d - prevDouble(d))/2 = mlo/2^s2;
+ * (nextDouble(d) - d)/2 = mhi/2^s2. */
+ BigInteger bigBase = BigInteger.valueOf(base);
+
+ boolean done = false;
+ do {
+ b = b.multiply(bigBase);
+ BigInteger[] divResult = b.divideAndRemainder(s);
+ b = divResult[1];
+ digit = (char)(divResult[0].intValue());
+ if (mlo == mhi)
+ mlo = mhi = mlo.multiply(bigBase);
+ else {
+ mlo = mlo.multiply(bigBase);
+ mhi = mhi.multiply(bigBase);
+ }
+
+ /* Do we yet have the shortest string that will round to d? */
+ int j = b.compareTo(mlo);
+ /* j is b/2^s2 compared with mlo/2^s2. */
+ BigInteger delta = s.subtract(mhi);
+ int j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta);
+ /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
+ if (j1 == 0 && ((word1 & 1) == 0)) {
+ if (j > 0)
+ digit++;
+ done = true;
+ } else
+ if (j < 0 || (j == 0 && ((word1 & 1) == 0))) {
+ if (j1 > 0) {
+ /* Either dig or dig+1 would work here as the least significant digit.
+ Use whichever would produce an output value closer to d. */
+ b = b.shiftLeft(1);
+ j1 = b.compareTo(s);
+ if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output
+ * such as 3.5 in base 3. */
+ digit++;
+ }
+ done = true;
+ } else if (j1 > 0) {
+ digit++;
+ done = true;
+ }
+// JS_ASSERT(digit < (uint32)base);
+ buffer[p++] = BASEDIGIT(digit);
+ } while (!done);
+ }
+
+ return new String(buffer, 0, p);
+ }
+
+ /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ * 1. Rather than iterating, we use a simple numeric overestimate
+ * to determine k = floor(log10(d)). We scale relevant
+ * quantities using O(log2(k)) rather than O(k) multiplications.
+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ * try to generate digits strictly left to right. Instead, we
+ * compute with fewer bits and propagate the carry if necessary
+ * when rounding the final digit up. This is often faster.
+ * 3. Under the assumption that input will be rounded nearest,
+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ * That is, we allow equality in stopping tests when the
+ * round-nearest rule will give the same floating-point value
+ * as would satisfaction of the stopping test with strict
+ * inequality.
+ * 4. We remove common factors of powers of 2 from relevant
+ * quantities.
+ * 5. When converting floating-point integers less than 1e16,
+ * we use floating-point arithmetic rather than resorting
+ * to multiple-precision integers.
+ * 6. When asked to produce fewer than 15 digits, we first try
+ * to get by with floating-point arithmetic; we resort to
+ * multiple-precision integer arithmetic only if we cannot
+ * guarantee that the floating-point calculation has given
+ * the correctly rounded result. For k requested digits and
+ * "uniformly" distributed input, the probability is
+ * something like 10^(k-15) that we must resort to the Long
+ * calculation.
+ */
+
+ static int word0(double d)
+ {
+ long dBits = Double.doubleToLongBits(d);
+ return (int)(dBits >> 32);
+ }
+
+ static double setWord0(double d, int i)
+ {
+ long dBits = Double.doubleToLongBits(d);
+ dBits = ((long)i << 32) | (dBits & 0x0FFFFFFFFL);
+ return Double.longBitsToDouble(dBits);
+ }
+
+ static int word1(double d)
+ {
+ long dBits = Double.doubleToLongBits(d);
+ return (int)(dBits);
+ }
+
+ /* Return b * 5^k. k must be nonnegative. */
+ // XXXX the C version built a cache of these
+ static BigInteger pow5mult(BigInteger b, int k)
+ {
+ return b.multiply(BigInteger.valueOf(5).pow(k));
+ }
+
+ static boolean roundOff(StringBuffer buf)
+ {
+ char lastCh;
+ while ((lastCh = buf.charAt(buf.length() - 1)) == '9') {
+ buf.setLength(buf.length() - 1);
+ if (buf.length() == 0) {
+ return true;
+ }
+ }
+ buf.append((char)(lastCh + 1));
+ return false;
+ }
+
+ /* Always emits at least one digit. */
+ /* If biasUp is set, then rounding in modes 2 and 3 will round away from zero
+ * when the number is exactly halfway between two representable values. For example,
+ * rounding 2.5 to zero digits after the decimal point will return 3 and not 2.
+ * 2.49 will still round to 2, and 2.51 will still round to 3. */
+ /* bufsize should be at least 20 for modes 0 and 1. For the other modes,
+ * bufsize should be two greater than the maximum number of output characters expected. */
+ static int
+ JS_dtoa(double d, int mode, boolean biasUp, int ndigits,
+ boolean[] sign, StringBuffer buf)
+ {
+ /* Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to 9999.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> max(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4-9 should give the same return values as 2-3, i.e.,
+ 4 <= mode <= 9 ==> same return as mode
+ 2 + (mode & 1). These modes are mainly for
+ debugging; often they run slower but sometimes
+ faster than modes 2-3.
+ 4,5,8,9 ==> left-to-right digit generation.
+ 6-9 ==> don't try fast floating-point estimate
+ (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ int b2, b5, i, ieps, ilim, ilim0, ilim1,
+ j, j1, k, k0, m2, m5, s2, s5;
+ char dig;
+ long L;
+ long x;
+ BigInteger b, b1, delta, mlo, mhi, S;
+ int[] be = new int[1];
+ int[] bbits = new int[1];
+ double d2, ds, eps;
+ boolean spec_case, denorm, k_check, try_quick, leftright;
+
+ if ((word0(d) & Sign_bit) != 0) {
+ /* set sign for everything, including 0's and NaNs */
+ sign[0] = true;
+ // word0(d) &= ~Sign_bit; /* clear sign bit */
+ d = setWord0(d, word0(d) & ~Sign_bit);
+ }
+ else
+ sign[0] = false;
+
+ if ((word0(d) & Exp_mask) == Exp_mask) {
+ /* Infinity or NaN */
+ buf.append(((word1(d) == 0) && ((word0(d) & Frac_mask) == 0)) ? "Infinity" : "NaN");
+ return 9999;
+ }
+ if (d == 0) {
+// no_digits:
+ buf.setLength(0);
+ buf.append('0'); /* copy "0" to buffer */
+ return 1;
+ }
+
+ b = d2b(d, be, bbits);
+ if ((i = (int)(word0(d) >>> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) {
+ d2 = setWord0(d, (word0(d) & Frac_mask1) | Exp_11);
+ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ * log10(x) = log(x) / log(10)
+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
+ *
+ * This suggests computing an approximation k to log10(d) by
+ *
+ * k = (i - Bias)*0.301029995663981
+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+ *
+ * We want k to be too large rather than too small.
+ * The error in the first-order Taylor series approximation
+ * is in our favor, so we just round up the constant enough
+ * to compensate for any error in the multiplication of
+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ * adding 1e-13 to the constant term more than suffices.
+ * Hence we adjust the constant term to 0.1760912590558.
+ * (We could get a more accurate k by invoking log10,
+ * but this is probably not worthwhile.)
+ */
+ i -= Bias;
+ denorm = false;
+ }
+ else {
+ /* d is denormalized */
+ i = bbits[0] + be[0] + (Bias + (P-1) - 1);
+ x = (i > 32) ? word0(d) << (64 - i) | word1(d) >>> (i - 32) : word1(d) << (32 - i);
+// d2 = x;
+// word0(d2) -= 31*Exp_msk1; /* adjust exponent */
+ d2 = setWord0(x, word0(x) - 31*Exp_msk1);
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = true;
+ }
+ /* At this point d = f*2^i, where 1 <= f < 2. d2 is an approximation of f. */
+ ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+ k = (int)ds;
+ if (ds < 0.0 && ds != k)
+ k--; /* want k = floor(ds) */
+ k_check = true;
+ if (k >= 0 && k <= Ten_pmax) {
+ if (d < tens[k])
+ k--;
+ k_check = false;
+ }
+ /* At this point floor(log10(d)) <= k <= floor(log10(d))+1.
+ If k_check is zero, we're guaranteed that k = floor(log10(d)). */
+ j = bbits[0] - i - 1;
+ /* At this point d = b/2^j, where b is an odd integer. */
+ if (j >= 0) {
+ b2 = 0;
+ s2 = j;
+ }
+ else {
+ b2 = -j;
+ s2 = 0;
+ }
+ if (k >= 0) {
+ b5 = 0;
+ s5 = k;
+ s2 += k;
+ }
+ else {
+ b2 -= k;
+ b5 = -k;
+ s5 = 0;
+ }
+ /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer,
+ b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */
+ if (mode < 0 || mode > 9)
+ mode = 0;
+ try_quick = true;
+ if (mode > 5) {
+ mode -= 4;
+ try_quick = false;
+ }
+ leftright = true;
+ ilim = ilim1 = 0;
+ switch(mode) {
+ case 0:
+ case 1:
+ ilim = ilim1 = -1;
+ i = 18;
+ ndigits = 0;
+ break;
+ case 2:
+ leftright = false;
+ /* no break */
+ case 4:
+ if (ndigits <= 0)
+ ndigits = 1;
+ ilim = ilim1 = i = ndigits;
+ break;
+ case 3:
+ leftright = false;
+ /* no break */
+ case 5:
+ i = ndigits + k + 1;
+ ilim = i;
+ ilim1 = i - 1;
+ if (i <= 0)
+ i = 1;
+ }
+ /* ilim is the maximum number of significant digits we want, based on k and ndigits. */
+ /* ilim1 is the maximum number of significant digits we want, based on k and ndigits,
+ when it turns out that k was computed too high by one. */
+
+ boolean fast_failed = false;
+ if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ d2 = d;
+ k0 = k;
+ ilim0 = ilim;
+ ieps = 2; /* conservative */
+ /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */
+ if (k > 0) {
+ ds = tens[k&0xf];
+ j = k >> 4;
+ if ((j & Bletch) != 0) {
+ /* prevent overflows */
+ j &= Bletch - 1;
+ d /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; (j != 0); j >>= 1, i++)
+ if ((j & 1) != 0) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ d /= ds;
+ }
+ else if ((j1 = -k) != 0) {
+ d *= tens[j1 & 0xf];
+ for(j = j1 >> 4; (j != 0); j >>= 1, i++)
+ if ((j & 1) != 0) {
+ ieps++;
+ d *= bigtens[i];
+ }
+ }
+ /* Check that k was computed correctly. */
+ if (k_check && d < 1.0 && ilim > 0) {
+ if (ilim1 <= 0)
+ fast_failed = true;
+ else {
+ ilim = ilim1;
+ k--;
+ d *= 10.;
+ ieps++;
+ }
+ }
+ /* eps bounds the cumulative error. */
+// eps = ieps*d + 7.0;
+// word0(eps) -= (P-1)*Exp_msk1;
+ eps = ieps*d + 7.0;
+ eps = setWord0(eps, word0(eps) - (P-1)*Exp_msk1);
+ if (ilim == 0) {
+ S = mhi = null;
+ d -= 5.0;
+ if (d > eps) {
+ buf.append('1');
+ k++;
+ return k + 1;
+ }
+ if (d < -eps) {
+ buf.setLength(0);
+ buf.append('0'); /* copy "0" to buffer */
+ return 1;
+ }
+ fast_failed = true;
+ }
+ if (!fast_failed) {
+ fast_failed = true;
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ eps = 0.5/tens[ilim-1] - eps;
+ for(i = 0;;) {
+ L = (long)d;
+ d -= L;
+ buf.append((char)('0' + L));
+ if (d < eps) {
+ return k + 1;
+ }
+ if (1.0 - d < eps) {
+// goto bump_up;
+ char lastCh;
+ while (true) {
+ lastCh = buf.charAt(buf.length() - 1);
+ buf.setLength(buf.length() - 1);
+ if (lastCh != '9') break;
+ if (buf.length() == 0) {
+ k++;
+ lastCh = '0';
+ break;
+ }
+ }
+ buf.append((char)(lastCh + 1));
+ return k + 1;
+ }
+ if (++i >= ilim)
+ break;
+ eps *= 10.0;
+ d *= 10.0;
+ }
+ }
+ else {
+ /* Generate ilim digits, then fix them up. */
+ eps *= tens[ilim-1];
+ for(i = 1;; i++, d *= 10.0) {
+ L = (long)d;
+ d -= L;
+ buf.append((char)('0' + L));
+ if (i == ilim) {
+ if (d > 0.5 + eps) {
+// goto bump_up;
+ char lastCh;
+ while (true) {
+ lastCh = buf.charAt(buf.length() - 1);
+ buf.setLength(buf.length() - 1);
+ if (lastCh != '9') break;
+ if (buf.length() == 0) {
+ k++;
+ lastCh = '0';
+ break;
+ }
+ }
+ buf.append((char)(lastCh + 1));
+ return k + 1;
+ }
+ else
+ if (d < 0.5 - eps) {
+ while (buf.charAt(buf.length() - 1) == '0')
+ buf.setLength(buf.length() - 1);
+// while(*--s == '0') ;
+// s++;
+ return k + 1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (fast_failed) {
+ buf.setLength(0);
+ d = d2;
+ k = k0;
+ ilim = ilim0;
+ }
+ }
+
+ /* Do we have a "small" integer? */
+
+ if (be[0] >= 0 && k <= Int_max) {
+ /* Yes. */
+ ds = tens[k];
+ if (ndigits < 0 && ilim <= 0) {
+ S = mhi = null;
+ if (ilim < 0 || d < 5*ds || (!biasUp && d == 5*ds)) {
+ buf.setLength(0);
+ buf.append('0'); /* copy "0" to buffer */
+ return 1;
+ }
+ buf.append('1');
+ k++;
+ return k + 1;
+ }
+ for(i = 1;; i++) {
+ L = (long) (d / ds);
+ d -= L*ds;
+ buf.append((char)('0' + L));
+ if (i == ilim) {
+ d += d;
+ if ((d > ds) || (d == ds && (((L & 1) != 0) || biasUp))) {
+// bump_up:
+// while(*--s == '9')
+// if (s == buf) {
+// k++;
+// *s = '0';
+// break;
+// }
+// ++*s++;
+ char lastCh;
+ while (true) {
+ lastCh = buf.charAt(buf.length() - 1);
+ buf.setLength(buf.length() - 1);
+ if (lastCh != '9') break;
+ if (buf.length() == 0) {
+ k++;
+ lastCh = '0';
+ break;
+ }
+ }
+ buf.append((char)(lastCh + 1));
+ }
+ break;
+ }
+ d *= 10.0;
+ if (d == 0)
+ break;
+ }
+ return k + 1;
+ }
+
+ m2 = b2;
+ m5 = b5;
+ mhi = mlo = null;
+ if (leftright) {
+ if (mode < 2) {
+ i = (denorm) ? be[0] + (Bias + (P-1) - 1 + 1) : 1 + P - bbits[0];
+ /* i is 1 plus the number of trailing zero bits in d's significand. Thus,
+ (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */
+ }
+ else {
+ j = ilim - 1;
+ if (m5 >= j)
+ m5 -= j;
+ else {
+ s5 += j -= m5;
+ b5 += j;
+ m5 = 0;
+ }
+ if ((i = ilim) < 0) {
+ m2 -= i;
+ i = 0;
+ }
+ /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */
+ }
+ b2 += i;
+ s2 += i;
+ mhi = BigInteger.valueOf(1);
+ /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or
+ input (when mode < 2) significant digit, divided by 10^k. */
+ }
+ /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5). Reduce common factors in
+ b2, m2, and s2 without changing the equalities. */
+ if (m2 > 0 && s2 > 0) {
+ i = (m2 < s2) ? m2 : s2;
+ b2 -= i;
+ m2 -= i;
+ s2 -= i;
+ }
+
+ /* Fold b5 into b and m5 into mhi. */
+ if (b5 > 0) {
+ if (leftright) {
+ if (m5 > 0) {
+ mhi = pow5mult(mhi, m5);
+ b1 = mhi.multiply(b);
+ b = b1;
+ }
+ if ((j = b5 - m5) != 0)
+ b = pow5mult(b, j);
+ }
+ else
+ b = pow5mult(b, b5);
+ }
+ /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and
+ (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */
+
+ S = BigInteger.valueOf(1);
+ if (s5 > 0)
+ S = pow5mult(S, s5);
+ /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and
+ (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */
+
+ /* Check for special case that d is a normalized power of 2. */
+ spec_case = false;
+ if (mode < 2) {
+ if ( (word1(d) == 0) && ((word0(d) & Bndry_mask) == 0)
+ && ((word0(d) & (Exp_mask & Exp_mask << 1)) != 0)
+ ) {
+ /* The special case. Here we want to be within a quarter of the last input
+ significant digit instead of one half of it when the decimal output string's value is less than d. */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = true;
+ }
+ }
+
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ *
+ * Perhaps we should just compute leading 28 bits of S once
+ * and for all and pass them and a shift to quorem, so it
+ * can do shifts and ors to compute the numerator for q.
+ */
+ byte [] S_bytes = S.toByteArray();
+ int S_hiWord = 0;
+ for (int idx = 0; idx < 4; idx++) {
+ S_hiWord = (S_hiWord << 8);
+ if (idx < S_bytes.length)
+ S_hiWord |= (S_bytes[idx] & 0xFF);
+ }
+ if ((i = (((s5 != 0) ? 32 - hi0bits(S_hiWord) : 1) + s2) & 0x1f) != 0)
+ i = 32 - i;
+ /* i is the number of leading zero bits in the most significant word of S*2^s2. */
+ if (i > 4) {
+ i -= 4;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ else if (i < 4) {
+ i += 28;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */
+ if (b2 > 0)
+ b = b.shiftLeft(b2);
+ if (s2 > 0)
+ S = S.shiftLeft(s2);
+ /* Now we have d/10^k = b/S and
+ (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */
+ if (k_check) {
+ if (b.compareTo(S) < 0) {
+ k--;
+ b = b.multiply(BigInteger.valueOf(10)); /* we botched the k estimate */
+ if (leftright)
+ mhi = mhi.multiply(BigInteger.valueOf(10));
+ ilim = ilim1;
+ }
+ }
+ /* At this point 1 <= d/10^k = b/S < 10. */
+
+ if (ilim <= 0 && mode > 2) {
+ /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode.
+ Output either zero or the minimum nonzero output depending on which is closer to d. */
+ if ((ilim < 0 )
+ || ((i = b.compareTo(S = S.multiply(BigInteger.valueOf(5)))) < 0)
+ || ((i == 0 && !biasUp))) {
+ /* Always emit at least one digit. If the number appears to be zero
+ using the current mode, then emit one '0' digit and set decpt to 1. */
+ /*no_digits:
+ k = -1 - ndigits;
+ goto ret; */
+ buf.setLength(0);
+ buf.append('0'); /* copy "0" to buffer */
+ return 1;
+// goto no_digits;
+ }
+// one_digit:
+ buf.append('1');
+ k++;
+ return k + 1;
+ }
+ if (leftright) {
+ if (m2 > 0)
+ mhi = mhi.shiftLeft(m2);
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = mlo;
+ mhi = mhi.shiftLeft(Log2P);
+ }
+ /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */
+ /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */
+
+ for(i = 1;;i++) {
+ BigInteger[] divResult = b.divideAndRemainder(S);
+ b = divResult[1];
+ dig = (char)(divResult[0].intValue() + '0');
+ /* Do we yet have the shortest decimal string
+ * that will round to d?
+ */
+ j = b.compareTo(mlo);
+ /* j is b/S compared with mlo/S. */
+ delta = S.subtract(mhi);
+ j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta);
+ /* j1 is b/S compared with 1 - mhi/S. */
+ if ((j1 == 0) && (mode == 0) && ((word1(d) & 1) == 0)) {
+ if (dig == '9') {
+ buf.append('9');
+ if (roundOff(buf)) {
+ k++;
+ buf.append('1');
+ }
+ return k + 1;
+// goto round_9_up;
+ }
+ if (j > 0)
+ dig++;
+ buf.append(dig);
+ return k + 1;
+ }
+ if ((j < 0)
+ || ((j == 0)
+ && (mode == 0)
+ && ((word1(d) & 1) == 0)
+ )) {
+ if (j1 > 0) {
+ /* Either dig or dig+1 would work here as the least significant decimal digit.
+ Use whichever would produce a decimal value closer to d. */
+ b = b.shiftLeft(1);
+ j1 = b.compareTo(S);
+ if (((j1 > 0) || (j1 == 0 && (((dig & 1) == 1) || biasUp)))
+ && (dig++ == '9')) {
+ buf.append('9');
+ if (roundOff(buf)) {
+ k++;
+ buf.append('1');
+ }
+ return k + 1;
+// goto round_9_up;
+ }
+ }
+ buf.append(dig);
+ return k + 1;
+ }
+ if (j1 > 0) {
+ if (dig == '9') { /* possible if i == 1 */
+// round_9_up:
+// *s++ = '9';
+// goto roundoff;
+ buf.append('9');
+ if (roundOff(buf)) {
+ k++;
+ buf.append('1');
+ }
+ return k + 1;
+ }
+ buf.append((char)(dig + 1));
+ return k + 1;
+ }
+ buf.append(dig);
+ if (i == ilim)
+ break;
+ b = b.multiply(BigInteger.valueOf(10));
+ if (mlo == mhi)
+ mlo = mhi = mhi.multiply(BigInteger.valueOf(10));
+ else {
+ mlo = mlo.multiply(BigInteger.valueOf(10));
+ mhi = mhi.multiply(BigInteger.valueOf(10));
+ }
+ }
+ }
+ else
+ for(i = 1;; i++) {
+// (char)(dig = quorem(b,S) + '0');
+ BigInteger[] divResult = b.divideAndRemainder(S);
+ b = divResult[1];
+ dig = (char)(divResult[0].intValue() + '0');
+ buf.append(dig);
+ if (i >= ilim)
+ break;
+ b = b.multiply(BigInteger.valueOf(10));
+ }
+
+ /* Round off last digit */
+
+ b = b.shiftLeft(1);
+ j = b.compareTo(S);
+ if ((j > 0) || (j == 0 && (((dig & 1) == 1) || biasUp))) {
+// roundoff:
+// while(*--s == '9')
+// if (s == buf) {
+// k++;
+// *s++ = '1';
+// goto ret;
+// }
+// ++*s++;
+ if (roundOff(buf)) {
+ k++;
+ buf.append('1');
+ return k + 1;
+ }
+ }
+ else {
+ /* Strip trailing zeros */
+ while (buf.charAt(buf.length() - 1) == '0')
+ buf.setLength(buf.length() - 1);
+// while(*--s == '0') ;
+// s++;
+ }
+// ret:
+// Bfree(S);
+// if (mhi) {
+// if (mlo && mlo != mhi)
+// Bfree(mlo);
+// Bfree(mhi);
+// }
+// ret1:
+// Bfree(b);
+// JS_ASSERT(s < buf + bufsize);
+ return k + 1;
+ }
+
+ /* Mapping of JSDToStrMode -> JS_dtoa mode */
+ private static final int dtoaModes[] = {
+ 0, /* DTOSTR_STANDARD */
+ 0, /* DTOSTR_STANDARD_EXPONENTIAL, */
+ 3, /* DTOSTR_FIXED, */
+ 2, /* DTOSTR_EXPONENTIAL, */
+ 2}; /* DTOSTR_PRECISION */
+
+ static void
+ JS_dtostr(StringBuffer buffer, int mode, int precision, double d)
+ {
+ int decPt; /* Position of decimal point relative to first digit returned by JS_dtoa */
+ boolean[] sign = new boolean[1]; /* true if the sign bit was set in d */
+ int nDigits; /* Number of significand digits returned by JS_dtoa */
+
+// JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE :
+// DTOSTR_VARIABLE_BUFFER_SIZE(precision)));
+
+ if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21))
+ mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */
+
+ decPt = JS_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, sign, buffer);
+ nDigits = buffer.length();
+
+ /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */
+ if (decPt != 9999) {
+ boolean exponentialNotation = false;
+ int minNDigits = 0; /* Minimum number of significand digits required by mode and precision */
+ int p;
+ int q;
+
+ switch (mode) {
+ case DTOSTR_STANDARD:
+ if (decPt < -5 || decPt > 21)
+ exponentialNotation = true;
+ else
+ minNDigits = decPt;
+ break;
+
+ case DTOSTR_FIXED:
+ if (precision >= 0)
+ minNDigits = decPt + precision;
+ else
+ minNDigits = decPt;
+ break;
+
+ case DTOSTR_EXPONENTIAL:
+// JS_ASSERT(precision > 0);
+ minNDigits = precision;
+ /* Fall through */
+ case DTOSTR_STANDARD_EXPONENTIAL:
+ exponentialNotation = true;
+ break;
+
+ case DTOSTR_PRECISION:
+// JS_ASSERT(precision > 0);
+ minNDigits = precision;
+ if (decPt < -5 || decPt > precision)
+ exponentialNotation = true;
+ break;
+ }
+
+ /* If the number has fewer than minNDigits, pad it with zeros at the end */
+ if (nDigits < minNDigits) {
+ p = minNDigits;
+ nDigits = minNDigits;
+ do {
+ buffer.append('0');
+ } while (buffer.length() != p);
+ }
+
+ if (exponentialNotation) {
+ /* Insert a decimal point if more than one significand digit */
+ if (nDigits != 1) {
+ buffer.insert(1, '.');
+ }
+ buffer.append('e');
+ if ((decPt - 1) >= 0)
+ buffer.append('+');
+ buffer.append(decPt - 1);
+// JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1);
+ } else if (decPt != nDigits) {
+ /* Some kind of a fraction in fixed notation */
+// JS_ASSERT(decPt <= nDigits);
+ if (decPt > 0) {
+ /* dd...dd . dd...dd */
+ buffer.insert(decPt, '.');
+ } else {
+ /* 0 . 00...00dd...dd */
+ for (int i = 0; i < 1 - decPt; i++)
+ buffer.insert(0, '0');
+ buffer.insert(1, '.');
+ }
+ }
+ }
+
+ /* If negative and neither -0.0 nor NaN, output a leading '-'. */
+ if (sign[0] &&
+ !(word0(d) == Sign_bit && word1(d) == 0) &&
+ !((word0(d) & Exp_mask) == Exp_mask &&
+ ((word1(d) != 0) || ((word0(d) & Frac_mask) != 0)))) {
+ buffer.insert(0, '-');
+ }
+ }
+
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/ErrorReporter.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/ErrorReporter.java
new file mode 100644
index 00000000000..e7ac6e17826
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/ErrorReporter.java
@@ -0,0 +1,104 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+// API class
+
+package com.google.gwt.dev.js.rhino;
+
+/**
+ * This is interface defines a protocol for the reporting of
+ * errors during JavaScript translation or execution.
+ *
+ * @author Norris Boyd
+ */
+
+public interface ErrorReporter {
+
+ /**
+ * Report a warning.
+ *
+ * The implementing class may choose to ignore the warning
+ * if it desires.
+ *
+ * @param message a String describing the warning
+ * @param sourceName a String describing the JavaScript source
+ * where the warning occured; typically a filename or URL
+ * @param line the line number associated with the warning
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ */
+ void warning(String message, String sourceName, int line,
+ String lineSource, int lineOffset);
+
+ /**
+ * Report an error.
+ *
+ * The implementing class is free to throw an exception if
+ * it desires.
+ *
+ * If execution has not yet begun, the JavaScript engine is
+ * free to find additional errors rather than terminating
+ * the translation. It will not execute a script that had
+ * errors, however.
+ *
+ * @param message a String describing the error
+ * @param sourceName a String describing the JavaScript source
+ * where the error occured; typically a filename or URL
+ * @param line the line number associated with the error
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ */
+ void error(String message, String sourceName, int line,
+ String lineSource, int lineOffset);
+
+ /**
+ * Creates an EvaluatorException that may be thrown.
+ *
+ * runtimeErrors, unlike errors, will always terminate the
+ * current script.
+ *
+ * @param message a String describing the error
+ * @param sourceName a String describing the JavaScript source
+ * where the error occured; typically a filename or URL
+ * @param line the line number associated with the error
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ * @return an EvaluatorException that will be thrown.
+ */
+ EvaluatorException runtimeError(String message, String sourceName,
+ int line, String lineSource,
+ int lineOffset);
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/EvaluatorException.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/EvaluatorException.java
new file mode 100644
index 00000000000..bbc8d452aa8
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/EvaluatorException.java
@@ -0,0 +1,56 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+/**
+ * The class of exceptions thrown by the JavaScript engine.
+ */
+public class EvaluatorException extends RuntimeException {
+
+ /**
+ * Create an exception with the specified detail message.
+ *
+ * Errors internal to the JavaScript engine will simply throw a
+ * RuntimeException.
+ *
+ * @param detail a message with detail about the exception
+ */
+ public EvaluatorException(String detail) {
+ super(detail);
+ }
+
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/IRFactory.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/IRFactory.java
new file mode 100644
index 00000000000..59be80b202b
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/IRFactory.java
@@ -0,0 +1,448 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+
+package com.google.gwt.dev.js.rhino;
+
+/**
+ * This class allows the creation of nodes, and follows the Factory pattern.
+ *
+ * @see Node
+ * @author Mike McCabe
+ * @author Norris Boyd
+ */
+public class IRFactory {
+
+ public IRFactory(TokenStream ts) {
+ this.ts = ts;
+ }
+
+ /**
+ * Script (for associating file/url names with toplevel scripts.)
+ */
+ public Object createScript(Object body, String sourceName,
+ int baseLineno, int endLineno, Object source)
+ {
+ Node result = new Node(TokenStream.SCRIPT);
+ Node children = ((Node) body).getFirstChild();
+ if (children != null)
+ result.addChildrenToBack(children);
+
+ result.putProp(Node.SOURCENAME_PROP, sourceName);
+ result.putIntProp(Node.BASE_LINENO_PROP, baseLineno);
+ result.putIntProp(Node.END_LINENO_PROP, endLineno);
+ if (source != null)
+ result.putProp(Node.SOURCE_PROP, source);
+ return result;
+ }
+
+ /**
+ * Leaf
+ */
+ public Object createLeaf(int nodeType) {
+ return new Node(nodeType);
+ }
+
+ public Object createLeaf(int nodeType, int nodeOp) {
+ return new Node(nodeType, nodeOp);
+ }
+
+ public int getLeafType(Object leaf) {
+ Node n = (Node) leaf;
+ return n.getType();
+ }
+
+ /**
+ * Statement leaf nodes.
+ */
+
+ public Object createSwitch(int lineno) {
+ return new Node(TokenStream.SWITCH, lineno);
+ }
+
+ public Object createVariables(int lineno) {
+ return new Node(TokenStream.VAR, lineno);
+ }
+
+ public Object createExprStatement(Object expr, int lineno) {
+ return new Node(TokenStream.EXPRSTMT, (Node) expr, lineno);
+ }
+
+ /**
+ * Name
+ */
+ public Object createName(String name) {
+ return Node.newString(TokenStream.NAME, name);
+ }
+
+ /**
+ * String (for literals)
+ */
+ public Object createString(String string) {
+ return Node.newString(string);
+ }
+
+ /**
+ * Number (for literals)
+ */
+ public Object createNumber(double number) {
+ return Node.newNumber(number);
+ }
+
+ /**
+ * Catch clause of try/catch/finally
+ * @param varName the name of the variable to bind to the exception
+ * @param catchCond the condition under which to catch the exception.
+ * May be null if no condition is given.
+ * @param stmts the statements in the catch clause
+ * @param lineno the starting line number of the catch clause
+ */
+ public Object createCatch(String varName, Object catchCond, Object stmts,
+ int lineno)
+ {
+ if (catchCond == null) {
+ catchCond = new Node(TokenStream.PRIMARY, TokenStream.TRUE);
+ }
+ return new Node(TokenStream.CATCH, (Node)createName(varName),
+ (Node)catchCond, (Node)stmts, lineno);
+ }
+
+ /**
+ * Throw
+ */
+ public Object createThrow(Object expr, int lineno) {
+ return new Node(TokenStream.THROW, (Node)expr, lineno);
+ }
+
+ /**
+ * Return
+ */
+ public Object createReturn(Object expr, int lineno) {
+ return expr == null
+ ? new Node(TokenStream.RETURN, lineno)
+ : new Node(TokenStream.RETURN, (Node)expr, lineno);
+ }
+
+ /**
+ * Label
+ */
+ public Object createLabel(String label, int lineno) {
+ Node result = new Node(TokenStream.LABEL, lineno);
+ Node name = Node.newString(TokenStream.NAME, label);
+ result.addChildToBack(name);
+ return result;
+ }
+
+ /**
+ * Break (possibly labeled)
+ */
+ public Object createBreak(String label, int lineno) {
+ Node result = new Node(TokenStream.BREAK, lineno);
+ if (label == null) {
+ return result;
+ } else {
+ Node name = Node.newString(TokenStream.NAME, label);
+ result.addChildToBack(name);
+ return result;
+ }
+ }
+
+ /**
+ * Continue (possibly labeled)
+ */
+ public Object createContinue(String label, int lineno) {
+ Node result = new Node(TokenStream.CONTINUE, lineno);
+ if (label == null) {
+ return result;
+ } else {
+ Node name = Node.newString(TokenStream.NAME, label);
+ result.addChildToBack(name);
+ return result;
+ }
+ }
+
+ /**
+ * debugger
+ */
+ public Object createDebugger(int lineno) {
+ Node result = new Node(TokenStream.DEBUGGER, lineno);
+ return result;
+ }
+
+ /**
+ * Statement block
+ * Creates the empty statement block
+ * Must make subsequent calls to add statements to the node
+ */
+ public Object createBlock(int lineno) {
+ return new Node(TokenStream.BLOCK, lineno);
+ }
+
+ public Object createFunction(String name, Object args, Object statements,
+ String sourceName, int baseLineno,
+ int endLineno, Object source,
+ boolean isExpr)
+ {
+ Node f = new Node(TokenStream.FUNCTION,
+ Node.newString(TokenStream.NAME,
+ name == null ? "" : name),
+ (Node)args, (Node)statements, baseLineno);
+
+ f.putProp(Node.SOURCENAME_PROP, sourceName);
+ f.putIntProp(Node.BASE_LINENO_PROP, baseLineno);
+ f.putIntProp(Node.END_LINENO_PROP, endLineno);
+ if (source != null)
+ f.putProp(Node.SOURCE_PROP, source);
+
+ return f;
+ }
+
+ /**
+ * Add a child to the back of the given node. This function
+ * breaks the Factory abstraction, but it removes a requirement
+ * from implementors of Node.
+ */
+ public void addChildToBack(Object parent, Object child) {
+ ((Node)parent).addChildToBack((Node)child);
+ }
+
+ /**
+ * While
+ */
+ public Object createWhile(Object cond, Object body, int lineno) {
+ return new Node(TokenStream.WHILE, (Node)cond, (Node)body, lineno);
+ }
+
+ /**
+ * DoWhile
+ */
+ public Object createDoWhile(Object body, Object cond, int lineno) {
+ return new Node(TokenStream.DO, (Node)body, (Node)cond, lineno);
+ }
+
+ /**
+ * For
+ */
+ public Object createFor(Object init, Object test, Object incr,
+ Object body, int lineno)
+ {
+ return new Node(TokenStream.FOR, (Node)init, (Node)test, (Node)incr,
+ (Node)body);
+ }
+
+ /**
+ * For .. In
+ *
+ */
+ public Object createForIn(Object lhs, Object obj, Object body, int lineno) {
+ return new Node(TokenStream.FOR, (Node)lhs, (Node)obj, (Node)body);
+ }
+
+ /**
+ * Try/Catch/Finally
+ */
+ public Object createTryCatchFinally(Object tryblock, Object catchblocks,
+ Object finallyblock, int lineno)
+ {
+ if (finallyblock == null) {
+ return new Node(TokenStream.TRY, (Node)tryblock, (Node)catchblocks);
+ }
+ return new Node(TokenStream.TRY, (Node)tryblock,
+ (Node)catchblocks, (Node)finallyblock);
+ }
+
+ /**
+ * Throw, Return, Label, Break and Continue are defined in ASTFactory.
+ */
+
+ /**
+ * With
+ */
+ public Object createWith(Object obj, Object body, int lineno) {
+ return new Node(TokenStream.WITH, (Node)obj, (Node)body, lineno);
+ }
+
+ /**
+ * Array Literal
+ */
+ public Object createArrayLiteral(Object obj) {
+ return obj;
+ }
+
+ /**
+ * Object Literals
+ */
+ public Object createObjectLiteral(Object obj) {
+ return obj;
+ }
+
+ /**
+ * Regular expressions
+ */
+ public Object createRegExp(String string, String flags) {
+ return flags.length() == 0
+ ? new Node(TokenStream.REGEXP,
+ Node.newString(string))
+ : new Node(TokenStream.REGEXP,
+ Node.newString(string),
+ Node.newString(flags));
+ }
+
+ /**
+ * If statement
+ */
+ public Object createIf(Object cond, Object ifTrue, Object ifFalse,
+ int lineno)
+ {
+ if (ifFalse == null)
+ return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue);
+ return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue, (Node)ifFalse);
+ }
+
+ public Object createTernary(Object cond, Object ifTrue, Object ifFalse) {
+ return new Node(TokenStream.HOOK,
+ (Node)cond, (Node)ifTrue, (Node)ifFalse);
+ }
+
+ /**
+ * Unary
+ */
+ public Object createUnary(int nodeType, Object child) {
+ Node childNode = (Node) child;
+ return new Node(nodeType, childNode);
+ }
+
+ public Object createUnary(int nodeType, int nodeOp, Object child) {
+ return new Node(nodeType, (Node)child, nodeOp);
+ }
+
+ /**
+ * Binary
+ */
+ public Object createBinary(int nodeType, Object left, Object right) {
+ Node temp;
+ switch (nodeType) {
+
+ case TokenStream.DOT:
+ nodeType = TokenStream.GETPROP;
+ Node idNode = (Node) right;
+ idNode.setType(TokenStream.STRING);
+ String id = idNode.getString();
+ if (id.equals("__proto__") || id.equals("__parent__")) {
+ Node result = new Node(nodeType, (Node) left);
+ result.putProp(Node.SPECIAL_PROP_PROP, id);
+ return result;
+ }
+ break;
+
+ case TokenStream.LB:
+ // OPT: could optimize to GETPROP iff string can't be a number
+ nodeType = TokenStream.GETELEM;
+ break;
+ }
+ return new Node(nodeType, (Node)left, (Node)right);
+ }
+
+ public Object createBinary(int nodeType, int nodeOp, Object left,
+ Object right)
+ {
+ if (nodeType == TokenStream.ASSIGN) {
+ return createAssignment(nodeOp, (Node) left, (Node) right,
+ null, false);
+ }
+ return new Node(nodeType, (Node) left, (Node) right, nodeOp);
+ }
+
+ public Object createAssignment(int nodeOp, Node left, Node right,
+ Class convert, boolean postfix)
+ {
+ int nodeType = left.getType();
+ switch (nodeType) {
+ case TokenStream.NAME:
+ case TokenStream.GETPROP:
+ case TokenStream.GETELEM:
+ break;
+ default:
+ // TODO: This should be a ReferenceError--but that's a runtime
+ // exception. Should we compile an exception into the code?
+ reportError("msg.bad.lhs.assign");
+ }
+
+ return new Node(TokenStream.ASSIGN, left, right, nodeOp);
+ }
+
+ private Node createConvert(Class toType, Node expr) {
+ if (toType == null)
+ return expr;
+ Node result = new Node(TokenStream.CONVERT, expr);
+ result.putProp(Node.TYPE_PROP, Number.class);
+ return result;
+ }
+
+ public static boolean hasSideEffects(Node exprTree) {
+ switch (exprTree.getType()) {
+ case TokenStream.INC:
+ case TokenStream.DEC:
+ case TokenStream.SETPROP:
+ case TokenStream.SETELEM:
+ case TokenStream.SETNAME:
+ case TokenStream.CALL:
+ case TokenStream.NEW:
+ return true;
+ default:
+ Node child = exprTree.getFirstChild();
+ while (child != null) {
+ if (hasSideEffects(child))
+ return true;
+ else
+ child = child.getNext();
+ }
+ break;
+ }
+ return false;
+ }
+
+ private void reportError(String msgResource) {
+
+ String message = Context.getMessage0(msgResource);
+ Context.reportError(
+ message, ts.getSourceName(), ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ // Only needed to get file/line information. Could create an interface
+ // that TokenStream implements if we want to make the connection less
+ // direct.
+ private TokenStream ts;
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/JavaScriptException.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/JavaScriptException.java
new file mode 100644
index 00000000000..2ce6957141f
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/JavaScriptException.java
@@ -0,0 +1,85 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+// API class
+
+package com.google.gwt.dev.js.rhino;
+
+
+/**
+ * Java reflection of JavaScript exceptions. (Possibly wrapping a Java exception.)
+ *
+ * @author Mike McCabe
+ */
+public class JavaScriptException extends Exception {
+
+ /**
+ * Create a JavaScript exception wrapping the given JavaScript value.
+ *
+ * Instances of this class are thrown by the JavaScript 'throw' keyword.
+ *
+ * @param value the JavaScript value thrown.
+ */
+ public JavaScriptException(Object value) {
+ super(value.toString());
+ this.value = value;
+ }
+
+ /**
+ * Get the exception value originally thrown. This may be a
+ * JavaScript value (null, undefined, Boolean, Number, String,
+ * Scriptable or Function) or a Java exception value thrown from a
+ * host object or from Java called through LiveConnect.
+ *
+ * @return the value wrapped by this exception
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * The JavaScript exception value. This value is not
+ * intended for general use; if the JavaScriptException wraps a
+ * Java exception, getScriptableValue may return a Scriptable
+ * wrapping the original Java exception object.
+ *
+ * We would prefer to go through a getter to encapsulate the value,
+ * however that causes the bizarre error "nanosecond timeout value
+ * out of range" on the MS JVM.
+ * @serial
+ */
+ Object value;
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/LineBuffer.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/LineBuffer.java
new file mode 100644
index 00000000000..4424d18ba09
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/LineBuffer.java
@@ -0,0 +1,342 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+import java.io.Reader;
+import java.io.IOException;
+
+/**
+ * An input buffer that combines fast character-based access with
+ * (slower) support for retrieving the text of the current line. It
+ * also supports building strings directly out of the internal buffer
+ * to support fast scanning with minimal object creation.
+ *
+ * Note that it is customized in several ways to support the
+ * TokenStream class, and should not be considered general.
+ *
+ * Credits to Kipp Hickman and John Bandhauer.
+ *
+ * @author Mike McCabe
+ */
+final class LineBuffer {
+ /*
+ * for smooth operation of getLine(), this should be greater than
+ * the length of any expected line. Currently, 256 is 3% slower
+ * than 4096 for large compiles, but seems safer given evaluateString.
+ * Strings for the scanner are are built with StringBuffers
+ * instead of directly out of the buffer whenever a string crosses
+ * a buffer boundary, so small buffer sizes will mean that more
+ * objects are created.
+ */
+ static final int BUFLEN = 256;
+
+ LineBuffer(Reader in, int lineno) {
+ this.in = in;
+ this.lineno = lineno;
+ }
+
+ int read() throws IOException {
+ for(;;) {
+ if (end == offset && !fill())
+ return -1;
+
+ int c = buffer[offset];
+ ++offset;
+
+ if ((c & EOL_HINT_MASK) == 0) {
+ switch (c) {
+ case '\r':
+ // if the next character is a newline, skip past it.
+ if (offset != end) {
+ if (buffer[offset] == '\n')
+ ++offset;
+ } else {
+ // set a flag for fill(), in case the first char
+ // of the next fill is a newline.
+ lastWasCR = true;
+ }
+ // NO break here!
+ case '\n': case '\u2028': case '\u2029':
+ prevStart = lineStart;
+ lineStart = offset;
+ lineno++;
+ return '\n';
+ }
+ }
+
+ if (c < 128 || !formatChar(c)) {
+ return c;
+ }
+ }
+ }
+
+ void unread() {
+ // offset can only be 0 when we're asked to unread() an implicit
+ // EOF_CHAR.
+
+ // This would be wrong behavior in the general case,
+ // because a peek() could map a buffer.length offset to 0
+ // in the process of a fill(), and leave it there. But
+ // the scanner never calls peek() or a failed match()
+ // followed by unread()... this would violate 1-character
+ // lookahead.
+ if (offset == 0 && !hitEOF) Context.codeBug();
+
+ if (offset == 0) // Same as if (hitEOF)
+ return;
+ offset--;
+ int c = buffer[offset];
+ if ((c & EOL_HINT_MASK) == 0 && eolChar(c)) {
+ lineStart = prevStart;
+ lineno--;
+ }
+ }
+
+ private void skipFormatChar() {
+ if (checkSelf && !formatChar(buffer[offset])) Context.codeBug();
+
+ // swap prev character with format one so possible call to
+ // startString can assume that previous non-format char is at
+ // offset - 1. Note it causes getLine to return not exactly the
+ // source LineBuffer read, but it is used only in error reporting
+ // and should not be a problem.
+ if (offset != 0) {
+ char tmp = buffer[offset];
+ buffer[offset] = buffer[offset - 1];
+ buffer[offset - 1] = tmp;
+ }
+ else if (otherEnd != 0) {
+ char tmp = buffer[offset];
+ buffer[offset] = otherBuffer[otherEnd - 1];
+ otherBuffer[otherEnd - 1] = tmp;
+ }
+
+ ++offset;
+ }
+
+ int peek() throws IOException {
+ for (;;) {
+ if (end == offset && !fill()) {
+ return -1;
+ }
+
+ int c = buffer[offset];
+ if ((c & EOL_HINT_MASK) == 0 && eolChar(c)) {
+ return '\n';
+ }
+ if (c < 128 || !formatChar(c)) {
+ return c;
+ }
+
+ skipFormatChar();
+ }
+ }
+
+ boolean match(int test) throws IOException {
+ // TokenStream never looks ahead for '\n', which allows simple code
+ if ((test & EOL_HINT_MASK) == 0 && eolChar(test))
+ Context.codeBug();
+ // Format chars are not allowed either
+ if (test >= 128 && formatChar(test))
+ Context.codeBug();
+
+ for (;;) {
+ if (end == offset && !fill())
+ return false;
+
+ int c = buffer[offset];
+ if (test == c) {
+ ++offset;
+ return true;
+ }
+ if (c < 128 || !formatChar(c)) {
+ return false;
+ }
+ skipFormatChar();
+ }
+ }
+
+ // Reconstruct a source line from the buffers. This can be slow...
+ String getLine() {
+ // Look for line end in the unprocessed buffer
+ int i = offset;
+ while(true) {
+ if (i == end) {
+ // if we're out of buffer, let's just expand it. We do
+ // this instead of reading into a StringBuffer to
+ // preserve the stream for later reads.
+ if (end == buffer.length) {
+ char[] tmp = new char[buffer.length * 2];
+ System.arraycopy(buffer, 0, tmp, 0, end);
+ buffer = tmp;
+ }
+ int charsRead = 0;
+ try {
+ charsRead = in.read(buffer, end, buffer.length - end);
+ } catch (IOException ioe) {
+ // ignore it, we're already displaying an error...
+ break;
+ }
+ if (charsRead < 0)
+ break;
+ end += charsRead;
+ }
+ int c = buffer[i];
+ if ((c & EOL_HINT_MASK) == 0 && eolChar(c))
+ break;
+ i++;
+ }
+
+ int start = lineStart;
+ if (lineStart < 0) {
+ // the line begins somewhere in the other buffer; get that first.
+ StringBuffer sb = new StringBuffer(otherEnd - otherStart + i);
+ sb.append(otherBuffer, otherStart, otherEnd - otherStart);
+ sb.append(buffer, 0, i);
+ return sb.toString();
+ } else {
+ return new String(buffer, lineStart, i - lineStart);
+ }
+ }
+
+ // Get the offset of the current character, relative to
+ // the line that getLine() returns.
+ int getOffset() {
+ if (lineStart < 0)
+ // The line begins somewhere in the other buffer.
+ return offset + (otherEnd - otherStart);
+ else
+ return offset - lineStart;
+ }
+
+ private boolean fill() throws IOException {
+ // fill should be caled only for emty buffer
+ if (checkSelf && !(end == offset)) Context.codeBug();
+
+ // swap buffers
+ char[] tempBuffer = buffer;
+ buffer = otherBuffer;
+ otherBuffer = tempBuffer;
+
+ // allocate the buffers lazily, in case we're handed a short string.
+ if (buffer == null) {
+ buffer = new char[BUFLEN];
+ }
+
+ // buffers have switched, so move the newline marker.
+ if (lineStart >= 0) {
+ otherStart = lineStart;
+ } else {
+ // discard beging of the old line
+ otherStart = 0;
+ }
+
+ otherEnd = end;
+
+ // set lineStart to a sentinel value, unless this is the first
+ // time around.
+ prevStart = lineStart = (otherBuffer == null) ? 0 : -1;
+
+ offset = 0;
+ end = in.read(buffer, 0, buffer.length);
+ if (end < 0) {
+ end = 0;
+
+ // can't null buffers here, because a string might be retrieved
+ // out of the other buffer, and a 0-length string might be
+ // retrieved out of this one.
+
+ hitEOF = true;
+ return false;
+ }
+
+ // If the last character of the previous fill was a carriage return,
+ // then ignore a newline.
+
+ // There's another bizzare special case here. If lastWasCR is
+ // true, and we see a newline, and the buffer length is
+ // 1... then we probably just read the last character of the
+ // file, and returning after advancing offset is not the right
+ // thing to do. Instead, we try to ignore the newline (and
+ // likely get to EOF for real) by doing yet another fill().
+ if (lastWasCR) {
+ if (buffer[0] == '\n') {
+ offset++;
+ if (end == 1)
+ return fill();
+ }
+ lineStart = offset;
+ lastWasCR = false;
+ }
+ return true;
+ }
+
+ int getLineno() { return lineno; }
+ boolean eof() { return hitEOF; }
+
+ private static boolean formatChar(int c) {
+ return Character.getType((char)c) == Character.FORMAT;
+ }
+
+ private static boolean eolChar(int c) {
+ return c == '\r' || c == '\n' || c == '\u2028' || c == '\u2029';
+ }
+
+ // Optimization for faster check for eol character: eolChar(c) returns
+ // true only when (c & EOL_HINT_MASK) == 0
+ private static final int EOL_HINT_MASK = 0xdfd0;
+
+ private Reader in;
+ private char[] otherBuffer = null;
+ private char[] buffer = null;
+
+ // Yes, there are too too many of these.
+ private int offset = 0;
+ private int end = 0;
+ private int otherEnd;
+ private int lineno;
+
+ private int lineStart = 0;
+ private int otherStart = 0;
+ private int prevStart = 0;
+
+ private boolean lastWasCR = false;
+ private boolean hitEOF = false;
+
+// Rudimentary support for Design-by-Contract
+ private static final boolean checkSelf = true;
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/Messages.properties b/js/js.parser/src/com/google/gwt/dev/js/rhino/Messages.properties
new file mode 100644
index 00000000000..7225327a8e7
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/Messages.properties
@@ -0,0 +1,533 @@
+#
+# Default JavaScript messages file.
+#
+# The contents of this file are subject to the Netscape Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/NPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is Rhino code, released
+# May 6, 1999.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1997-1999 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+# Norris Boyd
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU Public License (the "GPL"), in which case the
+# provisions of the GPL are applicable instead of those above.
+# If you wish to allow use of your version of this file only
+# under the terms of the GPL and not to allow others to use your
+# version of this file under the NPL, indicate your decision by
+# deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL. If you do not delete
+# the provisions above, a recipient may use your version of this
+# file under either the NPL or the GPL.
+
+#
+# To add JavaScript error messages for a particular locale, create a
+# new Messages_[locale].properties file, where [locale] is the Java
+# string abbreviation for that locale. For example, JavaScript
+# messages for the Polish locale should be located in
+# Messages_pl.properties, and messages for the Italian Swiss locale
+# should be located in Messages_it_CH.properties. Message properties
+# files should be accessible through the classpath under
+# org.mozilla.javascript.resources
+#
+# See:
+# java.util.ResourceBundle
+# java.text.MessageFormat
+#
+
+# SomeJavaClassWhereUsed
+
+# Codegen
+msg.dup.parms =\
+ Duplicate parameter name "{0}".
+
+# Context
+msg.ctor.not.found =\
+ Constructor for "{0}" not found.
+
+msg.not.ctor =\
+ "{0}" is not a constructor.
+
+# FunctionObject
+msg.varargs.ctor =\
+ Method or constructor "{0}" must be static with the signature \
+ "(Context cx, Object[] args, Function ctorObj, boolean inNewExpr)" \
+ to define a variable arguments constructor.
+
+msg.varargs.fun =\
+ Method "{0}" must be static with the signature \
+ "(Context cx, Scriptable thisObj, Object[] args, Function funObj)" \
+ to define a variable arguments function.
+
+msg.incompat.call =\
+ Method "{0}" called on incompatible object.
+
+msg.bad.parms =\
+ Bad method parameters for "{0}".
+
+msg.no.overload =\
+ Method "{0}" occurs multiple times in class "{1}".
+
+msg.method.not.found =\
+ Method "{0}" not found in "{1}".
+
+# IRFactory
+
+msg.bad.for.in.lhs =\
+ Invalid left-hand side of for..in loop.
+
+msg.bad.lhs.assign =\
+ Invalid assignment left-hand side.
+
+msg.mult.index =\
+ Only one variable allowed in for..in loop.
+
+msg.cant.convert =\
+ Can''t convert to type "{0}".
+
+# NativeGlobal
+msg.cant.call.indirect =\
+ Function "{0}" must be called directly, and not by way of a \
+ function of another name.
+
+msg.eval.nonstring =\
+ Calling eval() with anything other than a primitive string value will \
+ simply return the value. Is this what you intended?
+
+# NativeCall
+msg.only.from.new =\
+ "{0}" may only be invoked from a "new" expression.
+
+msg.deprec.ctor =\
+ The "{0}" constructor is deprecated.
+
+# NativeFunction
+msg.no.function.ref.found.in =\
+ no source found in {1} to decompile function reference {0}
+
+msg.no.function.ref.found =\
+ no source found to decompile function reference {0}
+
+msg.arg.isnt.array =\
+ second argument to Function.prototype.apply must be an array
+
+# NativeGlobal
+msg.bad.esc.mask =\
+ invalid string escape mask
+
+# NativeJavaClass
+msg.cant.instantiate =\
+ error instantiating ({0}): class {1} is interface or abstract
+
+msg.bad.ctor.sig =\
+ Found constructor with wrong signature: \
+ {0} calling {1} with signature {2}
+
+msg.not.java.obj =\
+ Expected argument to getClass() to be a Java object.
+
+msg.no.java.ctor =\
+ Java constructor for "{0}" with arguments "{1}" not found.
+
+# NativeJavaMethod
+msg.method.ambiguous =\
+ The choice of Java method {0}.{1} matching JavaScript argument types ({2}) is ambiguous; \
+ candidate methods are: {3}
+
+msg.constructor.ambiguous =\
+ The choice of Java constructor {0} matching JavaScript argument types ({1}) is ambiguous; \
+ candidate constructors are: {2}
+
+# NativeJavaObject
+msg.conversion.not.allowed =\
+ Cannot convert {0} to {1}
+
+# NativeRegExp
+msg.bad.quant =\
+ Invalid quantifier {0}
+
+msg.overlarge.max =\
+ Overly large maximum {0}
+
+msg.zero.quant =\
+ Zero quantifier {0}
+
+msg.max.lt.min =\
+ Maximum {0} less than minimum
+
+msg.unterm.quant =\
+ Unterminated quantifier {0}
+
+msg.unterm.paren =\
+ Unterminated parenthetical {0}
+
+msg.unterm.class =\
+ Unterminated character class {0}
+
+msg.bad.range =\
+ Invalid range in character class.
+
+msg.trail.backslash =\
+ Trailing \\ in regular expression.
+
+msg.no.regexp =\
+ Regular expressions are not available.
+
+msg.bad.backref =\
+ back-reference exceeds number of capturing parentheses.
+
+# NodeTransformer
+msg.dup.label =\
+ Duplicate label {0}.
+
+msg.undef.label =\
+ Undefined label {0}.
+
+msg.bad.break =\
+ Unlabelled break must be inside loop or switch.
+
+msg.continue.outside =\
+ continue must be inside loop.
+
+msg.continue.nonloop =\
+ Can only continue to labeled iteration statement.
+
+msg.fn.redecl =\
+ Function "{0}" redeclared; prior definition will be ignored.
+
+# Parser
+msg.no.paren.parms =\
+ missing ( before function parameters
+
+msg.no.parm =\
+ missing formal parameter
+
+msg.no.paren.after.parms =\
+ missing ) after formal parameters
+
+msg.no.brace.body =\
+ missing '{' before function body
+
+msg.no.brace.after.body =\
+ missing } after function body
+
+msg.no.paren.cond =\
+ missing ( before condition
+
+msg.no.paren.after.cond =\
+ missing ) after condition
+
+msg.no.semi.stmt =\
+ missing ; before statement
+
+msg.no.name.after.dot =\
+ missing name after . operator
+
+msg.no.bracket.index =\
+ missing ] in index expression
+
+msg.no.paren.switch =\
+ missing ( before switch expression
+
+msg.no.paren.after.switch =\
+ missing ) after switch expression
+
+msg.no.brace.switch =\
+ missing '{' before switch body
+
+msg.bad.switch =\
+ invalid switch statement
+
+msg.no.colon.case =\
+ missing : after case expression
+
+msg.no.while.do =\
+ missing while after do-loop body
+
+msg.no.paren.for =\
+ missing ( after for
+
+msg.no.semi.for =\
+ missing ; after for-loop initializer
+
+msg.no.semi.for.cond =\
+ missing ; after for-loop condition
+
+msg.no.paren.for.ctrl =\
+ missing ) after for-loop control
+
+msg.no.paren.with =\
+ missing ( before with-statement object
+
+msg.no.paren.after.with =\
+ missing ) after with-statement object
+
+msg.bad.return =\
+ invalid return
+
+msg.no.brace.block =\
+ missing } in compound statement
+
+msg.bad.label =\
+ invalid label
+
+msg.bad.var =\
+ missing variable name
+
+msg.bad.var.init =\
+ invalid variable initialization
+
+msg.no.colon.cond =\
+ missing : in conditional expression
+
+msg.no.paren.arg =\
+ missing ) after argument list
+
+msg.no.bracket.arg =\
+ missing ] after element list
+
+msg.bad.prop =\
+ invalid property id
+
+msg.no.colon.prop =\
+ missing : after property id
+
+msg.no.brace.prop =\
+ missing } after property list
+
+msg.no.paren =\
+ missing ) in parenthetical
+
+msg.reserved.id =\
+ identifier is a reserved word
+
+msg.no.paren.catch =\
+ missing ( before catch-block condition
+
+msg.bad.catchcond =\
+ invalid catch block condition
+
+msg.catch.unreachable =\
+ any catch clauses following an unqualified catch are unreachable
+
+msg.no.brace.catchblock =\
+ missing '{' before catch-block body
+
+msg.try.no.catchfinally =\
+ ''try'' without ''catch'' or ''finally''
+
+msg.syntax =\
+ syntax error
+
+# ScriptRuntime
+msg.assn.create =\
+ Assignment to undefined "{0}" will create a new variable. \
+ Add a variable statement at the top level scope to remove this warning.
+
+msg.prop.not.found =\
+ Property not found.
+
+msg.invalid.type =\
+ Invalid JavaScript value of type {0}
+
+msg.primitive.expected =\
+ Primitive type expected (had {0} instead)
+
+msg.null.to.object =\
+ Cannot convert null to an object.
+
+msg.undef.to.object =\
+ Cannot convert undefined to an object.
+
+msg.cyclic.value =\
+ Cyclic {0} value not allowed.
+
+msg.is.not.defined =\
+ "{0}" is not defined.
+
+msg.isnt.function =\
+ {0} is not a function.
+
+msg.bad.default.value =\
+ Object''s getDefaultValue() method returned an object.
+
+msg.instanceof.not.object = \
+ Can''t use instanceof on a non-object.
+
+msg.instanceof.bad.prototype = \
+ ''prototype'' property of {0} is not an object.
+
+msg.bad.radix = \
+ illegal radix {0}.
+
+# ScriptableObject
+msg.default.value =\
+ Cannot find default value for object.
+
+msg.zero.arg.ctor =\
+ Cannot load class "{0}" which has no zero-parameter constructor.
+
+msg.multiple.ctors =\
+ Cannot have more than one constructor method, but found both {0} and {1}.
+
+msg.ctor.multiple.parms =\
+ Can''t define constructor or class {0} since more than one \
+ constructor has multiple parameters.
+
+msg.extend.scriptable =\
+ {0} must extend ScriptableObject in order to define property {1}.
+
+msg.bad.getter.parms =\
+ In order to define a property, getter {0} must have zero parameters \
+ or a single ScriptableObject parameter.
+
+msg.obj.getter.parms =\
+ Expected static or delegated getter {0} to take a ScriptableObject parameter.
+
+msg.getter.static =\
+ Getter and setter must both be static or neither be static.
+
+msg.setter2.parms =\
+ Two-parameter setter must take a ScriptableObject as its first parameter.
+
+msg.setter1.parms =\
+ Expected single parameter setter for {0}
+
+msg.setter2.expected =\
+ Expected static or delegated setter {0} to take two parameters.
+
+msg.setter.parms =\
+ Expected either one or two parameters for setter.
+
+msg.add.sealed =\
+ Cannot add a property to a sealed object.
+
+msg.remove.sealed =\
+ Cannot remove a property from a sealed object.
+
+# TokenStream
+msg.token.replaces.pushback =\
+ ungot token {0} replaces pushback token {1}
+
+msg.missing.exponent =\
+ missing exponent
+
+msg.caught.nfe =\
+ number format error: {0}
+
+msg.unterminated.string.lit =\
+ unterminated string literal
+
+msg.oct.esc.too.large =\
+ octal escape too large
+
+msg.unterminated.comment =\
+ unterminated comment
+
+msg.unterminated.re.lit =\
+ unterminated regular expression literal
+
+msg.invalid.re.flag =\
+ invalid flag after regular expression
+
+msg.no.re.input.for =\
+ no input for {0}
+
+msg.illegal.character =\
+ illegal character
+
+msg.invalid.escape =\
+ invalid Unicode escape sequence
+
+# TokensStream warnings
+msg.bad.octal.literal =\
+ illegal octal literal digit {0}; interpreting it as a decimal digit
+
+msg.reserved.keyword =\
+ illegal usage of future reserved keyword {0}; interpreting it as ordinary identifier
+
+# Undefined
+msg.undefined =\
+ The undefined value has no properties.
+
+# LiveConnect errors
+msg.java.internal.field.type =\
+ Internal error: type conversion of {0} to assign to {1} on {2} failed.
+
+msg.java.conversion.implicit_method =\
+ Can''t find converter method "{0}" on class {1}.
+
+msg.java.method.assign =\
+ Java method "{0}" cannot be assigned to.
+
+msg.java.internal.private =\
+ Internal error: attempt to access private/protected field "{0}".
+
+msg.java.no_such_method =\
+ Can''t find method {0}.
+
+msg.script.is.not.constructor =\
+ Script objects are not constructors.
+
+msg.nonjava.method =\
+ Java method "{0}" was invoked with a ''this'' value that was not a Java object.
+
+msg.java.member.not.found =\
+ Java class "{0}" has no public instance field or method named "{1}".
+
+msg.pkg.int =\
+ Java package names may not be numbers.
+
+# ImporterTopLevel
+msg.ambig.import =\
+ Ambiguous import: "{0}" and and "{1}".
+
+msg.not.pkg =\
+ Function importPackage must be called with a package; had "{0}" instead.
+
+msg.not.class =\
+ Function importClass must be called with a class; had "{0}" instead.
+
+msg.prop.defined =\
+ Cannot import "{0}" since a property by that name is already defined.
+
+# Arrays
+msg.arraylength.bad =\
+ Inappropriate array length.
+
+# URI
+msg.bad.uri =\
+ Malformed URI sequence.
+
+# Number
+msg.bad.precision =\
+ Precision {0} out of range.
+
+# JSNI
+msg.jsni.expected.char =\
+ Expected "{0}" in JSNI reference
+
+msg.jsni.expected.identifier =\
+ Expected an identifier in JSNI reference
+
+msg.jsni.unexpected.identifier =\
+ Unexpected character in JSNI reference
+
+msg.jsni.expected.param.type =\
+ Expected a valid parameter type signature in JSNI method reference
+
+msg.jsni.unsupported.with =\
+ The ''with'' statement is unsupported in JSNI blocks (perhaps you could use a local variable instead?)
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/Node.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/Node.java
new file mode 100644
index 00000000000..1c279647753
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/Node.java
@@ -0,0 +1,725 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roger Lawrence
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+/**
+ * This class implements the root of the intermediate representation.
+ *
+ * @author Norris Boyd
+ * @author Mike McCabe
+ */
+
+public class Node implements Cloneable {
+
+ private static class NumberNode extends Node {
+
+ NumberNode(double number) {
+ super(TokenStream.NUMBER);
+ this.number = number;
+ }
+
+ @Override
+ public double getDouble() {
+ return this.number;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof NumberNode
+ && getDouble() == ((NumberNode) o).getDouble();
+ }
+
+ private double number;
+ }
+
+ private static class StringNode extends Node {
+
+ StringNode(int type, String str) {
+ super(type);
+ if (null == str) {
+ throw new IllegalArgumentException("StringNode: str is null");
+ }
+ this.str = str;
+ }
+
+ /** returns the string content.
+ * @return non null.
+ */
+ @Override
+ public String getString() {
+ return this.str;
+ }
+
+ /** sets the string content.
+ * @param str the new value. Non null.
+ */
+ @Override
+ public void setString(String str) {
+ if (null == str) {
+ throw new IllegalArgumentException("StringNode: str is null");
+ }
+ this.str = str;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof StringNode)) { return false; }
+ return o instanceof StringNode
+ && this.str.equals(((StringNode) o).str);
+ }
+
+ private String str;
+ }
+
+ public Node(int nodeType) {
+ type = nodeType;
+ }
+
+ public Node(int nodeType, Node child) {
+ type = nodeType;
+ first = last = child;
+ child.next = null;
+ }
+
+ public Node(int nodeType, Node left, Node right) {
+ type = nodeType;
+ first = left;
+ last = right;
+ left.next = right;
+ right.next = null;
+ }
+
+ public Node(int nodeType, Node left, Node mid, Node right) {
+ type = nodeType;
+ first = left;
+ last = right;
+ left.next = mid;
+ mid.next = right;
+ right.next = null;
+ }
+
+ public Node(int nodeType, Node left, Node mid, Node mid2, Node right) {
+ type = nodeType;
+ first = left;
+ last = right;
+ left.next = mid;
+ mid.next = mid2;
+ mid2.next = right;
+ right.next = null;
+ }
+
+ public Node(int nodeType, Node[] children) {
+ this.type = nodeType;
+ if (children.length != 0) {
+ this.first = children[0];
+ this.last = children[children.length - 1];
+
+ for (int i = 1; i < children.length; i++) {
+ if (null != children[i - 1].next) {
+ // fail early on loops. implies same node in array twice
+ throw new IllegalArgumentException("duplicate child");
+ }
+ children[i - 1].next = children[i];
+ }
+ if (null != this.last.next) {
+ // fail early on loops. implies same node in array twice
+ throw new IllegalArgumentException("duplicate child");
+ }
+ }
+ }
+
+ public Node(int nodeType, int value) {
+ type = nodeType;
+ intDatum = value;
+ }
+
+ public Node(int nodeType, Node child, int value) {
+ this(nodeType, child);
+ intDatum = value;
+ }
+
+ public Node(int nodeType, Node left, Node right, int value) {
+ this(nodeType, left, right);
+ intDatum = value;
+ }
+
+ public Node(int nodeType, Node left, Node mid, Node right, int value) {
+ this(nodeType, left, mid, right);
+ intDatum = value;
+ }
+
+ public static Node newNumber(double number) {
+ return new NumberNode(number);
+ }
+
+ public static Node newString(String str) {
+ return new StringNode(TokenStream.STRING, str);
+ }
+
+ public static Node newString(int type, String str) {
+ return new StringNode(type, str);
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public int getIntDatum() {
+ return this.intDatum;
+ }
+
+ public boolean hasChildren() {
+ return first != null;
+ }
+
+ public Node getFirstChild() {
+ return first;
+ }
+
+ public Node getLastChild() {
+ return last;
+ }
+
+ public Node getNext() {
+ return next;
+ }
+
+ public int getChildCount() {
+ int c = 0;
+ for (Node n = first; n != null; n = n.next)
+ c++;
+
+ return c;
+ }
+
+
+ public Node getChildBefore(Node child) {
+ if (child == first)
+ return null;
+ Node n = first;
+ while (n.next != child) {
+ n = n.next;
+ if (n == null)
+ throw new RuntimeException("node is not a child");
+ }
+ return n;
+ }
+
+ public Node getLastSibling() {
+ Node n = this;
+ while (n.next != null) {
+ n = n.next;
+ }
+ return n;
+ }
+
+ public void addChildToFront(Node child) {
+ child.next = first;
+ first = child;
+ if (last == null) {
+ last = child;
+ }
+ }
+
+ public void addChildToBack(Node child) {
+ child.next = null;
+ if (last == null) {
+ first = last = child;
+ return;
+ }
+ last.next = child;
+ last = child;
+ }
+
+ public void addChildrenToFront(Node children) {
+ Node lastSib = children.getLastSibling();
+ lastSib.next = first;
+ first = children;
+ if (last == null) {
+ last = lastSib;
+ }
+ }
+
+ public void addChildrenToBack(Node children) {
+ if (last != null) {
+ last.next = children;
+ }
+ last = children.getLastSibling();
+ if (first == null) {
+ first = children;
+ }
+ }
+
+ /**
+ * Add 'child' before 'node'.
+ */
+ public void addChildBefore(Node newChild, Node node) {
+ if (newChild.next != null)
+ throw new RuntimeException(
+ "newChild had siblings in addChildBefore");
+ if (first == node) {
+ newChild.next = first;
+ first = newChild;
+ return;
+ }
+ Node prev = getChildBefore(node);
+ addChildAfter(newChild, prev);
+ }
+
+ /**
+ * Add 'child' after 'node'.
+ */
+ public void addChildAfter(Node newChild, Node node) {
+ if (newChild.next != null)
+ throw new RuntimeException(
+ "newChild had siblings in addChildAfter");
+ newChild.next = node.next;
+ node.next = newChild;
+ if (last == node)
+ last = newChild;
+ }
+
+ public void removeChild(Node child) {
+ Node prev = getChildBefore(child);
+ if (prev == null)
+ first = first.next;
+ else
+ prev.next = child.next;
+ if (child == last) last = prev;
+ child.next = null;
+ }
+
+ public void replaceChild(Node child, Node newChild) {
+ newChild.next = child.next;
+ if (child == first) {
+ first = newChild;
+ } else {
+ Node prev = getChildBefore(child);
+ prev.next = newChild;
+ }
+ if (child == last)
+ last = newChild;
+ child.next = null;
+ }
+
+ public void replaceChildAfter(Node prevChild, Node newChild) {
+ Node child = prevChild.next;
+ newChild.next = child.next;
+ prevChild.next = newChild;
+ if (child == last)
+ last = newChild;
+ child.next = null;
+ }
+
+ public static final int
+ TARGET_PROP = 1,
+ BREAK_PROP = 2,
+ CONTINUE_PROP = 3,
+ ENUM_PROP = 4,
+ FUNCTION_PROP = 5,
+ TEMP_PROP = 6,
+ LOCAL_PROP = 7,
+ CODEOFFSET_PROP = 8,
+ FIXUPS_PROP = 9,
+ VARS_PROP = 10,
+ USES_PROP = 11,
+ REGEXP_PROP = 12,
+ CASES_PROP = 13,
+ DEFAULT_PROP = 14,
+ CASEARRAY_PROP = 15,
+ SOURCENAME_PROP = 16,
+ SOURCE_PROP = 17,
+ TYPE_PROP = 18,
+ SPECIAL_PROP_PROP = 19,
+ LABEL_PROP = 20,
+ FINALLY_PROP = 21,
+ LOCALCOUNT_PROP = 22,
+ /*
+ the following properties are defined and manipulated by the
+ optimizer -
+ TARGETBLOCK_PROP - the block referenced by a branch node
+ VARIABLE_PROP - the variable referenced by a BIND or NAME node
+ LASTUSE_PROP - that variable node is the last reference before
+ a new def or the end of the block
+ ISNUMBER_PROP - this node generates code on Number children and
+ delivers a Number result (as opposed to Objects)
+ DIRECTCALL_PROP - this call node should emit code to test the function
+ object against the known class and call diret if it
+ matches.
+ */
+
+ TARGETBLOCK_PROP = 23,
+ VARIABLE_PROP = 24,
+ LASTUSE_PROP = 25,
+ ISNUMBER_PROP = 26,
+ DIRECTCALL_PROP = 27,
+
+ BASE_LINENO_PROP = 28,
+ END_LINENO_PROP = 29,
+ SPECIALCALL_PROP = 30,
+ DEBUGSOURCE_PROP = 31;
+
+ public static final int // this value of the ISNUMBER_PROP specifies
+ BOTH = 0, // which of the children are Number types
+ LEFT = 1,
+ RIGHT = 2;
+
+ private static final String propToString(int propType) {
+ switch (propType) {
+ case TARGET_PROP: return "target";
+ case BREAK_PROP: return "break";
+ case CONTINUE_PROP: return "continue";
+ case ENUM_PROP: return "enum";
+ case FUNCTION_PROP: return "function";
+ case TEMP_PROP: return "temp";
+ case LOCAL_PROP: return "local";
+ case CODEOFFSET_PROP: return "codeoffset";
+ case FIXUPS_PROP: return "fixups";
+ case VARS_PROP: return "vars";
+ case USES_PROP: return "uses";
+ case REGEXP_PROP: return "regexp";
+ case CASES_PROP: return "cases";
+ case DEFAULT_PROP: return "default";
+ case CASEARRAY_PROP: return "casearray";
+ case SOURCENAME_PROP: return "sourcename";
+ case SOURCE_PROP: return "source";
+ case TYPE_PROP: return "type";
+ case SPECIAL_PROP_PROP: return "special_prop";
+ case LABEL_PROP: return "label";
+ case FINALLY_PROP: return "finally";
+ case LOCALCOUNT_PROP: return "localcount";
+
+ case TARGETBLOCK_PROP: return "targetblock";
+ case VARIABLE_PROP: return "variable";
+ case LASTUSE_PROP: return "lastuse";
+ case ISNUMBER_PROP: return "isnumber";
+ case DIRECTCALL_PROP: return "directcall";
+
+ case BASE_LINENO_PROP: return "base_lineno";
+ case END_LINENO_PROP: return "end_lineno";
+ case SPECIALCALL_PROP: return "specialcall";
+ case DEBUGSOURCE_PROP: return "debugsource";
+
+ default: Context.codeBug();
+
+ }
+ return null;
+ }
+
+ public Object getProp(int propType) {
+ if (props == null)
+ return null;
+ return props.getObject(propType);
+ }
+
+ public int getIntProp(int propType, int defaultValue) {
+ if (props == null)
+ return defaultValue;
+ return props.getInt(propType, defaultValue);
+ }
+
+ public int getExistingIntProp(int propType) {
+ return props.getExistingInt(propType);
+ }
+
+ public void putProp(int propType, Object prop) {
+ if (prop == null) {
+ removeProp(propType);
+ }
+ else {
+ if (props == null) {
+ props = new UintMap(2);
+ }
+ props.put(propType, prop);
+ }
+ }
+
+ public void putIntProp(int propType, int prop) {
+ if (props == null)
+ props = new UintMap(2);
+ props.put(propType, prop);
+ }
+
+ public void removeProp(int propType) {
+ if (props != null) {
+ props.remove(propType);
+ }
+ }
+
+ public int getOperation() {
+ switch (type) {
+ case TokenStream.EQOP:
+ case TokenStream.RELOP:
+ case TokenStream.UNARYOP:
+ case TokenStream.PRIMARY:
+ return intDatum;
+ }
+ Context.codeBug();
+ return 0;
+ }
+
+ public int getLineno() {
+ if (hasLineno()) {
+ return intDatum;
+ }
+ return -1;
+ }
+
+ private boolean hasLineno() {
+ switch (type) {
+ case TokenStream.EXPRSTMT:
+ case TokenStream.BLOCK:
+ case TokenStream.VAR:
+ case TokenStream.WHILE:
+ case TokenStream.DO:
+ case TokenStream.SWITCH:
+ case TokenStream.CATCH:
+ case TokenStream.THROW:
+ case TokenStream.RETURN:
+ case TokenStream.BREAK:
+ case TokenStream.CONTINUE:
+ case TokenStream.WITH:
+ return true;
+ }
+ return false;
+ }
+
+ /** Can only be called when getType() == TokenStream.NUMBER */
+ public double getDouble() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException(this + " is not a number node");
+ }
+
+ /** Can only be called when node has String context. */
+ public String getString() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException(this + " is not a string node");
+ }
+
+ /** Can only be called when node has String context. */
+ public void setString(String s) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException(this + " is not a string node");
+ }
+
+ /**
+ * Not usefully implemented.
+ */
+ @Override
+ public final int hashCode() {
+ assert false : "hashCode not designed";
+ return 42; // any arbitrary constant will do
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Node)) { return false; }
+ return hasLineno()
+ || this.getIntDatum() == ((Node) o).getIntDatum();
+ }
+
+ public Node cloneNode() {
+ Node result;
+ try {
+ result = (Node) super.clone();
+ result.next = null;
+ result.first = null;
+ result.last = null;
+ }
+ catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ return result;
+ }
+
+ public Node cloneTree() {
+ Node result = cloneNode();
+ for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) {
+ Node n2clone = n2.cloneTree();
+ if (result.last != null) {
+ result.last.next = n2clone;
+ }
+ if (result.first == null) {
+ result.first = n2clone;
+ }
+ result.last = n2clone;
+ }
+ return result;
+ }
+
+
+
+ @Override
+ public String toString() {
+ if (Context.printTrees) {
+ StringBuffer sb = new StringBuffer(TokenStream.tokenToName(type));
+ if (this instanceof StringNode) {
+ sb.append(' ');
+ sb.append(getString());
+ } else {
+ switch (type) {
+ case TokenStream.TARGET:
+ sb.append(' ');
+ sb.append(hashCode());
+ break;
+ case TokenStream.NUMBER:
+ sb.append(' ');
+ sb.append(getDouble());
+ break;
+ case TokenStream.FUNCTION:
+ sb.append(' ');
+ sb.append(first.getString());
+ break;
+ }
+ }
+ if (intDatum != -1) {
+ sb.append(' ');
+ sb.append(intDatum);
+ }
+ if (props == null)
+ return sb.toString();
+
+ int[] keys = props.getKeys();
+ for (int i = 0; i != keys.length; ++i) {
+ int key = keys[i];
+ sb.append(" [");
+ sb.append(propToString(key));
+ sb.append(": ");
+ switch (key) {
+ case FIXUPS_PROP : // can't add this as it recurses
+ sb.append("fixups property");
+ break;
+ case SOURCE_PROP : // can't add this as it has unprintables
+ sb.append("source property");
+ break;
+ case TARGETBLOCK_PROP : // can't add this as it recurses
+ sb.append("target block property");
+ break;
+ case LASTUSE_PROP : // can't add this as it is dull
+ sb.append("last use property");
+ break;
+ default :
+ Object obj = props.getObject(key);
+ if (obj != null) {
+ sb.append(obj.toString());
+ } else {
+ sb.append(props.getExistingInt(key));
+ }
+ break;
+ }
+ sb.append(']');
+ }
+ return sb.toString();
+ }
+ return null;
+ }
+
+ public String toStringTree() {
+ return toStringTreeHelper(0);
+ }
+
+
+ private String toStringTreeHelper(int level) {
+ if (Context.printTrees) {
+ StringBuffer s = new StringBuffer();
+ for (int i=0; i < level; i++) {
+ s.append(" ");
+ }
+ s.append(toString());
+ s.append('\n');
+ for (Node cursor = getFirstChild(); cursor != null;
+ cursor = cursor.getNext())
+ {
+ Node n = cursor;
+ s.append(n.toStringTreeHelper(level+1));
+ }
+ return s.toString();
+ }
+ return "";
+ }
+
+ /**
+ * Checks if the subtree under this node is the same as another subtree.
+ * Returns null if it's equal, or a message describing the differences.
+ */
+ public String checkTreeEquals(Node node2) {
+ boolean eq = false;
+
+ if (type == node2.getType() &&
+ getChildCount() == node2.getChildCount() &&
+ getClass() == node2.getClass()) {
+
+ eq = this.equals(node2);
+ }
+
+ if (!eq) {
+ return "Node tree inequality:\nTree1:\n" + toStringTreeHelper(1) +
+ "\n\nTree2:\n" + node2.toStringTreeHelper(1);
+ }
+
+ String res = null;
+ Node n, n2;
+ for (n = first, n2 = node2.first;
+ res == null && n != null;
+ n = n.next, n2 = n2.next) {
+ res = n.checkTreeEquals(n2);
+ if (res != null) {
+ return res;
+ }
+ }
+ return res;
+ }
+
+ public void setIsSyntheticBlock(boolean val) {
+ isSyntheticBlock = val;
+ }
+
+ public boolean isSyntheticBlock() {
+ return isSyntheticBlock;
+ }
+
+ int type; // type of the node; TokenStream.NAME for example
+ Node next; // next sibling
+ private Node first; // first element of a linked list of children
+ private Node last; // last element of a linked list of children
+ private int intDatum = -1; // encapsulated int data; depends on type
+ private UintMap props;
+ private boolean isSyntheticBlock = false;
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/ObjToIntMap.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/ObjToIntMap.java
new file mode 100644
index 00000000000..fd2fac44ed7
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/ObjToIntMap.java
@@ -0,0 +1,697 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * 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
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Map to associate objects to integers.
+ * The map does not synchronize any of its operation, so either use
+ * it from a single thread or do own synchronization or perform all mutation
+ * operations on one thread before passing the map to others
+ *
+ * @author Igor Bukanov
+ *
+ */
+
+public class ObjToIntMap implements Serializable {
+
+ public static final Object NULL_VALUE = new String("");
+
+// Map implementation via hashtable,
+// follows "The Art of Computer Programming" by Donald E. Knuth
+
+// ObjToIntMap is a copy cat of ObjToIntMap with API adjusted to object keys
+
+ public static class Iterator {
+
+ Iterator(ObjToIntMap master) {
+ this.master = master;
+ }
+
+ final void init(Object[] keys, int[] values, int keyCount) {
+ this.keys = keys;
+ this.values = values;
+ this.cursor = -1;
+ this.remaining = keyCount;
+ }
+
+ public void start() {
+ master.initIterator(this);
+ next();
+ }
+
+ public boolean done() {
+ return remaining < 0;
+ }
+
+ public void next() {
+ if (remaining == -1) Context.codeBug();
+ if (remaining == 0) {
+ remaining = -1;
+ cursor = -1;
+ }else {
+ for (++cursor; ; ++cursor) {
+ Object key = keys[cursor];
+ if (key != null && key != DELETED) {
+ --remaining;
+ break;
+ }
+ }
+ }
+ }
+
+ public Object getKey() {
+ Object key = keys[cursor];
+ if (key == NULL_VALUE) { key = null; }
+ return key;
+ }
+
+ public int getValue() {
+ return values[cursor];
+ }
+
+ public void setValue(int value) {
+ values[cursor] = value;
+ }
+
+ ObjToIntMap master;
+ private int cursor;
+ private int remaining;
+ private Object[] keys;
+ private int[] values;
+ }
+
+ public ObjToIntMap() {
+ this(4);
+ }
+
+ public ObjToIntMap(int keyCountHint) {
+ if (keyCountHint < 0) Context.codeBug();
+ // Table grow when number of stored keys >= 3/4 of max capacity
+ int minimalCapacity = keyCountHint * 4 / 3;
+ int i;
+ for (i = 2; (1 << i) < minimalCapacity; ++i) { }
+ power = i;
+ if (check && power < 2) Context.codeBug();
+ }
+
+ public boolean isEmpty() {
+ return keyCount == 0;
+ }
+
+ public int size() {
+ return keyCount;
+ }
+
+ public boolean has(Object key) {
+ if (key == null) { key = NULL_VALUE; }
+ return 0 <= findIndex(key);
+ }
+
+ /**
+ * Get integer value assigned with key.
+ * @return key integer value or defaultValue if key is absent
+ */
+ public int get(Object key, int defaultValue) {
+ if (key == null) { key = NULL_VALUE; }
+ int index = findIndex(key);
+ if (0 <= index) {
+ return values[index];
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Get integer value assigned with key.
+ * @return key integer value
+ * @throws RuntimeException if key does not exist
+ */
+ public int getExisting(Object key) {
+ if (key == null) { key = NULL_VALUE; }
+ int index = findIndex(key);
+ if (0 <= index) {
+ return values[index];
+ }
+ // Key must exist
+ Context.codeBug();
+ return 0;
+ }
+
+ public void put(Object key, int value) {
+ if (key == null) { key = NULL_VALUE; }
+ int index = ensureIndex(key);
+ values[index] = value;
+ }
+
+ /**
+ * If table already contains a key that equals to keyArg, return that key
+ * while setting its value to zero, otherwise add keyArg with 0 value to
+ * the table and return it.
+ */
+ public Object intern(Object keyArg) {
+ boolean nullKey = false;
+ if (keyArg == null) {
+ nullKey = true;
+ keyArg = NULL_VALUE;
+ }
+ int index = ensureIndex(keyArg);
+ values[index] = 0;
+ return (nullKey) ? null : keys[index];
+ }
+
+ public void remove(Object key) {
+ if (key == null) { key = NULL_VALUE; }
+ int index = findIndex(key);
+ if (0 <= index) {
+ keys[index] = DELETED;
+ --keyCount;
+ }
+ }
+
+ public void clear() {
+ int i = keys.length;
+ while (i != 0) {
+ keys[--i] = null;
+ }
+ keyCount = 0;
+ occupiedCount = 0;
+ }
+
+ public Iterator newIterator() {
+ return new Iterator(this);
+ }
+
+ // The sole purpose of the method is to avoid accessing private fields
+ // from the Iterator inner class to workaround JDK 1.1 compiler bug which
+ // generates code triggering VerifierError on recent JVMs
+ final void initIterator(Iterator i) {
+ i.init(keys, values, keyCount);
+ }
+
+ /** Return array of present keys */
+ public Object[] getKeys() {
+ Object[] array = new Object[keyCount];
+ getKeys(array, 0);
+ return array;
+ }
+
+ public void getKeys(Object[] array, int offset) {
+ int count = keyCount;
+ for (int i = 0; count != 0; ++i) {
+ Object key = keys[i];
+ if (key != null && key != DELETED) {
+ if (key == NULL_VALUE) { key = null; }
+ array[offset] = key;
+ ++offset;
+ --count;
+ }
+ }
+ }
+
+ private static int tableLookupStep(int fraction, int mask, int power) {
+ int shift = 32 - 2 * power;
+ if (shift >= 0) {
+ return ((fraction >>> shift) & mask) | 1;
+ }
+ else {
+ return (fraction & (mask >>> -shift)) | 1;
+ }
+ }
+
+ private int findIndex(Object key) {
+ if (keys != null) {
+ int hash = key.hashCode();
+ int fraction = hash * A;
+ int index = fraction >>> (32 - power);
+ Object test = keys[index];
+ if (test != null) {
+ int N = 1 << power;
+ if (test == key
+ || (values[N + index] == hash && test.equals(key)))
+ {
+ return index;
+ }
+ // Search in table after first failed attempt
+ int mask = N - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int n = 0;
+ for (;;) {
+ if (check) {
+ if (n >= occupiedCount) Context.codeBug();
+ ++n;
+ }
+ index = (index + step) & mask;
+ test = keys[index];
+ if (test == null) {
+ break;
+ }
+ if (test == key
+ || (values[N + index] == hash && test.equals(key)))
+ {
+ return index;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+// Insert key that is not present to table without deleted entries
+// and enough free space
+ private int insertNewKey(Object key, int hash) {
+ if (check && occupiedCount != keyCount) Context.codeBug();
+ if (check && keyCount == 1 << power) Context.codeBug();
+ int fraction = hash * A;
+ int index = fraction >>> (32 - power);
+ int N = 1 << power;
+ if (keys[index] != null) {
+ int mask = N - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int firstIndex = index;
+ do {
+ if (check && keys[index] == DELETED) Context.codeBug();
+ index = (index + step) & mask;
+ if (check && firstIndex == index) Context.codeBug();
+ } while (keys[index] != null);
+ }
+ keys[index] = key;
+ values[N + index] = hash;
+ ++occupiedCount;
+ ++keyCount;
+
+ return index;
+ }
+
+ private void rehashTable() {
+ if (keys == null) {
+ if (check && keyCount != 0) Context.codeBug();
+ if (check && occupiedCount != 0) Context.codeBug();
+ int N = 1 << power;
+ keys = new Object[N];
+ values = new int[2 * N];
+ }
+ else {
+ // Check if removing deleted entries would free enough space
+ if (keyCount * 2 >= occupiedCount) {
+ // Need to grow: less then half of deleted entries
+ ++power;
+ }
+ int N = 1 << power;
+ Object[] oldKeys = keys;
+ int[] oldValues = values;
+ int oldN = oldKeys.length;
+ keys = new Object[N];
+ values = new int[2 * N];
+
+ int remaining = keyCount;
+ occupiedCount = keyCount = 0;
+ for (int i = 0; remaining != 0; ++i) {
+ Object key = oldKeys[i];
+ if (key != null && key != DELETED) {
+ int keyHash = oldValues[oldN + i];
+ int index = insertNewKey(key, keyHash);
+ values[index] = oldValues[i];
+ --remaining;
+ }
+ }
+ }
+ }
+
+// Ensure key index creating one if necessary
+ private int ensureIndex(Object key) {
+ int hash = key.hashCode();
+ int index = -1;
+ int firstDeleted = -1;
+ if (keys != null) {
+ int fraction = hash * A;
+ index = fraction >>> (32 - power);
+ Object test = keys[index];
+ if (test != null) {
+ int N = 1 << power;
+ if (test == key
+ || (values[N + index] == hash && test.equals(key)))
+ {
+ return index;
+ }
+ if (test == DELETED) {
+ firstDeleted = index;
+ }
+
+ // Search in table after first failed attempt
+ int mask = N - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int n = 0;
+ for (;;) {
+ if (check) {
+ if (n >= occupiedCount) Context.codeBug();
+ ++n;
+ }
+ index = (index + step) & mask;
+ test = keys[index];
+ if (test == null) {
+ break;
+ }
+ if (test == key
+ || (values[N + index] == hash && test.equals(key)))
+ {
+ return index;
+ }
+ if (test == DELETED && firstDeleted < 0) {
+ firstDeleted = index;
+ }
+ }
+ }
+ }
+ // Inserting of new key
+ if (check && keys != null && keys[index] != null)
+ Context.codeBug();
+ if (firstDeleted >= 0) {
+ index = firstDeleted;
+ }
+ else {
+ // Need to consume empty entry: check occupation level
+ if (keys == null || occupiedCount * 4 >= (1 << power) * 3) {
+ // Too litle unused entries: rehash
+ rehashTable();
+ return insertNewKey(key, hash);
+ }
+ ++occupiedCount;
+ }
+ keys[index] = key;
+ values[(1 << power) + index] = hash;
+ ++keyCount;
+ return index;
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+
+ int count = keyCount;
+ for (int i = 0; count != 0; ++i) {
+ Object key = keys[i];
+ if (key != null && key != DELETED) {
+ --count;
+ out.writeObject(key);
+ out.writeInt(values[i]);
+ }
+ }
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ int writtenKeyCount = keyCount;
+ if (writtenKeyCount != 0) {
+ keyCount = 0;
+ int N = 1 << power;
+ keys = new Object[N];
+ values = new int[2 * N];
+ for (int i = 0; i != writtenKeyCount; ++i) {
+ Object key = in.readObject();
+ int hash = key.hashCode();
+ int index = insertNewKey(key, hash);
+ values[index] = in.readInt();
+ }
+ }
+ }
+
+ static final long serialVersionUID = 3396438333234169727L;
+
+// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
+// See Knuth etc.
+ private static final int A = 0x9e3779b9;
+
+ private static final Object DELETED = new Object();
+
+// Structure of kyes and values arrays (N == 1 << power):
+// keys[0 <= i < N]: key value or null or DELETED mark
+// values[0 <= i < N]: value of key at keys[i]
+// values[N <= i < 2*N]: hash code of key at keys[i-N]
+
+ private transient Object[] keys;
+ private transient int[] values;
+
+ private int power;
+ private int keyCount;
+ private transient int occupiedCount; // == keyCount + deleted_count
+
+// If true, enables consitency checks
+ private static final boolean check = false;
+
+/* TEST START
+
+ public static void main(String[] args) {
+ if (!check) {
+ System.err.println("Set check to true and re-run");
+ throw new RuntimeException("Set check to true and re-run");
+ }
+
+ ObjToIntMap map;
+ map = new ObjToIntMap(0);
+ testHash(map, 3);
+ map = new ObjToIntMap(0);
+ testHash(map, 10 * 1000);
+ map = new ObjToIntMap();
+ testHash(map, 10 * 1000);
+ map = new ObjToIntMap(30 * 1000);
+ testHash(map, 10 * 100);
+ map.clear();
+ testHash(map, 4);
+ map = new ObjToIntMap(0);
+ testHash(map, 10 * 100);
+ }
+
+ private static void testHash(ObjToIntMap map, int N) {
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ check(-1 == map.get(key, -1));
+ map.put(key, i);
+ check(i == map.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ map.put(key, i);
+ check(i == map.get(key, -1));
+ }
+
+ check(map.size() == N);
+
+ System.out.print("."); System.out.flush();
+ Object[] keys = map.getKeys();
+ check(keys.length == N);
+ for (int i = 0; i != N; ++i) {
+ Object key = keys[i];
+ check(map.has(key));
+ }
+
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ check(i == map.get(key, -1));
+ }
+
+ int Nsqrt = -1;
+ for (int i = 0; ; ++i) {
+ if (i * i >= N) {
+ Nsqrt = i;
+ break;
+ }
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i * i);
+ map.put(key, i);
+ check(i == map.get(key, -1));
+ }
+
+ check(map.size() == 2 * N - Nsqrt);
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i * i);
+ check(i == map.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(-1 - i * i);
+ map.put(key, i);
+ check(i == map.get(key, -1));
+ }
+
+ check(map.size() == 3 * N - Nsqrt);
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(-1 - i * i);
+ map.remove(key);
+ check(!map.has(key));
+ }
+
+ check(map.size() == 2 * N - Nsqrt);
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i * i);
+ check(i == map.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ int j = intSqrt(i);
+ if (j * j == i) {
+ check(j == map.get(key, -1));
+ }else {
+ check(i == map.get(key, -1));
+ }
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i * i);
+ map.remove(key);
+ check(-2 == map.get(key, -2));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ map.put(key, i);
+ check(i == map.get(key, -2));
+ }
+
+ check(map.size() == N);
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ check(i == map.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ ObjToIntMap copy = (ObjToIntMap)writeAndRead(map);
+ check(copy.size() == N);
+
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ check(i == copy.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ checkSameMaps(copy, map);
+
+ System.out.println(); System.out.flush();
+ }
+
+ private static void checkSameMaps(ObjToIntMap map1, ObjToIntMap map2) {
+ check(map1.size() == map2.size());
+ Object[] keys = map1.getKeys();
+ check(keys.length == map1.size());
+ for (int i = 0; i != keys.length; ++i) {
+ check(map1.get(keys[i], -1) == map2.get(keys[i], -1));
+ }
+ }
+
+ private static void check(boolean condition) {
+ if (!condition) Context.codeBug();
+ }
+
+ private static Object[] testPool;
+
+ private static Object testKey(int i) {
+ int MAX_POOL = 100;
+ if (0 <= i && i < MAX_POOL) {
+ if (testPool != null && testPool[i] != null) {
+ return testPool[i];
+ }
+ }
+ Object x = new Double(i + 0.5);
+ if (0 <= i && i < MAX_POOL) {
+ if (testPool == null) {
+ testPool = new Object[MAX_POOL];
+ }
+ testPool[i] = x;
+ }
+ return x;
+ }
+
+ private static int intSqrt(int i) {
+ int approx = (int)Math.sqrt(i) + 1;
+ while (approx * approx > i) {
+ --approx;
+ }
+ return approx;
+ }
+
+ private static Object writeAndRead(Object obj) {
+ try {
+ java.io.ByteArrayOutputStream
+ bos = new java.io.ByteArrayOutputStream();
+ java.io.ObjectOutputStream
+ out = new java.io.ObjectOutputStream(bos);
+ out.writeObject(obj);
+ out.close();
+ byte[] data = bos.toByteArray();
+ java.io.ByteArrayInputStream
+ bis = new java.io.ByteArrayInputStream(data);
+ java.io.ObjectInputStream
+ in = new java.io.ObjectInputStream(bis);
+ Object result = in.readObject();
+ in.close();
+ return result;
+ }catch (Exception ex) {
+ throw new RuntimeException("Unexpected");
+ }
+ }
+
+// TEST END */
+
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/Parser.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/Parser.java
new file mode 100644
index 00000000000..6d571da6afe
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/Parser.java
@@ -0,0 +1,1510 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Ang
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+import java.io.IOException;
+
+/**
+ * This class implements the JavaScript parser.
+ *
+ * It is based on the C source files jsparse.c and jsparse.h in the jsref
+ * package.
+ *
+ * @see TokenStream
+ *
+ * @author Mike McCabe
+ * @author Brendan Eich
+ */
+
+public class Parser {
+
+ public Parser(IRFactory nf) {
+ this.nf = nf;
+ }
+
+ private void mustMatchToken(TokenStream ts, int toMatch, String messageId)
+ throws IOException, JavaScriptException {
+ int tt;
+ if ((tt = ts.getToken()) != toMatch) {
+ reportError(ts, messageId);
+ ts.ungetToken(tt); // In case the parser decides to continue
+ }
+ }
+
+ private void reportError(TokenStream ts, String messageId)
+ throws JavaScriptException {
+ this.ok = false;
+ ts.reportSyntaxError(messageId, null);
+
+ /*
+ * Throw an exception to unwind the recursive descent parse. We use
+ * JavaScriptException here even though it is really a different use of the
+ * exception than it is usually used for.
+ */
+ throw new JavaScriptException(messageId);
+ }
+
+ /*
+ * Build a parse tree from the given TokenStream.
+ *
+ * @param ts the TokenStream to parse
+ *
+ * @return an Object representing the parsed program. If the parse fails, null
+ * will be returned. (The parse failure will result in a call to the current
+ * Context's ErrorReporter.)
+ */
+ public Object parse(TokenStream ts) throws IOException {
+ this.ok = true;
+ sourceTop = 0;
+ functionNumber = 0;
+
+ int tt; // last token from getToken();
+ int baseLineno = ts.getLineno(); // line number where source starts
+
+ /*
+ * so we have something to add nodes to until we've collected all the source
+ */
+ Object tempBlock = nf.createLeaf(TokenStream.BLOCK);
+ ((Node) tempBlock).setIsSyntheticBlock(true);
+
+ while (true) {
+ ts.flags |= ts.TSF_REGEXP;
+ tt = ts.getToken();
+ ts.flags &= ~ts.TSF_REGEXP;
+
+ if (tt <= ts.EOF) {
+ break;
+ }
+
+ if (tt == ts.FUNCTION) {
+ try {
+ nf.addChildToBack(tempBlock, function(ts, false));
+ } catch (JavaScriptException e) {
+ this.ok = false;
+ break;
+ }
+ } else {
+ ts.ungetToken(tt);
+ nf.addChildToBack(tempBlock, statement(ts));
+ }
+ }
+
+ if (!this.ok) {
+ // XXX ts.clearPushback() call here?
+ return null;
+ }
+
+ Object pn = nf.createScript(tempBlock, ts.getSourceName(), baseLineno, ts
+ .getLineno(), sourceToString(0));
+ ((Node) pn).setIsSyntheticBlock(true);
+ return pn;
+ }
+
+ /*
+ * The C version of this function takes an argument list, which doesn't seem
+ * to be needed for tree generation... it'd only be useful for checking
+ * argument hiding, which I'm not doing anyway...
+ */
+ private Object parseFunctionBody(TokenStream ts) throws IOException {
+ int oldflags = ts.flags;
+ ts.flags &= ~(TokenStream.TSF_RETURN_EXPR | TokenStream.TSF_RETURN_VOID);
+ ts.flags |= TokenStream.TSF_FUNCTION;
+
+ Object pn = nf.createBlock(ts.getLineno());
+ try {
+ int tt;
+ while ((tt = ts.peekToken()) > ts.EOF && tt != ts.RC) {
+ if (tt == TokenStream.FUNCTION) {
+ ts.getToken();
+ nf.addChildToBack(pn, function(ts, false));
+ } else {
+ nf.addChildToBack(pn, statement(ts));
+ }
+ }
+ } catch (JavaScriptException e) {
+ this.ok = false;
+ } finally {
+ // also in finally block:
+ // flushNewLines, clearPushback.
+
+ ts.flags = oldflags;
+ }
+
+ return pn;
+ }
+
+ private Object function(TokenStream ts, boolean isExpr) throws IOException,
+ JavaScriptException {
+ int baseLineno = ts.getLineno(); // line number where source starts
+
+ String name;
+ Object memberExprNode = null;
+ if (ts.matchToken(ts.NAME)) {
+ name = ts.getString();
+ if (!ts.matchToken(ts.LP)) {
+ if (Context.getContext().hasFeature(
+ Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)) {
+ // Extension to ECMA: if 'function ' does not follow
+ // by '(', assume starts memberExpr
+ sourceAddString(ts.NAME, name);
+ Object memberExprHead = nf.createName(name);
+ name = null;
+ memberExprNode = memberExprTail(ts, false, memberExprHead);
+ }
+ mustMatchToken(ts, ts.LP, "msg.no.paren.parms");
+ }
+ } else if (ts.matchToken(ts.LP)) {
+ // Anonymous function
+ name = null;
+ } else {
+ name = null;
+ if (Context.getContext().hasFeature(
+ Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)) {
+ // Note that memberExpr can not start with '(' like
+ // in (1+2).toString, because 'function (' already
+ // processed as anonymous function
+ memberExprNode = memberExpr(ts, false);
+ }
+ mustMatchToken(ts, ts.LP, "msg.no.paren.parms");
+ }
+
+ if (memberExprNode != null) {
+ // transform 'function' to = function
+ // even in the decompilated source
+ sourceAdd((char) ts.ASSIGN);
+ sourceAdd((char) ts.NOP);
+ }
+
+ // save a reference to the function in the enclosing source.
+ sourceAdd((char) ts.FUNCTION);
+ sourceAdd((char) functionNumber);
+ ++functionNumber;
+
+ // Save current source top to restore it on exit not to include
+ // function to parent source
+ int savedSourceTop = sourceTop;
+ int savedFunctionNumber = functionNumber;
+ Object args;
+ Object body;
+ String source;
+ try {
+ functionNumber = 0;
+
+ // FUNCTION as the first token in a Source means it's a function
+ // definition, and not a reference.
+ sourceAdd((char) ts.FUNCTION);
+ if (name != null) {
+ sourceAddString(ts.NAME, name);
+ }
+ sourceAdd((char) ts.LP);
+ args = nf.createLeaf(ts.LP);
+
+ if (!ts.matchToken(ts.GWT)) {
+ boolean first = true;
+ do {
+ if (!first)
+ sourceAdd((char) ts.COMMA);
+ first = false;
+ mustMatchToken(ts, ts.NAME, "msg.no.parm");
+ String s = ts.getString();
+ nf.addChildToBack(args, nf.createName(s));
+
+ sourceAddString(ts.NAME, s);
+ } while (ts.matchToken(ts.COMMA));
+
+ mustMatchToken(ts, ts.GWT, "msg.no.paren.after.parms");
+ }
+ sourceAdd((char) ts.GWT);
+
+ mustMatchToken(ts, ts.LC, "msg.no.brace.body");
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+ body = parseFunctionBody(ts);
+ mustMatchToken(ts, ts.RC, "msg.no.brace.after.body");
+ sourceAdd((char) ts.RC);
+ // skip the last EOL so nested functions work...
+
+ // name might be null;
+ source = sourceToString(savedSourceTop);
+ } finally {
+ sourceTop = savedSourceTop;
+ functionNumber = savedFunctionNumber;
+ }
+
+ Object pn = nf.createFunction(name, args, body, ts.getSourceName(),
+ baseLineno, ts.getLineno(), source, isExpr || memberExprNode != null);
+ if (memberExprNode != null) {
+ pn = nf.createBinary(ts.ASSIGN, ts.NOP, memberExprNode, pn);
+ }
+
+ // Add EOL but only if function is not part of expression, in which
+ // case it gets SEMI + EOL from Statement.
+ if (!isExpr) {
+ if (memberExprNode != null) {
+ // Add ';' to make 'function x.f(){}' and 'x.f = function(){}'
+ // to print the same strings when decompiling
+ sourceAdd((char) ts.SEMI);
+ }
+ sourceAdd((char) ts.EOL);
+ wellTerminated(ts, ts.FUNCTION);
+ }
+
+ return pn;
+ }
+
+ private Object statements(TokenStream ts) throws IOException {
+ Object pn = nf.createBlock(ts.getLineno());
+
+ int tt;
+ while ((tt = ts.peekToken()) > ts.EOF && tt != ts.RC) {
+ nf.addChildToBack(pn, statement(ts));
+ }
+
+ return pn;
+ }
+
+ private Object condition(TokenStream ts) throws IOException,
+ JavaScriptException {
+ Object pn;
+ mustMatchToken(ts, ts.LP, "msg.no.paren.cond");
+ sourceAdd((char) ts.LP);
+ pn = expr(ts, false);
+ mustMatchToken(ts, ts.GWT, "msg.no.paren.after.cond");
+ sourceAdd((char) ts.GWT);
+
+ // there's a check here in jsparse.c that corrects = to ==
+
+ return pn;
+ }
+
+ private boolean wellTerminated(TokenStream ts, int lastExprType)
+ throws IOException, JavaScriptException {
+ int tt = ts.peekTokenSameLine();
+ if (tt == ts.ERROR) {
+ return false;
+ }
+
+ if (tt != ts.EOF && tt != ts.EOL && tt != ts.SEMI && tt != ts.RC) {
+ int version = Context.getContext().getLanguageVersion();
+ if ((tt == ts.FUNCTION || lastExprType == ts.FUNCTION)
+ && (version < Context.VERSION_1_2)) {
+ /*
+ * Checking against version < 1.2 and version >= 1.0 in the above line
+ * breaks old javascript, so we keep it this way for now... XXX warning
+ * needed?
+ */
+ return true;
+ } else {
+ reportError(ts, "msg.no.semi.stmt");
+ }
+ }
+ return true;
+ }
+
+ // match a NAME; return null if no match.
+ private String matchLabel(TokenStream ts) throws IOException,
+ JavaScriptException {
+ int lineno = ts.getLineno();
+
+ String label = null;
+ int tt;
+ tt = ts.peekTokenSameLine();
+ if (tt == ts.NAME) {
+ ts.getToken();
+ label = ts.getString();
+ }
+
+ if (lineno == ts.getLineno())
+ wellTerminated(ts, ts.ERROR);
+
+ return label;
+ }
+
+ private Object statement(TokenStream ts) throws IOException {
+ try {
+ return statementHelper(ts);
+ } catch (JavaScriptException e) {
+ // skip to end of statement
+ int lineno = ts.getLineno();
+ int t;
+ do {
+ t = ts.getToken();
+ } while (t != TokenStream.SEMI && t != TokenStream.EOL
+ && t != TokenStream.EOF && t != TokenStream.ERROR);
+ return nf.createExprStatement(nf.createName("error"), lineno);
+ }
+ }
+
+ /**
+ * Whether the "catch (e: e instanceof Exception) { ... }" syntax is
+ * implemented.
+ */
+
+ private Object statementHelper(TokenStream ts) throws IOException,
+ JavaScriptException {
+ Object pn = null;
+
+ // If skipsemi == true, don't add SEMI + EOL to source at the
+ // end of this statment. For compound statements, IF/FOR etc.
+ boolean skipsemi = false;
+
+ int tt;
+
+ int lastExprType = 0; // For wellTerminated. 0 to avoid warning.
+
+ tt = ts.getToken();
+
+ switch (tt) {
+ case TokenStream.IF: {
+ skipsemi = true;
+
+ sourceAdd((char) ts.IF);
+ int lineno = ts.getLineno();
+ Object cond = condition(ts);
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+ Object ifTrue = statement(ts);
+ Object ifFalse = null;
+ if (ts.matchToken(ts.ELSE)) {
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.ELSE);
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+ ifFalse = statement(ts);
+ }
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.EOL);
+ pn = nf.createIf(cond, ifTrue, ifFalse, lineno);
+ break;
+ }
+
+ case TokenStream.SWITCH: {
+ skipsemi = true;
+
+ sourceAdd((char) ts.SWITCH);
+ pn = nf.createSwitch(ts.getLineno());
+
+ Object cur_case = null; // to kill warning
+ Object case_statements;
+
+ mustMatchToken(ts, ts.LP, "msg.no.paren.switch");
+ sourceAdd((char) ts.LP);
+ nf.addChildToBack(pn, expr(ts, false));
+ mustMatchToken(ts, ts.GWT, "msg.no.paren.after.switch");
+ sourceAdd((char) ts.GWT);
+ mustMatchToken(ts, ts.LC, "msg.no.brace.switch");
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+
+ while ((tt = ts.getToken()) != ts.RC && tt != ts.EOF) {
+ switch (tt) {
+ case TokenStream.CASE:
+ sourceAdd((char) ts.CASE);
+ cur_case = nf.createUnary(ts.CASE, expr(ts, false));
+ sourceAdd((char) ts.COLON);
+ sourceAdd((char) ts.EOL);
+ break;
+
+ case TokenStream.DEFAULT:
+ cur_case = nf.createLeaf(ts.DEFAULT);
+ sourceAdd((char) ts.DEFAULT);
+ sourceAdd((char) ts.COLON);
+ sourceAdd((char) ts.EOL);
+ // XXX check that there isn't more than one default
+ break;
+
+ default:
+ reportError(ts, "msg.bad.switch");
+ break;
+ }
+ mustMatchToken(ts, ts.COLON, "msg.no.colon.case");
+
+ case_statements = nf.createLeaf(TokenStream.BLOCK);
+ ((Node) case_statements).setIsSyntheticBlock(true);
+
+ while ((tt = ts.peekToken()) != ts.RC && tt != ts.CASE
+ && tt != ts.DEFAULT && tt != ts.EOF) {
+ nf.addChildToBack(case_statements, statement(ts));
+ }
+ // assert cur_case
+ nf.addChildToBack(cur_case, case_statements);
+
+ nf.addChildToBack(pn, cur_case);
+ }
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.EOL);
+ break;
+ }
+
+ case TokenStream.WHILE: {
+ skipsemi = true;
+
+ sourceAdd((char) ts.WHILE);
+ int lineno = ts.getLineno();
+ Object cond = condition(ts);
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+ Object body = statement(ts);
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.EOL);
+
+ pn = nf.createWhile(cond, body, lineno);
+ break;
+
+ }
+
+ case TokenStream.DO: {
+ sourceAdd((char) ts.DO);
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+
+ int lineno = ts.getLineno();
+
+ Object body = statement(ts);
+
+ sourceAdd((char) ts.RC);
+ mustMatchToken(ts, ts.WHILE, "msg.no.while.do");
+ sourceAdd((char) ts.WHILE);
+ Object cond = condition(ts);
+
+ pn = nf.createDoWhile(body, cond, lineno);
+ break;
+ }
+
+ case TokenStream.FOR: {
+ skipsemi = true;
+
+ sourceAdd((char) ts.FOR);
+ int lineno = ts.getLineno();
+
+ Object init; // Node init is also foo in 'foo in Object'
+ Object cond; // Node cond is also object in 'foo in Object'
+ Object incr = null; // to kill warning
+ Object body;
+
+ mustMatchToken(ts, ts.LP, "msg.no.paren.for");
+ sourceAdd((char) ts.LP);
+ tt = ts.peekToken();
+ if (tt == ts.SEMI) {
+ init = nf.createLeaf(ts.VOID);
+ } else {
+ if (tt == ts.VAR) {
+ // set init to a var list or initial
+ ts.getToken(); // throw away the 'var' token
+ init = variables(ts, true);
+ } else {
+ init = expr(ts, true);
+ }
+ }
+
+ tt = ts.peekToken();
+ if (tt == ts.RELOP && ts.getOp() == ts.IN) {
+ ts.matchToken(ts.RELOP);
+ sourceAdd((char) ts.IN);
+ // 'cond' is the object over which we're iterating
+ cond = expr(ts, false);
+ } else { // ordinary for loop
+ mustMatchToken(ts, ts.SEMI, "msg.no.semi.for");
+ sourceAdd((char) ts.SEMI);
+ if (ts.peekToken() == ts.SEMI) {
+ // no loop condition
+ cond = nf.createLeaf(ts.VOID);
+ } else {
+ cond = expr(ts, false);
+ }
+
+ mustMatchToken(ts, ts.SEMI, "msg.no.semi.for.cond");
+ sourceAdd((char) ts.SEMI);
+ if (ts.peekToken() == ts.GWT) {
+ incr = nf.createLeaf(ts.VOID);
+ } else {
+ incr = expr(ts, false);
+ }
+ }
+
+ mustMatchToken(ts, ts.GWT, "msg.no.paren.for.ctrl");
+ sourceAdd((char) ts.GWT);
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+ body = statement(ts);
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.EOL);
+
+ if (incr == null) {
+ // cond could be null if 'in obj' got eaten by the init node.
+ pn = nf.createForIn(init, cond, body, lineno);
+ } else {
+ pn = nf.createFor(init, cond, incr, body, lineno);
+ }
+ break;
+ }
+
+ case TokenStream.TRY: {
+ int lineno = ts.getLineno();
+
+ Object tryblock;
+ Object catchblocks = null;
+ Object finallyblock = null;
+
+ skipsemi = true;
+ sourceAdd((char) ts.TRY);
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+ tryblock = statement(ts);
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.EOL);
+
+ catchblocks = nf.createLeaf(TokenStream.BLOCK);
+
+ boolean sawDefaultCatch = false;
+ int peek = ts.peekToken();
+ if (peek == ts.CATCH) {
+ while (ts.matchToken(ts.CATCH)) {
+ if (sawDefaultCatch) {
+ reportError(ts, "msg.catch.unreachable");
+ }
+ sourceAdd((char) ts.CATCH);
+ mustMatchToken(ts, ts.LP, "msg.no.paren.catch");
+ sourceAdd((char) ts.LP);
+
+ mustMatchToken(ts, ts.NAME, "msg.bad.catchcond");
+ String varName = ts.getString();
+ sourceAddString(ts.NAME, varName);
+
+ Object catchCond = null;
+ if (ts.matchToken(ts.IF)) {
+ sourceAdd((char) ts.IF);
+ catchCond = expr(ts, false);
+ } else {
+ sawDefaultCatch = true;
+ }
+
+ mustMatchToken(ts, ts.GWT, "msg.bad.catchcond");
+ sourceAdd((char) ts.GWT);
+ mustMatchToken(ts, ts.LC, "msg.no.brace.catchblock");
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+
+ nf.addChildToBack(catchblocks, nf.createCatch(varName, catchCond,
+ statements(ts), ts.getLineno()));
+
+ mustMatchToken(ts, ts.RC, "msg.no.brace.after.body");
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.EOL);
+ }
+ } else if (peek != ts.FINALLY) {
+ mustMatchToken(ts, ts.FINALLY, "msg.try.no.catchfinally");
+ }
+
+ if (ts.matchToken(ts.FINALLY)) {
+ sourceAdd((char) ts.FINALLY);
+
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+ finallyblock = statement(ts);
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.EOL);
+ }
+
+ pn = nf.createTryCatchFinally(tryblock, catchblocks, finallyblock,
+ lineno);
+
+ break;
+ }
+ case TokenStream.THROW: {
+ int lineno = ts.getLineno();
+ sourceAdd((char) ts.THROW);
+ pn = nf.createThrow(expr(ts, false), lineno);
+ if (lineno == ts.getLineno())
+ wellTerminated(ts, ts.ERROR);
+ break;
+ }
+ case TokenStream.BREAK: {
+ int lineno = ts.getLineno();
+
+ sourceAdd((char) ts.BREAK);
+
+ // matchLabel only matches if there is one
+ String label = matchLabel(ts);
+ if (label != null) {
+ sourceAddString(ts.NAME, label);
+ }
+ pn = nf.createBreak(label, lineno);
+ break;
+ }
+ case TokenStream.CONTINUE: {
+ int lineno = ts.getLineno();
+
+ sourceAdd((char) ts.CONTINUE);
+
+ // matchLabel only matches if there is one
+ String label = matchLabel(ts);
+ if (label != null) {
+ sourceAddString(ts.NAME, label);
+ }
+ pn = nf.createContinue(label, lineno);
+ break;
+ }
+ case TokenStream.DEBUGGER: {
+ int lineno = ts.getLineno();
+
+ sourceAdd((char) ts.DEBUGGER);
+
+ pn = nf.createDebugger(lineno);
+ break;
+ }
+ case TokenStream.WITH: {
+ // bruce: we don't support this is JSNI code because it's impossible
+ // to identify bindings even passably well
+ //
+
+ reportError(ts, "msg.jsni.unsupported.with");
+
+ skipsemi = true;
+
+ sourceAdd((char) ts.WITH);
+ int lineno = ts.getLineno();
+ mustMatchToken(ts, ts.LP, "msg.no.paren.with");
+ sourceAdd((char) ts.LP);
+ Object obj = expr(ts, false);
+ mustMatchToken(ts, ts.GWT, "msg.no.paren.after.with");
+ sourceAdd((char) ts.GWT);
+ sourceAdd((char) ts.LC);
+ sourceAdd((char) ts.EOL);
+
+ Object body = statement(ts);
+
+ sourceAdd((char) ts.RC);
+ sourceAdd((char) ts.EOL);
+
+ pn = nf.createWith(obj, body, lineno);
+ break;
+ }
+ case TokenStream.VAR: {
+ int lineno = ts.getLineno();
+ pn = variables(ts, false);
+ if (ts.getLineno() == lineno)
+ wellTerminated(ts, ts.ERROR);
+ break;
+ }
+ case TokenStream.RETURN: {
+ Object retExpr = null;
+ int lineno = 0;
+
+ sourceAdd((char) ts.RETURN);
+
+ // bail if we're not in a (toplevel) function
+ if ((ts.flags & ts.TSF_FUNCTION) == 0)
+ reportError(ts, "msg.bad.return");
+
+ /* This is ugly, but we don't want to require a semicolon. */
+ ts.flags |= ts.TSF_REGEXP;
+ tt = ts.peekTokenSameLine();
+ ts.flags &= ~ts.TSF_REGEXP;
+
+ if (tt != ts.EOF && tt != ts.EOL && tt != ts.SEMI && tt != ts.RC) {
+ lineno = ts.getLineno();
+ retExpr = expr(ts, false);
+ if (ts.getLineno() == lineno)
+ wellTerminated(ts, ts.ERROR);
+ ts.flags |= ts.TSF_RETURN_EXPR;
+ } else {
+ ts.flags |= ts.TSF_RETURN_VOID;
+ }
+
+ // XXX ASSERT pn
+ pn = nf.createReturn(retExpr, lineno);
+ break;
+ }
+ case TokenStream.LC:
+ skipsemi = true;
+
+ pn = statements(ts);
+ mustMatchToken(ts, ts.RC, "msg.no.brace.block");
+ break;
+
+ case TokenStream.ERROR:
+ // Fall thru, to have a node for error recovery to work on
+ case TokenStream.EOL:
+ case TokenStream.SEMI:
+ pn = nf.createLeaf(ts.VOID);
+ skipsemi = true;
+ break;
+
+ default: {
+ lastExprType = tt;
+ int tokenno = ts.getTokenno();
+ ts.ungetToken(tt);
+ int lineno = ts.getLineno();
+
+ pn = expr(ts, false);
+
+ if (ts.peekToken() == ts.COLON) {
+ /*
+ * check that the last thing the tokenizer returned was a NAME and
+ * that only one token was consumed.
+ */
+ if (lastExprType != ts.NAME || (ts.getTokenno() != tokenno))
+ reportError(ts, "msg.bad.label");
+
+ ts.getToken(); // eat the COLON
+
+ /*
+ * in the C source, the label is associated with the statement that
+ * follows: nf.addChildToBack(pn, statement(ts));
+ */
+ String name = ts.getString();
+ pn = nf.createLabel(name, lineno);
+
+ // bruce: added to make it easier to bind labels to the
+ // statements they modify
+ //
+ nf.addChildToBack(pn, statement(ts));
+
+ // depend on decompiling lookahead to guess that that
+ // last name was a label.
+ sourceAdd((char) ts.COLON);
+ sourceAdd((char) ts.EOL);
+ return pn;
+ }
+
+ if (lastExprType == ts.FUNCTION) {
+ if (nf.getLeafType(pn) != ts.FUNCTION) {
+ reportError(ts, "msg.syntax");
+ }
+ }
+
+ pn = nf.createExprStatement(pn, lineno);
+
+ /*
+ * Check explicitly against (multi-line) function statement.
+ *
+ * lastExprEndLine is a hack to fix an automatic semicolon insertion
+ * problem with function expressions; the ts.getLineno() == lineno check
+ * was firing after a function definition even though the next statement
+ * was on a new line, because speculative getToken calls advanced the
+ * line number even when they didn't succeed.
+ */
+ if (ts.getLineno() == lineno
+ || (lastExprType == ts.FUNCTION && ts.getLineno() == lastExprEndLine)) {
+ wellTerminated(ts, lastExprType);
+ }
+ break;
+ }
+ }
+ ts.matchToken(ts.SEMI);
+ if (!skipsemi) {
+ sourceAdd((char) ts.SEMI);
+ sourceAdd((char) ts.EOL);
+ }
+
+ return pn;
+ }
+
+ private Object variables(TokenStream ts, boolean inForInit)
+ throws IOException, JavaScriptException {
+ Object pn = nf.createVariables(ts.getLineno());
+ boolean first = true;
+
+ sourceAdd((char) ts.VAR);
+
+ for (;;) {
+ Object name;
+ Object init;
+ mustMatchToken(ts, ts.NAME, "msg.bad.var");
+ String s = ts.getString();
+
+ if (!first)
+ sourceAdd((char) ts.COMMA);
+ first = false;
+
+ sourceAddString(ts.NAME, s);
+ name = nf.createName(s);
+
+ // omitted check for argument hiding
+
+ if (ts.matchToken(ts.ASSIGN)) {
+ if (ts.getOp() != ts.NOP)
+ reportError(ts, "msg.bad.var.init");
+
+ sourceAdd((char) ts.ASSIGN);
+ sourceAdd((char) ts.NOP);
+
+ init = assignExpr(ts, inForInit);
+ nf.addChildToBack(name, init);
+ }
+ nf.addChildToBack(pn, name);
+ if (!ts.matchToken(ts.COMMA))
+ break;
+ }
+ return pn;
+ }
+
+ private Object expr(TokenStream ts, boolean inForInit) throws IOException,
+ JavaScriptException {
+ Object pn = assignExpr(ts, inForInit);
+ while (ts.matchToken(ts.COMMA)) {
+ sourceAdd((char) ts.COMMA);
+ pn = nf.createBinary(ts.COMMA, pn, assignExpr(ts, inForInit));
+ }
+ return pn;
+ }
+
+ private Object assignExpr(TokenStream ts, boolean inForInit)
+ throws IOException, JavaScriptException {
+ Object pn = condExpr(ts, inForInit);
+
+ if (ts.matchToken(ts.ASSIGN)) {
+ // omitted: "invalid assignment left-hand side" check.
+ sourceAdd((char) ts.ASSIGN);
+ sourceAdd((char) ts.getOp());
+ pn = nf
+ .createBinary(ts.ASSIGN, ts.getOp(), pn, assignExpr(ts, inForInit));
+ }
+
+ return pn;
+ }
+
+ private Object condExpr(TokenStream ts, boolean inForInit)
+ throws IOException, JavaScriptException {
+ Object ifTrue;
+ Object ifFalse;
+
+ Object pn = orExpr(ts, inForInit);
+
+ if (ts.matchToken(ts.HOOK)) {
+ sourceAdd((char) ts.HOOK);
+ ifTrue = assignExpr(ts, false);
+ mustMatchToken(ts, ts.COLON, "msg.no.colon.cond");
+ sourceAdd((char) ts.COLON);
+ ifFalse = assignExpr(ts, inForInit);
+ return nf.createTernary(pn, ifTrue, ifFalse);
+ }
+
+ return pn;
+ }
+
+ private Object orExpr(TokenStream ts, boolean inForInit) throws IOException,
+ JavaScriptException {
+ Object pn = andExpr(ts, inForInit);
+ if (ts.matchToken(ts.OR)) {
+ sourceAdd((char) ts.OR);
+ pn = nf.createBinary(ts.OR, pn, orExpr(ts, inForInit));
+ }
+
+ return pn;
+ }
+
+ private Object andExpr(TokenStream ts, boolean inForInit) throws IOException,
+ JavaScriptException {
+ Object pn = bitOrExpr(ts, inForInit);
+ if (ts.matchToken(ts.AND)) {
+ sourceAdd((char) ts.AND);
+ pn = nf.createBinary(ts.AND, pn, andExpr(ts, inForInit));
+ }
+
+ return pn;
+ }
+
+ private Object bitOrExpr(TokenStream ts, boolean inForInit)
+ throws IOException, JavaScriptException {
+ Object pn = bitXorExpr(ts, inForInit);
+ while (ts.matchToken(ts.BITOR)) {
+ sourceAdd((char) ts.BITOR);
+ pn = nf.createBinary(ts.BITOR, pn, bitXorExpr(ts, inForInit));
+ }
+ return pn;
+ }
+
+ private Object bitXorExpr(TokenStream ts, boolean inForInit)
+ throws IOException, JavaScriptException {
+ Object pn = bitAndExpr(ts, inForInit);
+ while (ts.matchToken(ts.BITXOR)) {
+ sourceAdd((char) ts.BITXOR);
+ pn = nf.createBinary(ts.BITXOR, pn, bitAndExpr(ts, inForInit));
+ }
+ return pn;
+ }
+
+ private Object bitAndExpr(TokenStream ts, boolean inForInit)
+ throws IOException, JavaScriptException {
+ Object pn = eqExpr(ts, inForInit);
+ while (ts.matchToken(ts.BITAND)) {
+ sourceAdd((char) ts.BITAND);
+ pn = nf.createBinary(ts.BITAND, pn, eqExpr(ts, inForInit));
+ }
+ return pn;
+ }
+
+ private Object eqExpr(TokenStream ts, boolean inForInit) throws IOException,
+ JavaScriptException {
+ Object pn = relExpr(ts, inForInit);
+ while (ts.matchToken(ts.EQOP)) {
+ sourceAdd((char) ts.EQOP);
+ sourceAdd((char) ts.getOp());
+ pn = nf.createBinary(ts.EQOP, ts.getOp(), pn, relExpr(ts, inForInit));
+ }
+ return pn;
+ }
+
+ private Object relExpr(TokenStream ts, boolean inForInit) throws IOException,
+ JavaScriptException {
+ Object pn = shiftExpr(ts);
+ while (ts.matchToken(ts.RELOP)) {
+ int op = ts.getOp();
+ if (inForInit && op == ts.IN) {
+ ts.ungetToken(ts.RELOP);
+ break;
+ }
+ sourceAdd((char) ts.RELOP);
+ sourceAdd((char) op);
+ pn = nf.createBinary(ts.RELOP, op, pn, shiftExpr(ts));
+ }
+ return pn;
+ }
+
+ private Object shiftExpr(TokenStream ts) throws IOException,
+ JavaScriptException {
+ Object pn = addExpr(ts);
+ while (ts.matchToken(ts.SHOP)) {
+ sourceAdd((char) ts.SHOP);
+ sourceAdd((char) ts.getOp());
+ pn = nf.createBinary(ts.SHOP, ts.getOp(), pn, addExpr(ts));
+ }
+ return pn;
+ }
+
+ private Object addExpr(TokenStream ts) throws IOException,
+ JavaScriptException {
+ int tt;
+ Object pn = mulExpr(ts);
+
+ while ((tt = ts.getToken()) == ts.ADD || tt == ts.SUB) {
+ sourceAdd((char) tt);
+ // flushNewLines
+ pn = nf.createBinary(tt, pn, mulExpr(ts));
+ }
+ ts.ungetToken(tt);
+
+ return pn;
+ }
+
+ private Object mulExpr(TokenStream ts) throws IOException,
+ JavaScriptException {
+ int tt;
+
+ Object pn = unaryExpr(ts);
+
+ while ((tt = ts.peekToken()) == ts.MUL || tt == ts.DIV || tt == ts.MOD) {
+ tt = ts.getToken();
+ sourceAdd((char) tt);
+ pn = nf.createBinary(tt, pn, unaryExpr(ts));
+ }
+
+ return pn;
+ }
+
+ private Object unaryExpr(TokenStream ts) throws IOException,
+ JavaScriptException {
+ int tt;
+
+ ts.flags |= ts.TSF_REGEXP;
+ tt = ts.getToken();
+ ts.flags &= ~ts.TSF_REGEXP;
+
+ switch (tt) {
+ case TokenStream.UNARYOP:
+ sourceAdd((char) ts.UNARYOP);
+ sourceAdd((char) ts.getOp());
+ return nf.createUnary(ts.UNARYOP, ts.getOp(), unaryExpr(ts));
+
+ case TokenStream.ADD:
+ case TokenStream.SUB:
+ sourceAdd((char) ts.UNARYOP);
+ sourceAdd((char) tt);
+ return nf.createUnary(ts.UNARYOP, tt, unaryExpr(ts));
+
+ case TokenStream.INC:
+ case TokenStream.DEC:
+ sourceAdd((char) tt);
+ return nf.createUnary(tt, ts.PRE, memberExpr(ts, true));
+
+ case TokenStream.DELPROP:
+ sourceAdd((char) ts.DELPROP);
+ return nf.createUnary(ts.DELPROP, unaryExpr(ts));
+
+ case TokenStream.ERROR:
+ break;
+
+ default:
+ ts.ungetToken(tt);
+
+ int lineno = ts.getLineno();
+
+ Object pn = memberExpr(ts, true);
+
+ /*
+ * don't look across a newline boundary for a postfix incop.
+ *
+ * the rhino scanner seems to work differently than the js scanner here;
+ * in js, it works to have the line number check precede the peekToken
+ * calls. It'd be better if they had similar behavior...
+ */
+ int peeked;
+ if (((peeked = ts.peekToken()) == ts.INC || peeked == ts.DEC)
+ && ts.getLineno() == lineno) {
+ int pf = ts.getToken();
+ sourceAdd((char) pf);
+ return nf.createUnary(pf, ts.POST, pn);
+ }
+ return pn;
+ }
+ return nf.createName("err"); // Only reached on error. Try to continue.
+
+ }
+
+ private Object argumentList(TokenStream ts, Object listNode)
+ throws IOException, JavaScriptException {
+ boolean matched;
+ ts.flags |= ts.TSF_REGEXP;
+ matched = ts.matchToken(ts.GWT);
+ ts.flags &= ~ts.TSF_REGEXP;
+ if (!matched) {
+ boolean first = true;
+ do {
+ if (!first)
+ sourceAdd((char) ts.COMMA);
+ first = false;
+ nf.addChildToBack(listNode, assignExpr(ts, false));
+ } while (ts.matchToken(ts.COMMA));
+
+ mustMatchToken(ts, ts.GWT, "msg.no.paren.arg");
+ }
+ sourceAdd((char) ts.GWT);
+ return listNode;
+ }
+
+ private Object memberExpr(TokenStream ts, boolean allowCallSyntax)
+ throws IOException, JavaScriptException {
+ int tt;
+
+ Object pn;
+
+ /* Check for new expressions. */
+ ts.flags |= ts.TSF_REGEXP;
+ tt = ts.peekToken();
+ ts.flags &= ~ts.TSF_REGEXP;
+ if (tt == ts.NEW) {
+ /* Eat the NEW token. */
+ ts.getToken();
+ sourceAdd((char) ts.NEW);
+
+ /* Make a NEW node to append to. */
+ pn = nf.createLeaf(ts.NEW);
+ nf.addChildToBack(pn, memberExpr(ts, false));
+
+ if (ts.matchToken(ts.LP)) {
+ sourceAdd((char) ts.LP);
+ /* Add the arguments to pn, if any are supplied. */
+ pn = argumentList(ts, pn);
+ }
+
+ /*
+ * XXX there's a check in the C source against "too many constructor
+ * arguments" - how many do we claim to support?
+ */
+
+ /*
+ * Experimental syntax: allow an object literal to follow a new
+ * expression, which will mean a kind of anonymous class built with the
+ * JavaAdapter. the object literal will be passed as an additional
+ * argument to the constructor.
+ */
+ tt = ts.peekToken();
+ if (tt == ts.LC) {
+ nf.addChildToBack(pn, primaryExpr(ts));
+ }
+ } else {
+ pn = primaryExpr(ts);
+ }
+
+ return memberExprTail(ts, allowCallSyntax, pn);
+ }
+
+ private Object memberExprTail(TokenStream ts, boolean allowCallSyntax,
+ Object pn) throws IOException, JavaScriptException {
+ lastExprEndLine = ts.getLineno();
+ int tt;
+ while ((tt = ts.getToken()) > ts.EOF) {
+ if (tt == ts.DOT) {
+ sourceAdd((char) ts.DOT);
+ mustMatchToken(ts, ts.NAME, "msg.no.name.after.dot");
+ String s = ts.getString();
+ sourceAddString(ts.NAME, s);
+ pn = nf.createBinary(ts.DOT, pn, nf.createName(ts.getString()));
+ /*
+ * pn = nf.createBinary(ts.DOT, pn, memberExpr(ts)) is the version in
+ * Brendan's IR C version. Not in ECMA... does it reflect the 'new'
+ * operator syntax he mentioned?
+ */
+ lastExprEndLine = ts.getLineno();
+ } else if (tt == ts.LB) {
+ sourceAdd((char) ts.LB);
+ pn = nf.createBinary(ts.LB, pn, expr(ts, false));
+
+ mustMatchToken(ts, ts.RB, "msg.no.bracket.index");
+ sourceAdd((char) ts.RB);
+ lastExprEndLine = ts.getLineno();
+ } else if (allowCallSyntax && tt == ts.LP) {
+ /* make a call node */
+
+ pn = nf.createUnary(ts.CALL, pn);
+ sourceAdd((char) ts.LP);
+
+ /* Add the arguments to pn, if any are supplied. */
+ pn = argumentList(ts, pn);
+ lastExprEndLine = ts.getLineno();
+ } else {
+ ts.ungetToken(tt);
+
+ break;
+ }
+ }
+ return pn;
+ }
+
+ private Object primaryExpr(TokenStream ts) throws IOException,
+ JavaScriptException {
+ int tt;
+
+ Object pn;
+
+ ts.flags |= ts.TSF_REGEXP;
+ tt = ts.getToken();
+ ts.flags &= ~ts.TSF_REGEXP;
+
+ switch (tt) {
+
+ case TokenStream.FUNCTION:
+ return function(ts, true);
+
+ case TokenStream.LB: {
+ sourceAdd((char) ts.LB);
+ pn = nf.createLeaf(ts.ARRAYLIT);
+
+ ts.flags |= ts.TSF_REGEXP;
+ boolean matched = ts.matchToken(ts.RB);
+ ts.flags &= ~ts.TSF_REGEXP;
+
+ if (!matched) {
+ boolean first = true;
+ do {
+ ts.flags |= ts.TSF_REGEXP;
+ tt = ts.peekToken();
+ ts.flags &= ~ts.TSF_REGEXP;
+
+ if (!first)
+ sourceAdd((char) ts.COMMA);
+ else
+ first = false;
+
+ if (tt == ts.RB) { // to fix [,,,].length behavior...
+ break;
+ }
+
+ if (tt == ts.COMMA) {
+ nf.addChildToBack(pn, nf.createLeaf(ts.PRIMARY, ts.UNDEFINED));
+ } else {
+ nf.addChildToBack(pn, assignExpr(ts, false));
+ }
+
+ } while (ts.matchToken(ts.COMMA));
+ mustMatchToken(ts, ts.RB, "msg.no.bracket.arg");
+ }
+ sourceAdd((char) ts.RB);
+ return nf.createArrayLiteral(pn);
+ }
+
+ case TokenStream.LC: {
+ pn = nf.createLeaf(ts.OBJLIT);
+
+ sourceAdd((char) ts.LC);
+ if (!ts.matchToken(ts.RC)) {
+
+ boolean first = true;
+ commaloop : do {
+ Object property;
+
+ if (!first)
+ sourceAdd((char) ts.COMMA);
+ else
+ first = false;
+
+ tt = ts.getToken();
+ switch (tt) {
+ // map NAMEs to STRINGs in object literal context.
+ case TokenStream.NAME:
+ case TokenStream.STRING:
+ String s = ts.getString();
+ sourceAddString(ts.NAME, s);
+ property = nf.createString(ts.getString());
+ break;
+ case TokenStream.NUMBER:
+ double n = ts.getNumber();
+ sourceAddNumber(n);
+ property = nf.createNumber(n);
+ break;
+ case TokenStream.RC:
+ // trailing comma is OK.
+ ts.ungetToken(tt);
+ break commaloop;
+ default:
+ reportError(ts, "msg.bad.prop");
+ break commaloop;
+ }
+ mustMatchToken(ts, ts.COLON, "msg.no.colon.prop");
+
+ // OBJLIT is used as ':' in object literal for
+ // decompilation to solve spacing ambiguity.
+ sourceAdd((char) ts.OBJLIT);
+ nf.addChildToBack(pn, property);
+ nf.addChildToBack(pn, assignExpr(ts, false));
+
+ } while (ts.matchToken(ts.COMMA));
+
+ mustMatchToken(ts, ts.RC, "msg.no.brace.prop");
+ }
+ sourceAdd((char) ts.RC);
+ return nf.createObjectLiteral(pn);
+ }
+
+ case TokenStream.LP:
+
+ /*
+ * Brendan's IR-jsparse.c makes a new node tagged with TOK_LP here...
+ * I'm not sure I understand why. Isn't the grouping already implicit in
+ * the structure of the parse tree? also TOK_LP is already overloaded (I
+ * think) in the C IR as 'function call.'
+ */
+ sourceAdd((char) ts.LP);
+ pn = expr(ts, false);
+ sourceAdd((char) ts.GWT);
+ mustMatchToken(ts, ts.GWT, "msg.no.paren");
+ return pn;
+
+ case TokenStream.NAME:
+ String name = ts.getString();
+ sourceAddString(ts.NAME, name);
+ return nf.createName(name);
+
+ case TokenStream.NUMBER:
+ double n = ts.getNumber();
+ sourceAddNumber(n);
+ return nf.createNumber(n);
+
+ case TokenStream.STRING:
+ String s = ts.getString();
+ sourceAddString(ts.STRING, s);
+ return nf.createString(s);
+
+ case TokenStream.REGEXP: {
+ String flags = ts.regExpFlags;
+ ts.regExpFlags = null;
+ String re = ts.getString();
+ sourceAddString(ts.REGEXP, '/' + re + '/' + flags);
+ return nf.createRegExp(re, flags);
+ }
+
+ case TokenStream.PRIMARY:
+ sourceAdd((char) ts.PRIMARY);
+ sourceAdd((char) ts.getOp());
+ return nf.createLeaf(ts.PRIMARY, ts.getOp());
+
+ case TokenStream.RESERVED:
+ reportError(ts, "msg.reserved.id");
+ break;
+
+ case TokenStream.ERROR:
+ /* the scanner or one of its subroutines reported the error. */
+ break;
+
+ default:
+ reportError(ts, "msg.syntax");
+ break;
+
+ }
+ return null; // should never reach here
+ }
+
+ /**
+ * The following methods save decompilation information about the source.
+ * Source information is returned from the parser as a String associated with
+ * function nodes and with the toplevel script. When saved in the constant
+ * pool of a class, this string will be UTF-8 encoded, and token values will
+ * occupy a single byte.
+ *
+ * Source is saved (mostly) as token numbers. The tokens saved pretty much
+ * correspond to the token stream of a 'canonical' representation of the input
+ * program, as directed by the parser. (There were a few cases where tokens
+ * could have been left out where decompiler could easily reconstruct them,
+ * but I left them in for clarity). (I also looked adding source collection to
+ * TokenStream instead, where I could have limited the changes to a few lines
+ * in getToken... but this wouldn't have saved any space in the resulting
+ * source representation, and would have meant that I'd have to duplicate
+ * parser logic in the decompiler to disambiguate situations where newlines
+ * are important.) NativeFunction.decompile expands the tokens back into their
+ * string representations, using simple lookahead to correct spacing and
+ * indentation.
+ *
+ * Token types with associated ops (ASSIGN, SHOP, PRIMARY, etc.) are saved as
+ * two-token pairs. Number tokens are stored inline, as a NUMBER token, a
+ * character representing the type, and either 1 or 4 characters representing
+ * the bit-encoding of the number. String types NAME, STRING and OBJECT are
+ * currently stored as a token type, followed by a character giving the length
+ * of the string (assumed to be less than 2^16), followed by the characters of
+ * the string inlined into the source string. Changing this to some reference
+ * to to the string in the compiled class' constant pool would probably save a
+ * lot of space... but would require some method of deriving the final
+ * constant pool entry from information available at parse time.
+ *
+ * Nested functions need a similar mechanism... fortunately the nested
+ * functions for a given function are generated in source order. Nested
+ * functions are encoded as FUNCTION followed by a function number (encoded as
+ * a character), which is enough information to find the proper generated
+ * NativeFunction instance.
+ *
+ */
+ private void sourceAdd(char c) {
+ if (sourceTop == sourceBuffer.length) {
+ increaseSourceCapacity(sourceTop + 1);
+ }
+ sourceBuffer[sourceTop] = c;
+ ++sourceTop;
+ }
+
+ private void sourceAddString(int type, String str) {
+ int L = str.length();
+ // java string length < 2^16?
+ if (Context.check && L > Character.MAX_VALUE)
+ Context.codeBug();
+
+ if (sourceTop + L + 2 > sourceBuffer.length) {
+ increaseSourceCapacity(sourceTop + L + 2);
+ }
+ sourceAdd((char) type);
+ sourceAdd((char) L);
+ str.getChars(0, L, sourceBuffer, sourceTop);
+ sourceTop += L;
+ }
+
+ private void sourceAddNumber(double n) {
+ sourceAdd((char) TokenStream.NUMBER);
+
+ /*
+ * encode the number in the source stream. Save as NUMBER type (char | char
+ * char char char) where type is 'D' - double, 'S' - short, 'J' - long.
+ *
+ * We need to retain float vs. integer type info to keep the behavior of
+ * liveconnect type-guessing the same after decompilation. (Liveconnect
+ * tries to present 1.0 to Java as a float/double) OPT: This is no longer
+ * true. We could compress the format.
+ *
+ * This may not be the most space-efficient encoding; the chars created
+ * below may take up to 3 bytes in constant pool UTF-8 encoding, so a Double
+ * could take up to 12 bytes.
+ */
+
+ long lbits = (long) n;
+ if (lbits != n) {
+ // if it's floating point, save as a Double bit pattern.
+ // (12/15/97 our scanner only returns Double for f.p.)
+ lbits = Double.doubleToLongBits(n);
+ sourceAdd('D');
+ sourceAdd((char) (lbits >> 48));
+ sourceAdd((char) (lbits >> 32));
+ sourceAdd((char) (lbits >> 16));
+ sourceAdd((char) lbits);
+ } else {
+ // we can ignore negative values, bc they're already prefixed
+ // by UNARYOP SUB
+ if (Context.check && lbits < 0)
+ Context.codeBug();
+
+ // will it fit in a char?
+ // this gives a short encoding for integer values up to 2^16.
+ if (lbits <= Character.MAX_VALUE) {
+ sourceAdd('S');
+ sourceAdd((char) lbits);
+ } else { // Integral, but won't fit in a char. Store as a long.
+ sourceAdd('J');
+ sourceAdd((char) (lbits >> 48));
+ sourceAdd((char) (lbits >> 32));
+ sourceAdd((char) (lbits >> 16));
+ sourceAdd((char) lbits);
+ }
+ }
+ }
+
+ private void increaseSourceCapacity(int minimalCapacity) {
+ // Call this only when capacity increase is must
+ if (Context.check && minimalCapacity <= sourceBuffer.length)
+ Context.codeBug();
+ int newCapacity = sourceBuffer.length * 2;
+ if (newCapacity < minimalCapacity) {
+ newCapacity = minimalCapacity;
+ }
+ char[] tmp = new char[newCapacity];
+ System.arraycopy(sourceBuffer, 0, tmp, 0, sourceTop);
+ sourceBuffer = tmp;
+ }
+
+ private String sourceToString(int offset) {
+ if (Context.check && (offset < 0 || sourceTop < offset))
+ Context.codeBug();
+ return new String(sourceBuffer, offset, sourceTop - offset);
+ }
+
+ private int lastExprEndLine; // Hack to handle function expr termination.
+ private IRFactory nf;
+ private ErrorReporter er;
+ private boolean ok; // Did the parse encounter an error?
+
+ private char[] sourceBuffer = new char[128];
+ private int sourceTop;
+ private int functionNumber;
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/ScriptRuntime.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/ScriptRuntime.java
new file mode 100644
index 00000000000..d9599e3d815
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/ScriptRuntime.java
@@ -0,0 +1,240 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Patrick Beard
+ * Norris Boyd
+ * Igor Bukanov
+ * Roger Lawrence
+ * Frank Mitchell
+ * Andrew Wason
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+/**
+ * This is the class that implements the runtime.
+ *
+ * @author Norris Boyd
+ */
+
+public class ScriptRuntime {
+
+ public static double NaN = 0.0d / 0.0;
+
+ public static String numberToString(double d, int base) {
+ if (Double.isNaN(d))
+ return "NaN";
+ if (d == Double.POSITIVE_INFINITY)
+ return "Infinity";
+ if (d == Double.NEGATIVE_INFINITY)
+ return "-Infinity";
+ if (d == 0.0)
+ return "0";
+
+ if ((base < 2) || (base > 36)) {
+ throw new Error(Context.getMessage1("msg.bad.radix",
+ Integer.toString(base)));
+ }
+
+ if (base != 10) {
+ return DToA.JS_dtobasestr(base, d);
+ } else {
+ StringBuffer result = new StringBuffer();
+ DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
+ return result.toString();
+ }
+
+ }
+
+ /*
+ * Helper function for toNumber, parseInt, and TokenStream.getToken.
+ */
+ static double stringToNumber(String s, int start, int radix) {
+ char digitMax = '9';
+ char lowerCaseBound = 'a';
+ char upperCaseBound = 'A';
+ int len = s.length();
+ if (radix < 10) {
+ digitMax = (char) ('0' + radix - 1);
+ }
+ if (radix > 10) {
+ lowerCaseBound = (char) ('a' + radix - 10);
+ upperCaseBound = (char) ('A' + radix - 10);
+ }
+ int end;
+ double sum = 0.0;
+ for (end=start; end < len; end++) {
+ char c = s.charAt(end);
+ int newDigit;
+ if ('0' <= c && c <= digitMax)
+ newDigit = c - '0';
+ else if ('a' <= c && c < lowerCaseBound)
+ newDigit = c - 'a' + 10;
+ else if ('A' <= c && c < upperCaseBound)
+ newDigit = c - 'A' + 10;
+ else
+ break;
+ sum = sum*radix + newDigit;
+ }
+ if (start == end) {
+ return NaN;
+ }
+ if (sum >= 9007199254740992.0) {
+ if (radix == 10) {
+ /* If we're accumulating a decimal number and the number
+ * is >= 2^53, then the result from the repeated multiply-add
+ * above may be inaccurate. Call Java to get the correct
+ * answer.
+ */
+ try {
+ return Double.valueOf(s.substring(start, end)).doubleValue();
+ } catch (NumberFormatException nfe) {
+ return NaN;
+ }
+ } else if (radix == 2 || radix == 4 || radix == 8 ||
+ radix == 16 || radix == 32)
+ {
+ /* The number may also be inaccurate for one of these bases.
+ * This happens if the addition in value*radix + digit causes
+ * a round-down to an even least significant mantissa bit
+ * when the first dropped bit is a one. If any of the
+ * following digits in the number (which haven't been added
+ * in yet) are nonzero then the correct action would have
+ * been to round up instead of down. An example of this
+ * occurs when reading the number 0x1000000000000081, which
+ * rounds to 0x1000000000000000 instead of 0x1000000000000100.
+ */
+ BinaryDigitReader bdr = new BinaryDigitReader(radix, s, start, end);
+ int bit;
+ sum = 0.0;
+
+ /* Skip leading zeros. */
+ do {
+ bit = bdr.getNextBinaryDigit();
+ } while (bit == 0);
+
+ if (bit == 1) {
+ /* Gather the 53 significant bits (including the leading 1) */
+ sum = 1.0;
+ for (int j = 52; j != 0; j--) {
+ bit = bdr.getNextBinaryDigit();
+ if (bit < 0)
+ return sum;
+ sum = sum*2 + bit;
+ }
+ /* bit54 is the 54th bit (the first dropped from the mantissa) */
+ int bit54 = bdr.getNextBinaryDigit();
+ if (bit54 >= 0) {
+ double factor = 2.0;
+ int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
+ int bit3;
+
+ while ((bit3 = bdr.getNextBinaryDigit()) >= 0) {
+ sticky |= bit3;
+ factor *= 2;
+ }
+ sum += bit54 & (bit | sticky);
+ sum *= factor;
+ }
+ }
+ }
+ /* We don't worry about inaccurate numbers for any other base. */
+ }
+ return sum;
+ }
+
+ /**
+ * For escaping strings printed by object and array literals; not quite
+ * the same as 'escape.'
+ */
+ public static String escapeString(String s) {
+
+ StringBuffer sb = null;
+
+ for(int i = 0, L = s.length(); i != L; ++i) {
+ int c = s.charAt(i);
+
+ if (' ' <= c && c <= '~' && c != '"' && c != '\\') {
+ // an ordinary print character (like C isprint()) and not "
+ // or \ . Note single quote ' is not escaped
+ if (sb != null) {
+ sb.append((char)c);
+ }
+ continue;
+ }
+ if (sb == null) {
+ sb = new StringBuffer(L + 3);
+ sb.append(s);
+ sb.setLength(i);
+ }
+
+ int escape = -1;
+ switch (c) {
+ case '\b': escape = 'b'; break;
+ case '\f': escape = 'f'; break;
+ case '\n': escape = 'n'; break;
+ case '\r': escape = 'r'; break;
+ case '\t': escape = 't'; break;
+ case 0xb: escape = 'v'; break; // Java lacks \v.
+ case '"': escape = '"'; break;
+ case ' ': escape = ' '; break;
+ case '\\': escape = '\\'; break;
+ }
+ if (escape >= 0) {
+ // an \escaped sort of character
+ sb.append('\\');
+ sb.append((char)escape);
+ } else {
+ int hexSize;
+ if (c < 256) {
+ // 2-digit hex
+ sb.append("\\x");
+ hexSize = 2;
+ } else {
+ // Unicode.
+ sb.append("\\u");
+ hexSize = 4;
+ }
+ // append hexadecimal form of c left-padded with 0
+ for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
+ int digit = 0xf & (c >> shift);
+ int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit;
+ sb.append((char)hc);
+ }
+ }
+ }
+
+ return (sb == null) ? s : sb.toString();
+ }
+
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/TokenStream.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/TokenStream.java
new file mode 100644
index 00000000000..a677b31aa35
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/TokenStream.java
@@ -0,0 +1,1640 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * Roger Lawrence
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+import java.io.*;
+
+/**
+ * This class implements the JavaScript scanner.
+ *
+ * It is based on the C source files jsscan.c and jsscan.h
+ * in the jsref package.
+ *
+ * @see org.mozilla.javascript.Parser
+ *
+ * @author Mike McCabe
+ * @author Brendan Eich
+ */
+
+public class TokenStream {
+
+ static final boolean RESERVED_KEYWORD_AS_IDENTIFIER = false;
+
+ /*
+ * JSTokenStream flags, mirroring those in jsscan.h. These are used
+ * by the parser to change/check the state of the scanner.
+ */
+
+ final static int
+ TSF_NEWLINES = 1 << 0, // tokenize newlines
+ TSF_FUNCTION = 1 << 1, // scanning inside function body
+ TSF_RETURN_EXPR = 1 << 2, // function has 'return expr;'
+ TSF_RETURN_VOID = 1 << 3, // function has 'return;'
+ TSF_REGEXP = 1 << 4, // looking for a regular expression
+ TSF_DIRTYLINE = 1 << 5; // stuff other than whitespace since
+ // start of line
+
+ /*
+ * For chars - because we need something out-of-range
+ * to check. (And checking EOF by exception is annoying.)
+ * Note distinction from EOF token type!
+ */
+ private final static int
+ EOF_CHAR = -1;
+
+ /**
+ * Token types. These values correspond to JSTokenType values in
+ * jsscan.c.
+ */
+
+ public final static int
+ // start enum
+ ERROR = -1, // well-known as the only code < EOF
+ EOF = 0, // end of file token - (not EOF_CHAR)
+ EOL = 1, // end of line
+ // Beginning here are interpreter bytecodes. Their values
+ // must not exceed 127.
+ POPV = 2,
+ ENTERWITH = 3,
+ LEAVEWITH = 4,
+ RETURN = 5,
+ GOTO = 6,
+ IFEQ = 7,
+ IFNE = 8,
+ DUP = 9,
+ SETNAME = 10,
+ BITOR = 11,
+ BITXOR = 12,
+ BITAND = 13,
+ EQ = 14,
+ NE = 15,
+ LT = 16,
+ LE = 17,
+ GT = 18,
+ GE = 19,
+ LSH = 20,
+ RSH = 21,
+ URSH = 22,
+ ADD = 23,
+ SUB = 24,
+ MUL = 25,
+ DIV = 26,
+ MOD = 27,
+ BITNOT = 28,
+ NEG = 29,
+ NEW = 30,
+ DELPROP = 31,
+ TYPEOF = 32,
+ NAMEINC = 33,
+ PROPINC = 34,
+ ELEMINC = 35,
+ NAMEDEC = 36,
+ PROPDEC = 37,
+ ELEMDEC = 38,
+ GETPROP = 39,
+ SETPROP = 40,
+ GETELEM = 41,
+ SETELEM = 42,
+ CALL = 43,
+ NAME = 44,
+ NUMBER = 45,
+ STRING = 46,
+ ZERO = 47,
+ ONE = 48,
+ NULL = 49,
+ THIS = 50,
+ FALSE = 51,
+ TRUE = 52,
+ SHEQ = 53, // shallow equality (===)
+ SHNE = 54, // shallow inequality (!==)
+ CLOSURE = 55,
+ REGEXP = 56,
+ POP = 57,
+ POS = 58,
+ VARINC = 59,
+ VARDEC = 60,
+ BINDNAME = 61,
+ THROW = 62,
+ IN = 63,
+ INSTANCEOF = 64,
+ GOSUB = 65,
+ RETSUB = 66,
+ CALLSPECIAL = 67,
+ GETTHIS = 68,
+ NEWTEMP = 69,
+ USETEMP = 70,
+ GETBASE = 71,
+ GETVAR = 72,
+ SETVAR = 73,
+ UNDEFINED = 74,
+ TRY = 75,
+ ENDTRY = 76,
+ NEWSCOPE = 77,
+ TYPEOFNAME = 78,
+ ENUMINIT = 79,
+ ENUMNEXT = 80,
+ GETPROTO = 81,
+ GETPARENT = 82,
+ SETPROTO = 83,
+ SETPARENT = 84,
+ SCOPE = 85,
+ GETSCOPEPARENT = 86,
+ THISFN = 87,
+ JTHROW = 88,
+ // End of interpreter bytecodes
+ SEMI = 89, // semicolon
+ LB = 90, // left and right brackets
+ RB = 91,
+ LC = 92, // left and right curlies (braces)
+ RC = 93,
+ LP = 94, // left and right parentheses
+ GWT = 95,
+ COMMA = 96, // comma operator
+ ASSIGN = 97, // assignment ops (= += -= etc.)
+ HOOK = 98, // conditional (?:)
+ COLON = 99,
+ OR = 100, // logical or (||)
+ AND = 101, // logical and (&&)
+ EQOP = 102, // equality ops (== !=)
+ RELOP = 103, // relational ops (< <= > >=)
+ SHOP = 104, // shift ops (<< >> >>>)
+ UNARYOP = 105, // unary prefix operator
+ INC = 106, // increment/decrement (++ --)
+ DEC = 107,
+ DOT = 108, // member operator (.)
+ PRIMARY = 109, // true, false, null, this
+ FUNCTION = 110, // function keyword
+ EXPORT = 111, // export keyword
+ IMPORT = 112, // import keyword
+ IF = 113, // if keyword
+ ELSE = 114, // else keyword
+ SWITCH = 115, // switch keyword
+ CASE = 116, // case keyword
+ DEFAULT = 117, // default keyword
+ WHILE = 118, // while keyword
+ DO = 119, // do keyword
+ FOR = 120, // for keyword
+ BREAK = 121, // break keyword
+ CONTINUE = 122, // continue keyword
+ VAR = 123, // var keyword
+ WITH = 124, // with keyword
+ CATCH = 125, // catch keyword
+ FINALLY = 126, // finally keyword
+ RESERVED = 127, // reserved keywords
+
+ /** Added by Mike - these are JSOPs in the jsref, but I
+ * don't have them yet in the java implementation...
+ * so they go here. Also whatever I needed.
+
+ * Most of these go in the 'op' field when returning
+ * more general token types, eg. 'DIV' as the op of 'ASSIGN'.
+ */
+ NOP = 128, // NOP
+ NOT = 129, // etc.
+ PRE = 130, // for INC, DEC nodes.
+ POST = 131,
+
+ /**
+ * For JSOPs associated with keywords...
+ * eg. op = THIS; token = PRIMARY
+ */
+
+ VOID = 132,
+
+ /* types used for the parse tree - these never get returned
+ * by the scanner.
+ */
+ BLOCK = 133, // statement block
+ ARRAYLIT = 134, // array literal
+ OBJLIT = 135, // object literal
+ LABEL = 136, // label
+ TARGET = 137,
+ LOOP = 138,
+ ENUMDONE = 139,
+ EXPRSTMT = 140,
+ PARENT = 141,
+ CONVERT = 142,
+ JSR = 143,
+ NEWLOCAL = 144,
+ USELOCAL = 145,
+ DEBUGGER = 146,
+ SCRIPT = 147, // top-level node for entire script
+
+ LAST_TOKEN = 147,
+
+ // This value is only used as a return value for getTokenHelper,
+ // which is only called from getToken and exists to avoid an excessive
+ // recursion problem if a number of lines in a row are comments.
+ RETRY_TOKEN = 65535;
+
+ // end enum
+
+
+ public static String tokenToName(int token) {
+ if (Context.printTrees || Context.printICode) {
+ switch (token) {
+ case ERROR: return "error";
+ case EOF: return "eof";
+ case EOL: return "eol";
+ case POPV: return "popv";
+ case ENTERWITH: return "enterwith";
+ case LEAVEWITH: return "leavewith";
+ case RETURN: return "return";
+ case GOTO: return "goto";
+ case IFEQ: return "ifeq";
+ case IFNE: return "ifne";
+ case DUP: return "dup";
+ case SETNAME: return "setname";
+ case BITOR: return "bitor";
+ case BITXOR: return "bitxor";
+ case BITAND: return "bitand";
+ case EQ: return "eq";
+ case NE: return "ne";
+ case LT: return "lt";
+ case LE: return "le";
+ case GT: return "gt";
+ case GE: return "ge";
+ case LSH: return "lsh";
+ case RSH: return "rsh";
+ case URSH: return "ursh";
+ case ADD: return "add";
+ case SUB: return "sub";
+ case MUL: return "mul";
+ case DIV: return "div";
+ case MOD: return "mod";
+ case BITNOT: return "bitnot";
+ case NEG: return "neg";
+ case NEW: return "new";
+ case DELPROP: return "delprop";
+ case TYPEOF: return "typeof";
+ case NAMEINC: return "nameinc";
+ case PROPINC: return "propinc";
+ case ELEMINC: return "eleminc";
+ case NAMEDEC: return "namedec";
+ case PROPDEC: return "propdec";
+ case ELEMDEC: return "elemdec";
+ case GETPROP: return "getprop";
+ case SETPROP: return "setprop";
+ case GETELEM: return "getelem";
+ case SETELEM: return "setelem";
+ case CALL: return "call";
+ case NAME: return "name";
+ case NUMBER: return "number";
+ case STRING: return "string";
+ case ZERO: return "zero";
+ case ONE: return "one";
+ case NULL: return "null";
+ case THIS: return "this";
+ case FALSE: return "false";
+ case TRUE: return "true";
+ case SHEQ: return "sheq";
+ case SHNE: return "shne";
+ case CLOSURE: return "closure";
+ case REGEXP: return "object";
+ case POP: return "pop";
+ case POS: return "pos";
+ case VARINC: return "varinc";
+ case VARDEC: return "vardec";
+ case BINDNAME: return "bindname";
+ case THROW: return "throw";
+ case IN: return "in";
+ case INSTANCEOF: return "instanceof";
+ case GOSUB: return "gosub";
+ case RETSUB: return "retsub";
+ case CALLSPECIAL: return "callspecial";
+ case GETTHIS: return "getthis";
+ case NEWTEMP: return "newtemp";
+ case USETEMP: return "usetemp";
+ case GETBASE: return "getbase";
+ case GETVAR: return "getvar";
+ case SETVAR: return "setvar";
+ case UNDEFINED: return "undefined";
+ case TRY: return "try";
+ case ENDTRY: return "endtry";
+ case NEWSCOPE: return "newscope";
+ case TYPEOFNAME: return "typeofname";
+ case ENUMINIT: return "enuminit";
+ case ENUMNEXT: return "enumnext";
+ case GETPROTO: return "getproto";
+ case GETPARENT: return "getparent";
+ case SETPROTO: return "setproto";
+ case SETPARENT: return "setparent";
+ case SCOPE: return "scope";
+ case GETSCOPEPARENT: return "getscopeparent";
+ case THISFN: return "thisfn";
+ case JTHROW: return "jthrow";
+ case SEMI: return "semi";
+ case LB: return "lb";
+ case RB: return "rb";
+ case LC: return "lc";
+ case RC: return "rc";
+ case LP: return "lp";
+ case GWT: return "gwt";
+ case COMMA: return "comma";
+ case ASSIGN: return "assign";
+ case HOOK: return "hook";
+ case COLON: return "colon";
+ case OR: return "or";
+ case AND: return "and";
+ case EQOP: return "eqop";
+ case RELOP: return "relop";
+ case SHOP: return "shop";
+ case UNARYOP: return "unaryop";
+ case INC: return "inc";
+ case DEC: return "dec";
+ case DOT: return "dot";
+ case PRIMARY: return "primary";
+ case FUNCTION: return "function";
+ case EXPORT: return "export";
+ case IMPORT: return "import";
+ case IF: return "if";
+ case ELSE: return "else";
+ case SWITCH: return "switch";
+ case CASE: return "case";
+ case DEFAULT: return "default";
+ case WHILE: return "while";
+ case DO: return "do";
+ case FOR: return "for";
+ case BREAK: return "break";
+ case CONTINUE: return "continue";
+ case VAR: return "var";
+ case WITH: return "with";
+ case CATCH: return "catch";
+ case FINALLY: return "finally";
+ case RESERVED: return "reserved";
+ case NOP: return "nop";
+ case NOT: return "not";
+ case PRE: return "pre";
+ case POST: return "post";
+ case VOID: return "void";
+ case BLOCK: return "block";
+ case ARRAYLIT: return "arraylit";
+ case OBJLIT: return "objlit";
+ case LABEL: return "label";
+ case TARGET: return "target";
+ case LOOP: return "loop";
+ case ENUMDONE: return "enumdone";
+ case EXPRSTMT: return "exprstmt";
+ case PARENT: return "parent";
+ case CONVERT: return "convert";
+ case JSR: return "jsr";
+ case NEWLOCAL: return "newlocal";
+ case USELOCAL: return "uselocal";
+ case SCRIPT: return "script";
+ }
+ return "";
+ }
+ return "";
+ }
+
+ /* This function uses the cached op, string and number fields in
+ * TokenStream; if getToken has been called since the passed token
+ * was scanned, the op or string printed may be incorrect.
+ */
+ public String tokenToString(int token) {
+ if (Context.printTrees) {
+ String name = tokenToName(token);
+
+ switch (token) {
+ case UNARYOP:
+ case ASSIGN:
+ case PRIMARY:
+ case EQOP:
+ case SHOP:
+ case RELOP:
+ return name + " " + tokenToName(this.op);
+
+ case STRING:
+ case REGEXP:
+ case NAME:
+ return name + " `" + this.string + "'";
+
+ case NUMBER:
+ return "NUMBER " + this.number;
+ }
+
+ return name;
+ }
+ return "";
+ }
+
+ private static int getKeywordId(String name) {
+// #string_id_map#
+// The following assumes that EOF == 0
+ final int
+ Id_break = BREAK,
+ Id_case = CASE,
+ Id_continue = CONTINUE,
+ Id_default = DEFAULT,
+ Id_delete = DELPROP,
+ Id_do = DO,
+ Id_else = ELSE,
+ Id_export = EXPORT,
+ Id_false = PRIMARY | (FALSE << 8),
+ Id_for = FOR,
+ Id_function = FUNCTION,
+ Id_if = IF,
+ Id_in = RELOP | (IN << 8),
+ Id_new = NEW,
+ Id_null = PRIMARY | (NULL << 8),
+ Id_return = RETURN,
+ Id_switch = SWITCH,
+ Id_this = PRIMARY | (THIS << 8),
+ Id_true = PRIMARY | (TRUE << 8),
+ Id_typeof = UNARYOP | (TYPEOF << 8),
+ Id_var = VAR,
+ Id_void = UNARYOP | (VOID << 8),
+ Id_while = WHILE,
+ Id_with = WITH,
+
+ // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
+ Id_abstract = RESERVED,
+ Id_boolean = RESERVED,
+ Id_byte = RESERVED,
+ Id_catch = CATCH,
+ Id_char = RESERVED,
+ Id_class = RESERVED,
+ Id_const = RESERVED,
+ Id_debugger = DEBUGGER,
+ Id_double = RESERVED,
+ Id_enum = RESERVED,
+ Id_extends = RESERVED,
+ Id_final = RESERVED,
+ Id_finally = FINALLY,
+ Id_float = RESERVED,
+ Id_goto = RESERVED,
+ Id_implements = RESERVED,
+ Id_import = IMPORT,
+ Id_instanceof = RELOP | (INSTANCEOF << 8),
+ Id_int = RESERVED,
+ Id_interface = RESERVED,
+ Id_long = RESERVED,
+ Id_native = RESERVED,
+ Id_package = RESERVED,
+ Id_private = RESERVED,
+ Id_protected = RESERVED,
+ Id_public = RESERVED,
+ Id_short = RESERVED,
+ Id_static = RESERVED,
+ Id_super = RESERVED,
+ Id_synchronized = RESERVED,
+ Id_throw = THROW,
+ Id_throws = RESERVED,
+ Id_transient = RESERVED,
+ Id_try = TRY,
+ Id_volatile = RESERVED;
+
+ int id;
+ String s = name;
+// #generated# Last update: 2001-06-01 17:45:01 CEST
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 2: c=s.charAt(1);
+ if (c=='f') { if (s.charAt(0)=='i') {id=Id_if; break L0;} }
+ else if (c=='n') { if (s.charAt(0)=='i') {id=Id_in; break L0;} }
+ else if (c=='o') { if (s.charAt(0)=='d') {id=Id_do; break L0;} }
+ break L;
+ case 3: switch (s.charAt(0)) {
+ case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L;
+ case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') {id=Id_int; break L0;} break L;
+ case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L;
+ case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L;
+ case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L;
+ } break L;
+ case 4: switch (s.charAt(0)) {
+ case 'b': X="byte";id=Id_byte; break L;
+ case 'c': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {id=Id_case; break L0;} }
+ else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') {id=Id_char; break L0;} }
+ break L;
+ case 'e': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {id=Id_else; break L0;} }
+ else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') {id=Id_enum; break L0;} }
+ break L;
+ case 'g': X="goto";id=Id_goto; break L;
+ case 'l': X="long";id=Id_long; break L;
+ case 'n': X="null";id=Id_null; break L;
+ case 't': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {id=Id_true; break L0;} }
+ else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') {id=Id_this; break L0;} }
+ break L;
+ case 'v': X="void";id=Id_void; break L;
+ case 'w': X="with";id=Id_with; break L;
+ } break L;
+ case 5: switch (s.charAt(2)) {
+ case 'a': X="class";id=Id_class; break L;
+ case 'e': X="break";id=Id_break; break L;
+ case 'i': X="while";id=Id_while; break L;
+ case 'l': X="false";id=Id_false; break L;
+ case 'n': c=s.charAt(0);
+ if (c=='c') { X="const";id=Id_const; }
+ else if (c=='f') { X="final";id=Id_final; }
+ break L;
+ case 'o': c=s.charAt(0);
+ if (c=='f') { X="float";id=Id_float; }
+ else if (c=='s') { X="short";id=Id_short; }
+ break L;
+ case 'p': X="super";id=Id_super; break L;
+ case 'r': X="throw";id=Id_throw; break L;
+ case 't': X="catch";id=Id_catch; break L;
+ } break L;
+ case 6: switch (s.charAt(1)) {
+ case 'a': X="native";id=Id_native; break L;
+ case 'e': c=s.charAt(0);
+ if (c=='d') { X="delete";id=Id_delete; }
+ else if (c=='r') { X="return";id=Id_return; }
+ break L;
+ case 'h': X="throws";id=Id_throws; break L;
+ case 'm': X="import";id=Id_import; break L;
+ case 'o': X="double";id=Id_double; break L;
+ case 't': X="static";id=Id_static; break L;
+ case 'u': X="public";id=Id_public; break L;
+ case 'w': X="switch";id=Id_switch; break L;
+ case 'x': X="export";id=Id_export; break L;
+ case 'y': X="typeof";id=Id_typeof; break L;
+ } break L;
+ case 7: switch (s.charAt(1)) {
+ case 'a': X="package";id=Id_package; break L;
+ case 'e': X="default";id=Id_default; break L;
+ case 'i': X="finally";id=Id_finally; break L;
+ case 'o': X="boolean";id=Id_boolean; break L;
+ case 'r': X="private";id=Id_private; break L;
+ case 'x': X="extends";id=Id_extends; break L;
+ } break L;
+ case 8: switch (s.charAt(0)) {
+ case 'a': X="abstract";id=Id_abstract; break L;
+ case 'c': X="continue";id=Id_continue; break L;
+ case 'd': X="debugger";id=Id_debugger; break L;
+ case 'f': X="function";id=Id_function; break L;
+ case 'v': X="volatile";id=Id_volatile; break L;
+ } break L;
+ case 9: c=s.charAt(0);
+ if (c=='i') { X="interface";id=Id_interface; }
+ else if (c=='p') { X="protected";id=Id_protected; }
+ else if (c=='t') { X="transient";id=Id_transient; }
+ break L;
+ case 10: c=s.charAt(1);
+ if (c=='m') { X="implements";id=Id_implements; }
+ else if (c=='n') { X="instanceof";id=Id_instanceof; }
+ break L;
+ case 12: X="synchronized";id=Id_synchronized; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ }
+// #/generated#
+// #/string_id_map#
+
+ return id;
+ }
+
+ private int stringToKeyword(String name) {
+ int id = getKeywordId(name);
+ if (id == 0) { return EOF; }
+ this.op = id >> 8;
+ return id & 0xff;
+ }
+
+ public TokenStream(Reader in,
+ String sourceName, int lineno)
+ {
+ this.in = new LineBuffer(in, lineno);
+ this.pushbackToken = EOF;
+ this.sourceName = sourceName;
+ flags = 0;
+ }
+
+ /* return and pop the token from the stream if it matches...
+ * otherwise return null
+ */
+ public boolean matchToken(int toMatch) throws IOException {
+ int token = getToken();
+ if (token == toMatch)
+ return true;
+
+ // didn't match, push back token
+ tokenno--;
+ this.pushbackToken = token;
+ return false;
+ }
+
+ public void clearPushback() {
+ this.pushbackToken = EOF;
+ }
+
+ 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);
+ }
+ this.pushbackToken = tt;
+ tokenno--;
+ }
+
+ public int peekToken() throws IOException {
+ int result = getToken();
+
+ this.pushbackToken = result;
+ tokenno--;
+ return result;
+ }
+
+ public int peekTokenSameLine() throws IOException {
+ int result;
+
+ flags |= TSF_NEWLINES; // SCAN_NEWLINES from jsscan.h
+ result = peekToken();
+ flags &= ~TSF_NEWLINES; // HIDE_NEWLINES from jsscan.h
+ if (this.pushbackToken == EOL)
+ this.pushbackToken = EOF;
+ return result;
+ }
+
+ public static boolean isJSKeyword(String s) {
+ return getKeywordId(s) != 0;
+ }
+
+ public 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;
+ do {
+ c = getTokenHelper();
+ } while (c == RETRY_TOKEN);
+ return c;
+ }
+
+ private int getTokenHelper() throws IOException {
+ int c;
+ tokenno++;
+
+ // Check for pushed-back token
+ if (this.pushbackToken != EOF) {
+ int result = this.pushbackToken;
+ this.pushbackToken = EOF;
+ return result;
+ }
+
+ // Eat whitespace, possibly sensitive to newlines.
+ do {
+ c = in.read();
+ if (c == '\n') {
+ flags &= ~TSF_DIRTYLINE;
+ if ((flags & TSF_NEWLINES) != 0)
+ 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();
+ if (c == 'u') {
+ identifierStart = true;
+ isUnicodeEscapeStart = true;
+ stringBufferTop = 0;
+ } else {
+ identifierStart = false;
+ c = '\\';
+ in.unread();
+ }
+ } else {
+ identifierStart = Character.isJavaIdentifierStart((char)c);
+ if (identifierStart) {
+ stringBufferTop = 0;
+ addToString(c);
+ }
+
+ // bruce: special handling of JSNI signatures
+ // - it would be nice to handle Unicode escapes in the future
+ //
+ if (c == '@') {
+ stringBufferTop = 0;
+ addToString(c);
+ return jsniMatchReference();
+ }
+ }
+
+ if (identifierStart) {
+ boolean containsEscape = isUnicodeEscapeStart;
+ for (;;) {
+ if (isUnicodeEscapeStart) {
+ // strictly speaking we should probably push-back
+ // all the bad characters if the uXXXX
+ // sequence is malformed. But since there isn't a
+ // correct context(is there?) for a bad Unicode
+ // escape sequence in an identifier, we can report
+ // an error here.
+ int escapeVal = 0;
+ for (int i = 0; i != 4; ++i) {
+ c = in.read();
+ escapeVal = (escapeVal << 4) | xDigitToInt(c);
+ // Next check takes care about c < 0 and bad escape
+ if (escapeVal < 0) { break; }
+ }
+ if (escapeVal < 0) {
+ reportSyntaxError("msg.invalid.escape", null);
+ return ERROR;
+ }
+ addToString(escapeVal);
+ isUnicodeEscapeStart = false;
+ } else {
+ c = in.read();
+ if (c == '\\') {
+ c = in.read();
+ if (c == 'u') {
+ isUnicodeEscapeStart = true;
+ containsEscape = true;
+ } else {
+ reportSyntaxError("msg.illegal.character", null);
+ return ERROR;
+ }
+ } else {
+ if (!Character.isJavaIdentifierPart((char)c)) {
+ break;
+ }
+ addToString(c);
+ }
+ }
+ }
+ in.unread();
+
+ String str = getStringFromBuffer();
+ if (!containsEscape) {
+ // OPT we shouldn't have to make a string (object!) to
+ // check if it's a keyword.
+
+ // Return the corresponding token if it's a keyword
+ int result = stringToKeyword(str);
+ if (result != EOF) {
+ if (result != RESERVED) {
+ return result;
+ }
+ else if (!RESERVED_KEYWORD_AS_IDENTIFIER)
+ {
+ return result;
+ }
+ else {
+ // If implementation permits to use future reserved
+ // keywords in violation with the EcmaScript standard,
+ // treat it as name but issue warning
+ Object[] errArgs = { str };
+ reportSyntaxWarning("msg.reserved.keyword", errArgs);
+ }
+ }
+ }
+ this.string = str;
+ return NAME;
+ }
+
+ // is it a number?
+ if (isDigit(c) || (c == '.' && isDigit(in.peek()))) {
+
+ stringBufferTop = 0;
+ int base = 10;
+
+ if (c == '0') {
+ c = in.read();
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = in.read();
+ } else if (isDigit(c)) {
+ base = 8;
+ } else {
+ addToString('0');
+ }
+ }
+
+ if (base == 16) {
+ while (0 <= xDigitToInt(c)) {
+ addToString(c);
+ c = in.read();
+ }
+ } else {
+ while ('0' <= c && c <= '9') {
+ /*
+ * We permit 08 and 09 as decimal numbers, which
+ * makes our behavior a superset of the ECMA
+ * numeric grammar. We might not always be so
+ * permissive, so we warn about it.
+ */
+ if (base == 8 && c >= '8') {
+ Object[] errArgs = { c == '8' ? "8" : "9" };
+ reportSyntaxWarning("msg.bad.octal.literal", errArgs);
+ base = 10;
+ }
+ addToString(c);
+ c = in.read();
+ }
+ }
+
+ boolean isInteger = true;
+
+ if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {
+ isInteger = false;
+ if (c == '.') {
+ do {
+ addToString(c);
+ c = in.read();
+ } while (isDigit(c));
+ }
+ if (c == 'e' || c == 'E') {
+ addToString(c);
+ c = in.read();
+ if (c == '+' || c == '-') {
+ addToString(c);
+ c = in.read();
+ }
+ if (!isDigit(c)) {
+ reportSyntaxError("msg.missing.exponent", null);
+ return ERROR;
+ }
+ do {
+ addToString(c);
+ c = in.read();
+ } while (isDigit(c));
+ }
+ }
+ in.unread();
+ String numString = getStringFromBuffer();
+
+ double dval;
+ if (base == 10 && !isInteger) {
+ try {
+ // Use Java conversion to number from string...
+ dval = (Double.valueOf(numString)).doubleValue();
+ }
+ catch (NumberFormatException ex) {
+ Object[] errArgs = { ex.getMessage() };
+ reportSyntaxError("msg.caught.nfe", errArgs);
+ return ERROR;
+ }
+ } else {
+ dval = ScriptRuntime.stringToNumber(numString, 0, base);
+ }
+
+ this.number = dval;
+ return NUMBER;
+ }
+
+ // is it a string?
+ if (c == '"' || c == '\'') {
+ // We attempt to accumulate a string the fast way, by
+ // building it directly out of the reader. But if there
+ // are any escaped characters in the string, we revert to
+ // building it out of a StringBuffer.
+
+ int quoteChar = c;
+ int val = 0;
+ stringBufferTop = 0;
+
+ c = in.read();
+ strLoop: while (c != quoteChar) {
+ if (c == '\n' || c == EOF_CHAR) {
+ in.unread();
+ reportSyntaxError("msg.unterminated.string.lit", null);
+ return ERROR;
+ }
+
+ if (c == '\\') {
+ // We've hit an escaped character
+
+ c = in.read();
+ switch (c) {
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+
+ // \v a late addition to the ECMA spec,
+ // it is not in Java, so use 0xb
+ case 'v': c = 0xb; break;
+
+ case 'u': {
+ /*
+ * Get 4 hex digits; if the u escape is not
+ * followed by 4 hex digits, use 'u' + the literal
+ * character sequence that follows.
+ */
+ int escapeStart = stringBufferTop;
+ addToString('u');
+ int escapeVal = 0;
+ for (int i = 0; i != 4; ++i) {
+ c = in.read();
+ escapeVal = (escapeVal << 4) | xDigitToInt(c);
+ if (escapeVal < 0) {
+ continue strLoop;
+ }
+ addToString(c);
+ }
+ // prepare for replace of stored 'u' sequence
+ // by escape value
+ stringBufferTop = escapeStart;
+ c = escapeVal;
+ } break;
+
+ case 'x': {
+ /* Get 2 hex digits, defaulting to 'x' + literal
+ * sequence, as above.
+ */
+ c = in.read();
+ int escapeVal = xDigitToInt(c);
+ if (escapeVal < 0) {
+ addToString('x');
+ continue strLoop;
+ } else {
+ int c1 = c;
+ c = in.read();
+ escapeVal = (escapeVal << 4) | xDigitToInt(c);
+ if (escapeVal < 0) {
+ addToString('x');
+ addToString(c1);
+ continue strLoop;
+ } else {
+ // got 2 hex digits
+ c = escapeVal;
+ }
+ }
+ } break;
+
+ case '\n':
+ // Remove line terminator
+ c = in.read();
+ continue strLoop;
+
+ default: if ('0' <= c && c < '8') {
+ val = c - '0';
+ c = in.read();
+ if ('0' <= c && c < '8') {
+ val = 8 * val + c - '0';
+ c = in.read();
+ 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();
+ }
+ }
+ in.unread();
+ c = val;
+ }
+ }
+ }
+ addToString(c);
+ c = in.read();
+ }
+
+ this.string = getStringFromBuffer();
+ return STRING;
+ }
+
+ switch (c)
+ {
+ case '\n': return EOL;
+ case ';': return SEMI;
+ case '[': return LB;
+ case ']': return RB;
+ case '{': return LC;
+ case '}': return RC;
+ case '(': return LP;
+ case ')': return GWT;
+ case ',': return COMMA;
+ case '?': return HOOK;
+ case ':': return COLON;
+ case '.': return DOT;
+
+ case '|':
+ if (in.match('|')) {
+ return OR;
+ } else if (in.match('=')) {
+ this.op = BITOR;
+ return ASSIGN;
+ } else {
+ return BITOR;
+ }
+
+ case '^':
+ if (in.match('=')) {
+ this.op = BITXOR;
+ return ASSIGN;
+ } else {
+ return BITXOR;
+ }
+
+ case '&':
+ if (in.match('&')) {
+ return AND;
+ } else if (in.match('=')) {
+ this.op = BITAND;
+ return ASSIGN;
+ } else {
+ return BITAND;
+ }
+
+ case '=':
+ if (in.match('=')) {
+ if (in.match('='))
+ this.op = SHEQ;
+ else
+ this.op = EQ;
+ return EQOP;
+ } else {
+ this.op = NOP;
+ return ASSIGN;
+ }
+
+ case '!':
+ if (in.match('=')) {
+ if (in.match('='))
+ this.op = SHNE;
+ else
+ this.op = NE;
+ return EQOP;
+ } else {
+ this.op = NOT;
+ return UNARYOP;
+ }
+
+ case '<':
+ /* NB:treat HTML begin-comment as comment-till-eol */
+ if (in.match('!')) {
+ if (in.match('-')) {
+ if (in.match('-')) {
+ skipLine();
+ return RETRY_TOKEN; // in place of 'goto retry'
+ }
+ in.unread();
+ }
+ in.unread();
+ }
+ if (in.match('<')) {
+ if (in.match('=')) {
+ this.op = LSH;
+ return ASSIGN;
+ } else {
+ this.op = LSH;
+ return SHOP;
+ }
+ } else {
+ if (in.match('=')) {
+ this.op = LE;
+ return RELOP;
+ } else {
+ this.op = LT;
+ return RELOP;
+ }
+ }
+
+ case '>':
+ if (in.match('>')) {
+ if (in.match('>')) {
+ if (in.match('=')) {
+ this.op = URSH;
+ return ASSIGN;
+ } else {
+ this.op = URSH;
+ return SHOP;
+ }
+ } else {
+ if (in.match('=')) {
+ this.op = RSH;
+ return ASSIGN;
+ } else {
+ this.op = RSH;
+ return SHOP;
+ }
+ }
+ } else {
+ if (in.match('=')) {
+ this.op = GE;
+ return RELOP;
+ } else {
+ this.op = GT;
+ return RELOP;
+ }
+ }
+
+ case '*':
+ if (in.match('=')) {
+ this.op = MUL;
+ return ASSIGN;
+ } else {
+ return MUL;
+ }
+
+ case '/':
+ // is it a // comment?
+ if (in.match('/')) {
+ skipLine();
+ return RETRY_TOKEN;
+ }
+ if (in.match('*')) {
+ while ((c = in.read()) != -1 &&
+ !(c == '*' && in.match('/'))) {
+ ; // empty loop body
+ }
+ if (c == EOF_CHAR) {
+ reportSyntaxError("msg.unterminated.comment", null);
+ return ERROR;
+ }
+ return RETRY_TOKEN; // `goto retry'
+ }
+
+ // is it a regexp?
+ if ((flags & TSF_REGEXP) != 0) {
+ stringBufferTop = 0;
+ while ((c = in.read()) != '/') {
+ if (c == '\n' || c == EOF_CHAR) {
+ in.unread();
+ reportSyntaxError("msg.unterminated.re.lit", null);
+ return ERROR;
+ }
+ if (c == '\\') {
+ addToString(c);
+ c = in.read();
+ }
+
+ addToString(c);
+ }
+ int reEnd = stringBufferTop;
+
+ while (true) {
+ if (in.match('g'))
+ addToString('g');
+ else if (in.match('i'))
+ addToString('i');
+ else if (in.match('m'))
+ addToString('m');
+ else
+ break;
+ }
+
+ if (isAlpha(in.peek())) {
+ reportSyntaxError("msg.invalid.re.flag", null);
+ return ERROR;
+ }
+
+ this.string = new String(stringBuffer, 0, reEnd);
+ this.regExpFlags = new String(stringBuffer, reEnd,
+ stringBufferTop - reEnd);
+ return REGEXP;
+ }
+
+
+ if (in.match('=')) {
+ this.op = DIV;
+ return ASSIGN;
+ } else {
+ return DIV;
+ }
+
+ case '%':
+ this.op = MOD;
+ if (in.match('=')) {
+ return ASSIGN;
+ } else {
+ return MOD;
+ }
+
+ case '~':
+ this.op = BITNOT;
+ return UNARYOP;
+
+ case '+':
+ if (in.match('=')) {
+ this.op = ADD;
+ return ASSIGN;
+ } else if (in.match('+')) {
+ return INC;
+ } else {
+ return ADD;
+ }
+
+ case '-':
+ if (in.match('=')) {
+ this.op = SUB;
+ c = ASSIGN;
+ } else if (in.match('-')) {
+ if (0 == (flags & TSF_DIRTYLINE)) {
+ // treat HTML end-comment after possible whitespace
+ // after line start as comment-utill-eol
+ if (in.match('>')) {
+ skipLine();
+ return RETRY_TOKEN;
+ }
+ }
+ c = DEC;
+ } else {
+ c = SUB;
+ }
+ flags |= TSF_DIRTYLINE;
+ return c;
+
+ default:
+ reportSyntaxError("msg.illegal.character", null);
+ return ERROR;
+ }
+ }
+
+ private void skipWhitespace() throws IOException {
+ int tmp;
+ do {
+ tmp = in.read();
+ } while (isJSSpace(tmp) || tmp == '\n');
+ // Reposition back to first non whitespace char.
+ in.unread();
+ }
+
+ private int jsniMatchReference() throws IOException {
+
+ // First, read the type name whose member is being accessed.
+ if (!jsniMatchQualifiedTypeName('.', ':')) {
+ return ERROR;
+ }
+
+ // Now we must the second colon.
+ //
+ int c = in.read();
+ if (c != ':') {
+ in.unread();
+ reportSyntaxError("msg.jsni.expected.char", new String[] { ":" });
+ return ERROR;
+ }
+ addToString(c);
+
+ // Skip whitespace starting after ::.
+ skipWhitespace();
+
+ // Finish by reading the field or method signature.
+ if (!jsniMatchMethodSignatureOrFieldName()) {
+ return ERROR;
+ }
+
+ this.string = new String(stringBuffer, 0, stringBufferTop);
+ return NAME;
+ }
+
+ private boolean jsniMatchParamListSignature() throws IOException {
+ // Assume the opening '(' has already been read.
+ // Read param type signatures until we see a closing ')'.
+
+ skipWhitespace();
+
+ // First check for the special case of * as the parameter list, indicating
+ // a wildcard
+ if (in.peek() == '*') {
+ addToString(in.read());
+ if (in.peek() != ')') {
+ reportSyntaxError("msg.jsni.expected.char", new String[] { ")" });
+ }
+ addToString(in.read());
+ return true;
+ }
+
+ // Otherwise, loop through reading one param type at a time
+ do {
+ // Skip whitespace between parameters.
+ skipWhitespace();
+
+ int c = in.read();
+
+ if (c == ')') {
+ // Finished successfully.
+ //
+ addToString(c);
+ return true;
+ }
+
+ in.unread();
+ } while (jsniMatchParamTypeSignature());
+
+ // If we made it here, we can assume that there was an invalid type
+ // signature that was already reported and that the offending char
+ // was already unread.
+ //
+ return false;
+ }
+
+ private boolean jsniMatchParamTypeSignature() throws IOException {
+ int c = in.read();
+ switch (c) {
+ case 'Z':
+ case 'B':
+ case 'C':
+ case 'S':
+ case 'I':
+ case 'J':
+ case 'F':
+ case 'D':
+ // Primitive type id.
+ addToString(c);
+ return true;
+ case 'L':
+ // Class/Interface type prefix.
+ addToString(c);
+ return jsniMatchQualifiedTypeName('/', ';');
+ case '[':
+ // Array type prefix.
+ addToString(c);
+ return jsniMatchParamArrayTypeSignature();
+ default:
+ in.unread();
+ reportSyntaxError("msg.jsni.expected.param.type", null);
+ return false;
+ }
+ }
+
+ private boolean jsniMatchParamArrayTypeSignature() throws IOException {
+ // Assume the leading '[' has already been read.
+ // What follows must be another param type signature.
+ //
+ return jsniMatchParamTypeSignature();
+ }
+
+ private boolean jsniMatchMethodSignatureOrFieldName() throws IOException {
+ int c = in.read();
+
+
+ // We must see an ident start here.
+ //
+ if (!Character.isJavaIdentifierStart((char)c)) {
+ in.unread();
+ reportSyntaxError("msg.jsni.expected.identifier", null);
+ return false;
+ }
+
+ addToString(c);
+
+ for (;;) {
+ c = in.read();
+ if (Character.isJavaIdentifierPart((char)c)) {
+ addToString(c);
+ }
+ else if (c == '(') {
+ // This means we're starting a JSNI method signature.
+ //
+ addToString(c);
+ if (jsniMatchParamListSignature()) {
+ // Finished a method signature with success.
+ // Assume the callee unread the last char.
+ //
+ return true;
+ }
+ else {
+ // Assume the callee reported the error and unread the last char.
+ //
+ return false;
+ }
+ }
+ else {
+ // We don't know this char, so it finishes the token.
+ //
+ in.unread();
+ return true;
+ }
+ }
+ }
+
+ /**
+ * This method is called to match the fully-qualified type name that
+ * should appear after the '@' in a JSNI reference.
+ * @param sepChar the character that will separate the Java idents
+ * (either a '.' or '/')
+ * @param endChar the character that indicates the end of the
+ */
+ private boolean jsniMatchQualifiedTypeName(char sepChar, char endChar)
+ throws IOException {
+ int c = in.read();
+
+ // Whether nested or not, we must see an ident start here.
+ //
+ if (!Character.isJavaIdentifierStart((char)c)) {
+ in.unread();
+ reportSyntaxError("msg.jsni.expected.identifier", null);
+ return false;
+ }
+
+ // Now actually add the first ident char.
+ //
+ addToString(c);
+
+ // And append any other ident chars.
+ //
+ for (;;) {
+ c = in.read();
+ if (Character.isJavaIdentifierPart((char)c)) {
+ addToString(c);
+ }
+ else {
+ break;
+ }
+ }
+
+ // Arrray-type reference
+ while (c == '[') {
+ if (']' == in.peek()) {
+ addToString('[');
+ addToString(in.read());
+ c = in.read();
+ } else {
+ break;
+ }
+ }
+
+ // We have a non-ident char to classify.
+ //
+ if (c == sepChar) {
+ addToString(c);
+ if (jsniMatchQualifiedTypeName(sepChar, endChar)) {
+ // We consumed up to the endChar, so we finished with total success.
+ //
+ return true;
+ } else {
+ // Assume that the nested call reported the syntax error and
+ // unread the last character.
+ //
+ return false;
+ }
+ } else if (c == endChar) {
+ // Matched everything up to the specified end char.
+ //
+ addToString(c);
+ return true;
+ } else {
+ // This is an unknown char that finishes the token.
+ //
+ in.unread();
+ return true;
+ }
+ }
+
+ private String getStringFromBuffer() {
+ return new String(stringBuffer, 0, stringBufferTop);
+ }
+
+ private void addToString(int c) {
+ if (stringBufferTop == stringBuffer.length) {
+ char[] tmp = new char[stringBuffer.length * 2];
+ System.arraycopy(stringBuffer, 0, tmp, 0, stringBufferTop);
+ stringBuffer = tmp;
+ }
+ stringBuffer[stringBufferTop++] = (char)c;
+ }
+
+ public void reportSyntaxError(String messageProperty, Object[] args) {
+ String message = Context.getMessage(messageProperty, args);
+
+ Context.reportError(message, getSourceName(),
+ getLineno(), getLine(), getOffset());
+ }
+
+ private void reportSyntaxWarning(String messageProperty, Object[] args) {
+ String message = Context.getMessage(messageProperty, args);
+ Context.reportWarning(message, getSourceName(),
+ 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(); }
+
+ // instance variables
+ private LineBuffer in;
+
+
+ /* for TSF_REGEXP, etc.
+ * should this be manipulated by gettor/settor functions?
+ * should it be passed to getToken();
+ */
+ int flags;
+ String regExpFlags;
+
+ private String sourceName;
+ private int pushbackToken;
+ private int tokenno;
+
+ private int op;
+
+ // Set this to an inital non-null value so that the Parser has
+ // something to retrieve even if an error has occured and no
+ // string is found. Fosters one class of error, but saves lots of
+ // code.
+ private String string = "";
+ private double number;
+
+ private char[] stringBuffer = new char[128];
+ private int stringBufferTop;
+}
diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/UintMap.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/UintMap.java
new file mode 100644
index 00000000000..365088e0f3f
--- /dev/null
+++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/UintMap.java
@@ -0,0 +1,657 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * 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
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Map to associate non-negative integers to objects or integers.
+ * The map does not synchronize any of its operation, so either use
+ * it from a single thread or do own synchronization or perform all mutation
+ * operations on one thread before passing the map to others
+ *
+ * @author Igor Bukanov
+ *
+ */
+
+class UintMap implements Serializable {
+
+// Map implementation via hashtable,
+// follows "The Art of Computer Programming" by Donald E. Knuth
+
+ public UintMap() {
+ this(4);
+ }
+
+ public UintMap(int initialCapacity) {
+ if (initialCapacity < 0) Context.codeBug();
+ // Table grow when number of stored keys >= 3/4 of max capacity
+ int minimalCapacity = initialCapacity * 4 / 3;
+ int i;
+ for (i = 2; (1 << i) < minimalCapacity; ++i) { }
+ power = i;
+ if (check && power < 2) Context.codeBug();
+ }
+
+ public boolean isEmpty() {
+ return keyCount == 0;
+ }
+
+ public int size() {
+ return keyCount;
+ }
+
+ public boolean has(int key) {
+ if (key < 0) Context.codeBug();
+ return 0 <= findIndex(key);
+ }
+
+ /**
+ * Get object value assigned with key.
+ * @return key object value or null if key is absent
+ */
+ public Object getObject(int key) {
+ if (key < 0) Context.codeBug();
+ if (values != null) {
+ int index = findIndex(key);
+ if (0 <= index) {
+ return values[index];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get integer value assigned with key.
+ * @return key integer value or defaultValue if key is absent
+ */
+ public int getInt(int key, int defaultValue) {
+ if (key < 0) Context.codeBug();
+ int index = findIndex(key);
+ if (0 <= index) {
+ if (ivaluesShift != 0) {
+ return keys[ivaluesShift + index];
+ }
+ return 0;
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Get integer value assigned with key.
+ * @return key integer value or defaultValue if key does not exist or does
+ * not have int value
+ * @throws RuntimeException if key does not exist
+ */
+ public int getExistingInt(int key) {
+ if (key < 0) Context.codeBug();
+ int index = findIndex(key);
+ if (0 <= index) {
+ if (ivaluesShift != 0) {
+ return keys[ivaluesShift + index];
+ }
+ return 0;
+ }
+ // Key must exist
+ Context.codeBug();
+ return 0;
+ }
+
+ /**
+ * Set object value of the key.
+ * If key does not exist, also set its int value to 0.
+ */
+ public void put(int key, Object value) {
+ if (key < 0) Context.codeBug();
+ int index = ensureIndex(key, false);
+ if (values == null) {
+ values = new Object[1 << power];
+ }
+ values[index] = value;
+ }
+
+ /**
+ * Set int value of the key.
+ * If key does not exist, also set its object value to null.
+ */
+ public void put(int key, int value) {
+ if (key < 0) Context.codeBug();
+ int index = ensureIndex(key, true);
+ if (ivaluesShift == 0) {
+ int N = 1 << power;
+ // keys.length can be N * 2 after clear which set ivaluesShift to 0
+ if (keys.length != N * 2) {
+ int[] tmp = new int[N * 2];
+ System.arraycopy(keys, 0, tmp, 0, N);
+ keys = tmp;
+ }
+ ivaluesShift = N;
+ }
+ keys[ivaluesShift + index] = value;
+ }
+
+ public void remove(int key) {
+ if (key < 0) Context.codeBug();
+ int index = findIndex(key);
+ if (0 <= index) {
+ keys[index] = DELETED;
+ --keyCount;
+ // Allow to GC value and make sure that new key with the deleted
+ // slot shall get proper default values
+ if (values != null) { values[index] = null; }
+ if (ivaluesShift != 0) { keys[ivaluesShift + index] = 0; }
+ }
+ }
+
+ public void clear() {
+ int N = 1 << power;
+ if (keys != null) {
+ for (int i = 0; i != N; ++i) {
+ keys[i] = EMPTY;
+ }
+ if (values != null) {
+ for (int i = 0; i != N; ++i) {
+ values[i] = null;
+ }
+ }
+ }
+ ivaluesShift = 0;
+ keyCount = 0;
+ occupiedCount = 0;
+ }
+
+ /** Return array of present keys */
+ public int[] getKeys() {
+ int[] keys = this.keys;
+ int n = keyCount;
+ int[] result = new int[n];
+ for (int i = 0; n != 0; ++i) {
+ int entry = keys[i];
+ if (entry != EMPTY && entry != DELETED) {
+ result[--n] = entry;
+ }
+ }
+ return result;
+ }
+
+ private static int tableLookupStep(int fraction, int mask, int power) {
+ int shift = 32 - 2 * power;
+ if (shift >= 0) {
+ return ((fraction >>> shift) & mask) | 1;
+ }
+ else {
+ return (fraction & (mask >>> -shift)) | 1;
+ }
+ }
+
+ private int findIndex(int key) {
+ int[] keys = this.keys;
+ if (keys != null) {
+ int fraction = key * A;
+ int index = fraction >>> (32 - power);
+ int entry = keys[index];
+ if (entry == key) { return index; }
+ if (entry != EMPTY) {
+ // Search in table after first failed attempt
+ int mask = (1 << power) - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int n = 0;
+ do {
+ if (check) {
+ if (n >= occupiedCount) Context.codeBug();
+ ++n;
+ }
+ index = (index + step) & mask;
+ entry = keys[index];
+ if (entry == key) { return index; }
+ } while (entry != EMPTY);
+ }
+ }
+ return -1;
+ }
+
+// Insert key that is not present to table without deleted entries
+// and enough free space
+ private int insertNewKey(int key) {
+ if (check && occupiedCount != keyCount) Context.codeBug();
+ if (check && keyCount == 1 << power) Context.codeBug();
+ int[] keys = this.keys;
+ int fraction = key * A;
+ int index = fraction >>> (32 - power);
+ if (keys[index] != EMPTY) {
+ int mask = (1 << power) - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int firstIndex = index;
+ do {
+ if (check && keys[index] == DELETED) Context.codeBug();
+ index = (index + step) & mask;
+ if (check && firstIndex == index) Context.codeBug();
+ } while (keys[index] != EMPTY);
+ }
+ keys[index] = key;
+ ++occupiedCount;
+ ++keyCount;
+ return index;
+ }
+
+ private void rehashTable(boolean ensureIntSpace) {
+ if (keys != null) {
+ // Check if removing deleted entries would free enough space
+ if (keyCount * 2 >= occupiedCount) {
+ // Need to grow: less then half of deleted entries
+ ++power;
+ }
+ }
+ int N = 1 << power;
+ int[] old = keys;
+ int oldShift = ivaluesShift;
+ if (oldShift == 0 && !ensureIntSpace) {
+ keys = new int[N];
+ }
+ else {
+ ivaluesShift = N; keys = new int[N * 2];
+ }
+ for (int i = 0; i != N; ++i) { keys[i] = EMPTY; }
+
+ Object[] oldValues = values;
+ if (oldValues != null) { values = new Object[N]; }
+
+ int oldCount = keyCount;
+ occupiedCount = 0;
+ if (oldCount != 0) {
+ keyCount = 0;
+ for (int i = 0, remaining = oldCount; remaining != 0; ++i) {
+ int key = old[i];
+ if (key != EMPTY && key != DELETED) {
+ int index = insertNewKey(key);
+ if (oldValues != null) {
+ values[index] = oldValues[i];
+ }
+ if (oldShift != 0) {
+ keys[ivaluesShift + index] = old[oldShift + i];
+ }
+ --remaining;
+ }
+ }
+ }
+ }
+
+// Ensure key index creating one if necessary
+ private int ensureIndex(int key, boolean intType) {
+ int index = -1;
+ int firstDeleted = -1;
+ int[] keys = this.keys;
+ if (keys != null) {
+ int fraction = key * A;
+ index = fraction >>> (32 - power);
+ int entry = keys[index];
+ if (entry == key) { return index; }
+ if (entry != EMPTY) {
+ if (entry == DELETED) { firstDeleted = index; }
+ // Search in table after first failed attempt
+ int mask = (1 << power) - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int n = 0;
+ do {
+ if (check) {
+ if (n >= occupiedCount) Context.codeBug();
+ ++n;
+ }
+ index = (index + step) & mask;
+ entry = keys[index];
+ if (entry == key) { return index; }
+ if (entry == DELETED && firstDeleted < 0) {
+ firstDeleted = index;
+ }
+ } while (entry != EMPTY);
+ }
+ }
+ // Inserting of new key
+ if (check && keys != null && keys[index] != EMPTY)
+ Context.codeBug();
+ if (firstDeleted >= 0) {
+ index = firstDeleted;
+ }
+ else {
+ // Need to consume empty entry: check occupation level
+ if (keys == null || occupiedCount * 4 >= (1 << power) * 3) {
+ // Too few unused entries: rehash
+ rehashTable(intType);
+ return insertNewKey(key);
+ }
+ ++occupiedCount;
+ }
+ keys[index] = key;
+ ++keyCount;
+ return index;
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+
+ int count = keyCount;
+ if (count != 0) {
+ boolean hasIntValues = (ivaluesShift != 0);
+ boolean hasObjectValues = (values != null);
+ out.writeBoolean(hasIntValues);
+ out.writeBoolean(hasObjectValues);
+
+ for (int i = 0; count != 0; ++i) {
+ int key = keys[i];
+ if (key != EMPTY && key != DELETED) {
+ --count;
+ out.writeInt(key);
+ if (hasIntValues) {
+ out.writeInt(keys[ivaluesShift + i]);
+ }
+ if (hasObjectValues) {
+ out.writeObject(values[i]);
+ }
+ }
+ }
+ }
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ int writtenKeyCount = keyCount;
+ if (writtenKeyCount != 0) {
+ keyCount = 0;
+ boolean hasIntValues = in.readBoolean();
+ boolean hasObjectValues = in.readBoolean();
+
+ int N = 1 << power;
+ if (hasIntValues) {
+ keys = new int[2 * N];
+ ivaluesShift = N;
+ }else {
+ keys = new int[N];
+ }
+ for (int i = 0; i != N; ++i) {
+ keys[i] = EMPTY;
+ }
+ if (hasObjectValues) {
+ values = new Object[N];
+ }
+ for (int i = 0; i != writtenKeyCount; ++i) {
+ int key = in.readInt();
+ int index = insertNewKey(key);
+ if (hasIntValues) {
+ int ivalue = in.readInt();
+ keys[ivaluesShift + index] = ivalue;
+ }
+ if (hasObjectValues) {
+ values[index] = in.readObject();
+ }
+ }
+ }
+ }
+
+ static final long serialVersionUID = -6916326879143724506L;
+
+
+// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
+// See Knuth etc.
+ private static final int A = 0x9e3779b9;
+
+ private static final int EMPTY = -1;
+ private static final int DELETED = -2;
+
+// Structure of kyes and values arrays (N == 1 << power):
+// keys[0 <= i < N]: key value or EMPTY or DELETED mark
+// values[0 <= i < N]: value of key at keys[i]
+// keys[N <= i < 2N]: int values of keys at keys[i - N]
+
+ private transient int[] keys;
+ private transient Object[] values;
+
+ private int power;
+ private int keyCount;
+ private transient int occupiedCount; // == keyCount + deleted_count
+
+ // If ivaluesShift != 0, keys[ivaluesShift + index] contains integer
+ // values associated with keys
+ private transient int ivaluesShift;
+
+// If true, enables consitency checks
+ private static final boolean check = false;
+
+/* TEST START
+
+ public static void main(String[] args) {
+ if (!check) {
+ System.err.println("Set check to true and re-run");
+ throw new RuntimeException("Set check to true and re-run");
+ }
+
+ UintMap map;
+ map = new UintMap();
+ testHash(map, 2);
+ map = new UintMap();
+ testHash(map, 10 * 1000);
+ map = new UintMap(30 * 1000);
+ testHash(map, 10 * 100);
+ map.clear();
+ testHash(map, 4);
+ map = new UintMap(0);
+ testHash(map, 10 * 100);
+ }
+
+ private static void testHash(UintMap map, int N) {
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i, i);
+ check(i == map.getInt(i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i, i);
+ check(i == map.getInt(i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i, new Integer(i));
+ check(-1 == map.getInt(i, -1));
+ Integer obj = (Integer)map.getObject(i);
+ check(obj != null && i == obj.intValue());
+ }
+
+ check(map.size() == N);
+
+ System.out.print("."); System.out.flush();
+ int[] keys = map.getKeys();
+ check(keys.length == N);
+ for (int i = 0; i != N; ++i) {
+ int key = keys[i];
+ check(map.has(key));
+ check(!map.isIntType(key));
+ check(map.isObjectType(key));
+ Integer obj = (Integer) map.getObject(key);
+ check(obj != null && key == obj.intValue());
+ }
+
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ check(-1 == map.getInt(i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i * i, i);
+ check(i == map.getInt(i * i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ check(i == map.getInt(i * i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i * i, new Integer(i));
+ check(-1 == map.getInt(i * i, -1));
+ map.remove(i * i);
+ check(!map.has(i * i));
+ map.put(i * i, i);
+ check(map.isIntType(i * i));
+ check(null == map.getObject(i * i));
+ map.remove(i * i);
+ check(!map.isObjectType(i * i));
+ check(!map.isIntType(i * i));
+ }
+
+ int old_size = map.size();
+ for (int i = 0; i != N; ++i) {
+ map.remove(i * i);
+ check(map.size() == old_size);
+ }
+
+ System.out.print("."); System.out.flush();
+ map.clear();
+ check(map.size() == 0);
+ for (int i = 0; i != N; ++i) {
+ map.put(i * i, i);
+ map.put(i * i + 1, new Double(i+0.5));
+ }
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.print("."); System.out.flush();
+ map = new UintMap(0);
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+ map = new UintMap(1);
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+ map = new UintMap(1000);
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.print("."); System.out.flush();
+ map = new UintMap(N / 10);
+ for (int i = 0; i != N; ++i) {
+ map.put(2*i+1, i);
+ }
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.print("."); System.out.flush();
+ map = new UintMap(N / 10);
+ for (int i = 0; i != N; ++i) {
+ map.put(2*i+1, i);
+ }
+ for (int i = 0; i != N / 2; ++i) {
+ map.remove(2*i+1);
+ }
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.print("."); System.out.flush();
+ map = new UintMap();
+ for (int i = 0; i != N; ++i) {
+ map.put(2*i+1, new Double(i + 10));
+ }
+ for (int i = 0; i != N / 2; ++i) {
+ map.remove(2*i+1);
+ }
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.println(); System.out.flush();
+
+ }
+
+ private static void checkSameMaps(UintMap map1, UintMap map2) {
+ check(map1.size() == map2.size());
+ int[] keys = map1.getKeys();
+ check(keys.length == map1.size());
+ for (int i = 0; i != keys.length; ++i) {
+ int key = keys[i];
+ check(map2.has(key));
+ check(map1.isObjectType(key) == map2.isObjectType(key));
+ check(map1.isIntType(key) == map2.isIntType(key));
+ Object o1 = map1.getObject(key);
+ Object o2 = map2.getObject(key);
+ if (map1.isObjectType(key)) {
+ check(o1.equals(o2));
+ }else {
+ check(map1.getObject(key) == null);
+ check(map2.getObject(key) == null);
+ }
+ if (map1.isIntType(key)) {
+ check(map1.getExistingInt(key) == map2.getExistingInt(key));
+ }else {
+ check(map1.getInt(key, -10) == -10);
+ check(map1.getInt(key, -11) == -11);
+ check(map2.getInt(key, -10) == -10);
+ check(map2.getInt(key, -11) == -11);
+ }
+ }
+ }
+
+ private static void check(boolean condition) {
+ if (!condition) Context.codeBug();
+ }
+
+ private static Object writeAndRead(Object obj) {
+ try {
+ java.io.ByteArrayOutputStream
+ bos = new java.io.ByteArrayOutputStream();
+ java.io.ObjectOutputStream
+ out = new java.io.ObjectOutputStream(bos);
+ out.writeObject(obj);
+ out.close();
+ byte[] data = bos.toByteArray();
+ java.io.ByteArrayInputStream
+ bis = new java.io.ByteArrayInputStream(data);
+ java.io.ObjectInputStream
+ in = new java.io.ObjectInputStream(bis);
+ Object result = in.readObject();
+ in.close();
+ return result;
+ }catch (Exception ex) {
+ ex.printStackTrace();
+ throw new RuntimeException("Unexpected");
+ }
+ }
+
+// TEST END */
+}
diff --git a/js/js.rhino-parser/src/com/google/gwt/dev/js/rhino/ObjArray.java b/js/js.rhino-parser/src/com/google/gwt/dev/js/rhino/ObjArray.java
new file mode 100644
index 00000000000..85156730145
--- /dev/null
+++ b/js/js.rhino-parser/src/com/google/gwt/dev/js/rhino/ObjArray.java
@@ -0,0 +1,356 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ * 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
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the NPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the NPL or the GPL.
+ */
+// Modified by Google
+
+package com.google.gwt.dev.js.rhino;
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+Implementation of resizable array with focus on minimizing memory usage by storing few initial array elements in object fields. Can also be used as a stack.
+*/
+
+public class ObjArray implements Serializable {
+
+ public ObjArray() { }
+
+ public ObjArray(int capacityHint) {
+ if (capacityHint < 0) throw new IllegalArgumentException();
+ if (capacityHint > FIELDS_STORE_SIZE) {
+ data = new Object[capacityHint - FIELDS_STORE_SIZE];
+ }
+ }
+
+ public final boolean isEmpty() {
+ return size == 0;
+ }
+
+ public final int size() {
+ return size;
+ }
+
+ public final void setSize(int newSize) {
+ if (newSize < 0) throw new IllegalArgumentException();
+ int N = size;
+ if (newSize < N) {
+ for (int i = newSize; i != N; ++i) {
+ setImpl(i, null);
+ }
+ }else if (newSize > N) {
+ if (newSize > FIELDS_STORE_SIZE) {
+ ensureCapacity(newSize);
+ }
+ }
+ size = newSize;
+ }
+
+ public final Object get(int index) {
+ if (!(0 <= index && index < size)) throw invalidIndex(index, size);
+ return getImpl(index);
+ }
+
+ public final void set(int index, Object value) {
+ if (!(0 <= index && index < size)) throw invalidIndex(index, size);
+ setImpl(index, value);
+ }
+
+ private Object getImpl(int index) {
+ switch (index) {
+ case 0: return f0;
+ case 1: return f1;
+ case 2: return f2;
+ case 3: return f3;
+ case 4: return f4;
+ case 5: return f5;
+ }
+ return data[index - FIELDS_STORE_SIZE];
+ }
+
+ private void setImpl(int index, Object value) {
+ switch (index) {
+ case 0: f0 = value; break;
+ case 1: f1 = value; break;
+ case 2: f2 = value; break;
+ case 3: f3 = value; break;
+ case 4: f4 = value; break;
+ case 5: f5 = value; break;
+ default: data[index - FIELDS_STORE_SIZE] = value;
+ }
+
+ }
+
+ public int indexOf(Object obj) {
+ int N = size;
+ for (int i = 0; i != N; ++i) {
+ Object current = getImpl(i);
+ if (current == obj || (current != null && current.equals(obj))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int lastIndexOf(Object obj) {
+ for (int i = size; i != 0;) {
+ --i;
+ Object current = getImpl(i);
+ if (current == obj || (current != null && current.equals(obj))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public final Object peek() {
+ int N = size;
+ if (N == 0) throw invalidEmptyStackAccess();
+ return getImpl(N - 1);
+ }
+
+ public final Object pop() {
+ int N = size;
+ --N;
+ Object top;
+ switch (N) {
+ case -1: throw invalidEmptyStackAccess();
+ case 0: top = f0; f0 = null; break;
+ case 1: top = f1; f1 = null; break;
+ case 2: top = f2; f2 = null; break;
+ case 3: top = f3; f3 = null; break;
+ case 4: top = f4; f4 = null; break;
+ case 5: top = f5; f5 = null; break;
+ default:
+ top = data[N - FIELDS_STORE_SIZE];
+ data[N - FIELDS_STORE_SIZE] = null;
+ }
+ size = N;
+ return top;
+ }
+
+ public final void push(Object value) {
+ add(value);
+ }
+
+ public final void add(Object value) {
+ int N = size;
+ if (N >= FIELDS_STORE_SIZE) {
+ ensureCapacity(N + 1);
+ }
+ size = N + 1;
+ setImpl(N, value);
+ }
+
+ public final void add(int index, Object value) {
+ Object tmp;
+ int N = size;
+ if (!(0 <= index && index <= N)) throw invalidIndex(index, N + 1);
+ switch (index) {
+ case 0:
+ if (N == 0) { f0 = value; break; }
+ tmp = f0; f0 = value; value = tmp;
+ case 1:
+ if (N == 1) { f1 = value; break; }
+ tmp = f1; f1 = value; value = tmp;
+ case 2:
+ if (N == 2) { f2 = value; break; }
+ tmp = f2; f2 = value; value = tmp;
+ case 3:
+ if (N == 3) { f3 = value; break; }
+ tmp = f3; f3 = value; value = tmp;
+ case 4:
+ if (N == 4) { f4 = value; break; }
+ tmp = f4; f4 = value; value = tmp;
+ case 5:
+ if (N == 5) { f5 = value; break; }
+ tmp = f5; f5 = value; value = tmp;
+
+ index = FIELDS_STORE_SIZE;
+ default:
+ ensureCapacity(N + 1);
+ if (index != N) {
+ System.arraycopy(data, index - FIELDS_STORE_SIZE,
+ data, index - FIELDS_STORE_SIZE + 1,
+ N - index);
+ }
+ data[index - FIELDS_STORE_SIZE] = value;
+ }
+ size = N + 1;
+ }
+
+ public final void remove(int index) {
+ int N = size;
+ if (!(0 <= index && index < N)) throw invalidIndex(index, N);
+ --N;
+ switch (index) {
+ case 0:
+ if (N == 0) { f0 = null; break; }
+ f0 = f1;
+ case 1:
+ if (N == 1) { f1 = null; break; }
+ f1 = f2;
+ case 2:
+ if (N == 2) { f2 = null; break; }
+ f2 = f3;
+ case 3:
+ if (N == 3) { f3 = null; break; }
+ f3 = f4;
+ case 4:
+ if (N == 4) { f4 = null; break; }
+ f4 = f5;
+ case 5:
+ if (N == 5) { f5 = null; break; }
+ f5 = data[0];
+
+ index = FIELDS_STORE_SIZE;
+ default:
+ if (index != N) {
+ System.arraycopy(data, index - FIELDS_STORE_SIZE + 1,
+ data, index - FIELDS_STORE_SIZE,
+ N - index);
+ }
+ data[N - FIELDS_STORE_SIZE] = null;
+ }
+ size = N;
+ }
+
+ public final void clear() {
+ int N = size;
+ for (int i = 0; i != N; ++i) {
+ setImpl(i, null);
+ }
+ size = 0;
+ }
+
+ public final Object[] toArray() {
+ Object[] array = new Object[size];
+ toArray(array, 0);
+ return array;
+ }
+
+ public final void toArray(Object[] array) {
+ toArray(array, 0);
+ }
+
+ public final void toArray(Object[] array, int offset) {
+ int N = size;
+ switch (N) {
+ default:
+ System.arraycopy(data, 0, array, offset + FIELDS_STORE_SIZE,
+ N - FIELDS_STORE_SIZE);
+ case 6: array[offset + 5] = f5;
+ case 5: array[offset + 4] = f4;
+ case 4: array[offset + 3] = f3;
+ case 3: array[offset + 2] = f2;
+ case 2: array[offset + 1] = f1;
+ case 1: array[offset + 0] = f0;
+ case 0: break;
+ }
+ }
+
+ private void ensureCapacity(int minimalCapacity) {
+ int required = minimalCapacity - FIELDS_STORE_SIZE;
+ if (required <= 0) throw new IllegalArgumentException();
+ if (data == null) {
+ int alloc = FIELDS_STORE_SIZE * 2;
+ if (alloc < required) {
+ alloc = required;
+ }
+ data = new Object[alloc];
+ } else {
+ int alloc = data.length;
+ if (alloc < required) {
+ if (alloc <= FIELDS_STORE_SIZE) {
+ alloc = FIELDS_STORE_SIZE * 2;
+ } else {
+ alloc *= 2;
+ }
+ if (alloc < required) {
+ alloc = required;
+ }
+ Object[] tmp = new Object[alloc];
+ if (size > FIELDS_STORE_SIZE) {
+ System.arraycopy(data, 0, tmp, 0,
+ size - FIELDS_STORE_SIZE);
+ }
+ data = tmp;
+ }
+ }
+ }
+
+ private static RuntimeException invalidIndex(int index, int upperBound) {
+ // \u2209 is "NOT ELEMENT OF"
+ String msg = index+" \u2209 [0, "+upperBound+')';
+ return new IndexOutOfBoundsException(msg);
+ }
+
+ private static RuntimeException invalidEmptyStackAccess() {
+ throw new RuntimeException("Empty stack");
+ }
+
+ private void writeObject(ObjectOutputStream os) throws IOException {
+ os.defaultWriteObject();
+ int N = size;
+ for (int i = 0; i != N; ++i) {
+ Object obj = getImpl(i);
+ os.writeObject(obj);
+ }
+ }
+
+ private void readObject(ObjectInputStream is)
+ throws IOException, ClassNotFoundException
+ {
+ is.defaultReadObject(); // It reads size
+ int N = size;
+ if (N > FIELDS_STORE_SIZE) {
+ data = new Object[N - FIELDS_STORE_SIZE];
+ }
+ for (int i = 0; i != N; ++i) {
+ Object obj = is.readObject();
+ setImpl(i, obj);
+ }
+ }
+
+ static final long serialVersionUID = 7448768847663119705L;
+
+// Number of data elements
+ private int size;
+
+ private static final int FIELDS_STORE_SIZE = 6;
+ private transient Object f0, f1, f2, f3, f4, f5;
+ private transient Object[] data;
+}
diff --git a/js/js.translator/js.translator.iml b/js/js.translator/js.translator.iml
index 7f4ef570412..517bbe9302c 100644
--- a/js/js.translator/js.translator.iml
+++ b/js/js.translator/js.translator.iml
@@ -15,5 +15,4 @@
-
-
+
\ No newline at end of file