diff --git a/mozilla/js/js2/java/BinaryDigitReader.java b/mozilla/js/js2/java/BinaryDigitReader.java new file mode 100644 index 00000000000..882fde202b3 --- /dev/null +++ b/mozilla/js/js2/java/BinaryDigitReader.java @@ -0,0 +1,43 @@ +/* -*- Mode: java; tab-width: 8 -*- + * Copyright © 1997, 1998 Netscape Communications Corporation, + * All Rights Reserved. + */ + +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/mozilla/js/js2/java/Context.java b/mozilla/js/js2/java/Context.java new file mode 100644 index 00000000000..cb32c9e7f18 --- /dev/null +++ b/mozilla/js/js2/java/Context.java @@ -0,0 +1,79 @@ +import java.util.Hashtable; + +class Context { + + static boolean printTrees = true; + public static final int VERSION_DEFAULT = 0; + public static final int VERSION_1_2 = 120; + + public Context() { + setLanguageVersion(VERSION_DEFAULT); + } + + public synchronized void enter() throws ThreadLinkException, + InterruptedException + { + Thread t = Thread.currentThread(); + + while (currentThread != null) { + + // Check to avoid deadlock + if (t == currentThread) { + throw new ThreadLinkException( + "Context already associated with a Thread"); + } + wait(); + } + currentThread = t; + + synchronized (threadContexts) { + Context cx = (Context) threadContexts.get(t); + if (cx != null) { + currentThread = null; + throw new ThreadLinkException( + "Thread already associated with a Context"); + } + threadContexts.put(t, this); + } + } + + static Context getContext() { + Thread t = Thread.currentThread(); + Context cx = (Context) threadContexts.get(t); + if (cx == null) { + throw new WrappedException(new ThreadLinkException( + "No Context associated with current Thread")); + } + return cx; + } + + public void setLanguageVersion(int version) { + this.version = version; + } + + public int getLanguageVersion() { + return version; + } + int version; + + static String getMessage(String messageId, Object[] arguments) + { + return messageId; + } + + static void reportError(String message, String sourceName, + int lineno, String lineSource, + int lineOffset) + { + } + + static void reportWarning(String message, String sourceName, + int lineno, String lineSource, + int lineOffset) + { + } + + private Thread currentThread; + private static Hashtable threadContexts = new Hashtable(11); +}; + \ No newline at end of file diff --git a/mozilla/js/js2/java/FunctionNode.java b/mozilla/js/js2/java/FunctionNode.java new file mode 100644 index 00000000000..9ffad550aa5 --- /dev/null +++ b/mozilla/js/js2/java/FunctionNode.java @@ -0,0 +1,18 @@ +/* -*- Mode: java; tab-width: 8 -*- + * Copyright © 1998 Netscape Communications Corporation, + * All Rights Reserved. + */ + +import java.util.*; + +public class FunctionNode extends Node { + + public FunctionNode(String name, Node left, Node right) { + super(TokenStream.FUNCTION, left, right, name); + } + + public String getFunctionName() { + return getString(); + } + +} diff --git a/mozilla/js/js2/java/IRFactory.java b/mozilla/js/js2/java/IRFactory.java new file mode 100644 index 00000000000..1b6997519fd --- /dev/null +++ b/mozilla/js/js2/java/IRFactory.java @@ -0,0 +1,994 @@ +/* -*- Mode: java; tab-width: 8 -*- + * Copyright © 1997, 1998 Netscape Communications Corporation, + * All Rights Reserved. + */ + +/** + * This class allows the creation of nodes, and follows the Factory pattern. + * + * @see Node + * @see NodeFactory + * @see ASTFactory + + * @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, sourceName); + Node children = ((Node) body).getFirstChild(); + if (children != null) + result.addChildrenToBack(children); + result.putProp(Node.SOURCENAME_PROP, sourceName); + result.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno)); + result.putProp(Node.END_LINENO_PROP, new Integer(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, String id) { + return new Node(nodeType, id); + } + + public Object createLeaf(int nodeType, int nodeOp) { + return new Node(nodeType, new Integer(nodeOp)); + } + + /** + * Statement leaf nodes. + */ + + public Object createSwitch(int lineno) { + return new Node(TokenStream.SWITCH, new Integer(lineno)); + } + + public Object createVariables(int lineno) { + return new Node(TokenStream.VAR, new Integer(lineno)); + } + + public Object createExprStatement(Object expr, int lineno) { + return new Node(TokenStream.EXPRSTMT, (Node) expr, new Integer(lineno)); + } + + /** + * Name + */ + public Object createName(String name) { + return new Node(TokenStream.NAME, name); + } + + /** + * String (for literals) + */ + public Object createString(String string) { + return new Node(TokenStream.STRING, string); + } + + /** + * Number (for literals) + */ + public Object createNumber(Number number) { + return new Node(TokenStream.NUMBER, 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, + new Integer(TokenStream.TRUE)); + Node result = new Node(TokenStream.CATCH, (Node)createName(varName), + (Node)catchCond, (Node)stmts); + result.setDatum(new Integer(lineno)); + return result; + } + + /** + * Throw + */ + public Object createThrow(Object expr, int lineno) { + return new Node(TokenStream.THROW, (Node)expr, new Integer(lineno)); + } + + /** + * Return + */ + public Object createReturn(Object expr, int lineno) { + return expr == null + ? new Node(TokenStream.RETURN, new Integer(lineno)) + : new Node(TokenStream.RETURN, (Node)expr, new Integer(lineno)); + } + + /** + * Label + */ + public Object createLabel(String label, int lineno) { + Node result = new Node(TokenStream.LABEL, new Integer(lineno)); + Node name = new Node(TokenStream.NAME, label); + result.addChildToBack(name); + return result; + } + + /** + * Break (possibly labeled) + */ + public Object createBreak(String label, int lineno) { + Node result = new Node(TokenStream.BREAK, new Integer(lineno)); + if (label == null) { + return result; + } else { + Node name = new Node(TokenStream.NAME, label); + result.addChildToBack(name); + return result; + } + } + + /** + * Continue (possibly labeled) + */ + public Object createContinue(String label, int lineno) { + Node result = new Node(TokenStream.CONTINUE, new Integer(lineno)); + if (label == null) { + return result; + } else { + Node name = new Node(TokenStream.NAME, label); + result.addChildToBack(name); + 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, new Integer(lineno)); + } + + public Object createFunctionNode(String name, Object args, + Object statements) + { + if (name == null) + name = ""; + return new FunctionNode(name, (Node) args, (Node) statements); + } + + public Object createFunction(String name, Object args, Object statements, + String sourceName, int baseLineno, + int endLineno, Object source) + { + Node f = (Node) createFunctionNode(name, args, statements); + f.putProp(Node.SOURCENAME_PROP, sourceName); + f.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno)); + f.putProp(Node.END_LINENO_PROP, new Integer(endLineno)); + if (source != null) + f.putProp(Node.SOURCE_PROP, source); + Node result = new Node(TokenStream.FUNCTION, name); + result.putProp(Node.FUNCTION_PROP, f); + return result; + } + + /** + * 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) { + // Just add a GOTO to the condition in the do..while + Node result = (Node) createDoWhile(body, cond, lineno); + Node condTarget = (Node) result.getProp(Node.CONTINUE_PROP); + Node GOTO = new Node(TokenStream.GOTO); + GOTO.putProp(Node.TARGET_PROP, condTarget); + result.addChildToFront(GOTO); + return result; + } + + /** + * DoWhile + */ + public Object createDoWhile(Object body, Object cond, int lineno) { + Node result = new Node(TokenStream.LOOP, new Integer(lineno)); + Node bodyTarget = new Node(TokenStream.TARGET); + Node condTarget = new Node(TokenStream.TARGET); + Node IFEQ = new Node(TokenStream.IFEQ, (Node)cond); + IFEQ.putProp(Node.TARGET_PROP, bodyTarget); + Node breakTarget = new Node(TokenStream.TARGET); + + result.addChildToBack(bodyTarget); + result.addChildrenToBack((Node)body); + result.addChildToBack(condTarget); + result.addChildToBack(IFEQ); + result.addChildToBack(breakTarget); + + result.putProp(Node.BREAK_PROP, breakTarget); + result.putProp(Node.CONTINUE_PROP, condTarget); + + return result; + } + + /** + * For + */ + public Object createFor(Object init, Object test, Object incr, + Object body, int lineno) + { + if (((Node) test).getType() == TokenStream.VOID) { + test = new Node(TokenStream.PRIMARY, + new Integer(TokenStream.TRUE)); + } + Node result = (Node)createWhile(test, body, lineno); + Node initNode = (Node) init; + if (initNode.getType() != TokenStream.VOID) { + if (initNode.getType() != TokenStream.VAR) + initNode = new Node(TokenStream.POP, initNode); + result.addChildToFront(initNode); + } + Node condTarget = (Node)result.getProp(Node.CONTINUE_PROP); + Node incrTarget = new Node(TokenStream.TARGET); + result.addChildBefore(incrTarget, condTarget); + if (((Node) incr).getType() != TokenStream.VOID) { + incr = createUnary(TokenStream.POP, incr); + result.addChildAfter((Node)incr, incrTarget); + } + result.putProp(Node.CONTINUE_PROP, incrTarget); + return result; + } + + /** + * For .. In + * + */ + public Object createForIn(Object lhs, Object obj, Object body, int lineno) { + String name; + Node lhsNode = (Node) lhs; + Node objNode = (Node) obj; + int type = lhsNode.getType(); + + Node lvalue = lhsNode; + switch (type) { + + case TokenStream.NAME: + case TokenStream.GETPROP: + case TokenStream.GETELEM: + break; + + case TokenStream.VAR: + /* + * check that there was only one variable given. + * we can't do this in the parser, because then the + * parser would have to know something about the + * 'init' node of the for-in loop. + */ + Node lastChild = lhsNode.getLastChild(); + if (lhsNode.getFirstChild() != lastChild) { + reportError("msg.mult.index"); + } + lvalue = new Node(TokenStream.NAME, lastChild.getString()); + break; + + default: + reportError("msg.bad.for.in.lhs"); + return objNode; + } + + Node init = new Node(TokenStream.ENUMINIT, objNode); + Node next = new Node(TokenStream.ENUMNEXT); + next.putProp(Node.ENUM_PROP, init); + Node temp = createNewTemp(next); + Node cond = new Node(TokenStream.EQOP, new Integer(TokenStream.NE)); + cond.addChildToBack(temp); + cond.addChildToBack(new Node(TokenStream.PRIMARY, + new Integer(TokenStream.NULL))); + Node newBody = new Node(TokenStream.BLOCK); + Node assign = (Node) createAssignment(TokenStream.NOP, lvalue, + createUseTemp(temp), null, + false); + newBody.addChildToBack(new Node(TokenStream.POP, assign)); + newBody.addChildToBack((Node) body); + Node result = (Node) createWhile(cond, newBody, lineno); + + result.addChildToFront(init); + if (type == TokenStream.VAR) + result.addChildToFront(lhsNode); + + Node done = new Node(TokenStream.ENUMDONE); + done.putProp(Node.ENUM_PROP, init); + result.addChildToBack(done); + + return result; + } + + /** + * Try/Catch/Finally + * + * The IRFactory tries to express as much as possible in the tree; + * the responsibilities remaining for Codegen are to add the Java + * handlers: (Either (but not both) of TARGET and FINALLY might not + * be defined) + + * - a catch handler for javascript exceptions that unwraps the + * exception onto the stack and GOTOes to the catch target - + * TARGET_PROP in the try node. + + * - a finally handler that catches any exception, stores it to a + * temporary, and JSRs to the finally target - FINALLY_PROP in the + * try node - before re-throwing the exception. + + * ... and a goto to GOTO around these handlers. + */ + public Object createTryCatchFinally(Object tryblock, Object catchblocks, + Object finallyblock, int lineno) + { + Node trynode = (Node)tryblock; + + // short circuit + if (trynode.getType() == TokenStream.BLOCK && !trynode.hasChildren()) + return trynode; + + Node pn = new Node(TokenStream.TRY, trynode, new Integer(lineno)); + Node catchNodes = (Node)catchblocks; + boolean hasCatch = catchNodes.hasChildren(); + boolean hasFinally = false; + Node finallyNode = null; + Node finallyTarget = null; + if (finallyblock != null) { + finallyNode = (Node)finallyblock; + hasFinally = (finallyNode.getType() != TokenStream.BLOCK + || finallyNode.hasChildren()); + if (hasFinally) { + // make a TARGET for the finally that the tcf node knows about + finallyTarget = new Node(TokenStream.TARGET); + pn.putProp(Node.FINALLY_PROP, finallyTarget); + + // add jsr finally to the try block + Node jsrFinally = new Node(TokenStream.JSR); + jsrFinally.putProp(Node.TARGET_PROP, finallyTarget); + pn.addChildToBack(jsrFinally); + } + } + + // short circuit + if (!hasFinally && !hasCatch) // bc finally might be an empty block... + return trynode; + + Node endTarget = new Node(TokenStream.TARGET); + Node GOTOToEnd = new Node(TokenStream.GOTO); + GOTOToEnd.putProp(Node.TARGET_PROP, endTarget); + pn.addChildToBack(GOTOToEnd); + + if (hasCatch) { + /* + * + Given + + try { + throw 3; + } catch (e: e instanceof Object) { + print("object"); + } catch (e2) { + print(e2); + } + + rewrite as + + try { + throw 3; + } catch (x) { + o = newScope(); + o.e = x; + with (o) { + if (e instanceof Object) { + print("object"); + } + } + o2 = newScope(); + o2.e2 = x; + with (o2) { + if (true) { + print(e2); + } + } + } + */ + // make a TARGET for the catch that the tcf node knows about + Node catchTarget = new Node(TokenStream.TARGET); + pn.putProp(Node.TARGET_PROP, catchTarget); + // mark it + pn.addChildToBack(catchTarget); + + // get the exception object and store it in a temp + Node exn = createNewLocal(new Node(TokenStream.VOID)); + pn.addChildToBack(new Node(TokenStream.POP, exn)); + + Node endCatch = new Node(TokenStream.TARGET); + + // add [jsr finally?] goto end to each catch block + // expects catchNode children to be (cond block) pairs. + Node cb = catchNodes.getFirstChild(); + while (cb != null) { + Node catchStmt = new Node(TokenStream.BLOCK); + int catchLineNo = ((Integer)cb.getDatum()).intValue(); + + Node name = cb.getFirstChild(); + Node cond = name.getNextSibling(); + Node catchBlock = cond.getNextSibling(); + cb.removeChild(name); + cb.removeChild(cond); + cb.removeChild(catchBlock); + + Node newScope = createNewLocal(new Node(TokenStream.NEWSCOPE)); + Node initScope = new Node(TokenStream.SETPROP, newScope, + new Node(TokenStream.STRING, + name.getString()), + createUseLocal(exn)); + catchStmt.addChildToBack(new Node(TokenStream.POP, initScope)); + + catchBlock.addChildToBack(new Node(TokenStream.LEAVEWITH)); + Node GOTOToEndCatch = new Node(TokenStream.GOTO); + GOTOToEndCatch.putProp(Node.TARGET_PROP, endCatch); + catchBlock.addChildToBack(GOTOToEndCatch); + + Node ifStmt = (Node) createIf(cond, catchBlock, null, catchLineNo); + // Try..catch produces "with" code in order to limit + // the scope of the exception object. + // OPT: We should be able to figure out the correct + // scoping at compile-time and avoid the + // runtime overhead. + Node withStmt = (Node) createWith(createUseLocal(newScope), + ifStmt, catchLineNo); + catchStmt.addChildToBack(withStmt); + + pn.addChildToBack(catchStmt); + + // move to next cb + cb = cb.getNextSibling(); + } + + // Generate code to rethrow if no catch clause was executed + Node rethrow = new Node(TokenStream.THROW, createUseLocal(exn)); + pn.addChildToBack(rethrow); + + pn.addChildToBack(endCatch); + // add a JSR finally if needed + if (hasFinally) { + Node jsrFinally = new Node(TokenStream.JSR); + jsrFinally.putProp(Node.TARGET_PROP, finallyTarget); + pn.addChildToBack(jsrFinally); + Node GOTO = new Node(TokenStream.GOTO); + GOTO.putProp(Node.TARGET_PROP, endTarget); + pn.addChildToBack(GOTO); + } + } + + if (hasFinally) { + pn.addChildToBack(finallyTarget); + Node returnTemp = createNewLocal(new Node(TokenStream.VOID)); + Node popAndMake = new Node(TokenStream.POP, returnTemp); + pn.addChildToBack(popAndMake); + pn.addChildToBack(finallyNode); + Node ret = createUseLocal(returnTemp); + + // add the magic prop that makes it output a RET + ret.putProp(Node.TARGET_PROP, Boolean.TRUE); + pn.addChildToBack(ret); + } + pn.addChildToBack(endTarget); + return pn; + } + + /** + * Throw, Return, Label, Break and Continue are defined in ASTFactory. + */ + + /** + * With + */ + public Object createWith(Object obj, Object body, int lineno) { + Node result = new Node(TokenStream.BLOCK, new Integer(lineno)); + result.addChildToBack(new Node(TokenStream.ENTERWITH, (Node)obj)); + Node bodyNode = new Node(TokenStream.WITH, (Node) body, + new Integer(lineno)); + result.addChildrenToBack(bodyNode); + result.addChildToBack(new Node(TokenStream.LEAVEWITH)); + return result; + } + + /** + * Array Literal + *
createArrayLiteral rewrites its argument as array creation + * plus a series of array element entries, so later compiler + * stages don't need to know about array literals. + */ + public Object createArrayLiteral(Object obj) { + Node array; + Node result; + array = result = new Node(TokenStream.NEW, + new Node(TokenStream.NAME, "Array")); + Node temp = createNewTemp(result); + result = temp; + + java.util.Enumeration children = ((Node) obj).getChildIterator(); + + Node elem = null; + int i = 0; + while (children.hasMoreElements()) { + elem = (Node) children.nextElement(); + if (elem.getType() == TokenStream.PRIMARY && + elem.getInt() == TokenStream.UNDEFINED) + { + i++; + continue; + } + Node addelem = new Node(TokenStream.SETELEM, createUseTemp(temp), + new Node(TokenStream.NUMBER, + new Integer(i)), + elem); + i++; + result = new Node(TokenStream.COMMA, result, addelem); + } + + /* + * If the version is 120, then new Array(4) means create a new + * array with 4 as the first element. In this case, we might + * need to explicitly check against trailing undefined + * elements in the array literal, and set the length manually + * if these occur. Otherwise, we can add an argument to the + * node specifying new Array() to provide the array length. + * (Which will make Array optimizations involving allocating a + * Java array to back the javascript array work better.) + */ + if (Context.getContext().getLanguageVersion() == Context.VERSION_1_2) { + /* When last array element is empty, we need to set the + * length explicitly, because we can't depend on SETELEM + * to do it for us - because empty [,,] array elements + * never set anything at all. */ + if (elem != null && + elem.getType() == TokenStream.PRIMARY && + elem.getInt() == TokenStream.UNDEFINED) + { + Node setlength = new Node(TokenStream.SETPROP, + createUseTemp(temp), + new Node(TokenStream.STRING, + "length"), + new Node(TokenStream.NUMBER, + new Integer(i))); + result = new Node(TokenStream.COMMA, result, setlength); + } + } else { + array.addChildToBack(new Node(TokenStream.NUMBER, + new Integer(i))); + } + return new Node(TokenStream.COMMA, result, createUseTemp(temp)); + } + + /** + * Object Literals + *
createObjectLiteral rewrites its argument as object + * creation plus object property entries, so later compiler + * stages don't need to know about object literals. + */ + public Object createObjectLiteral(Object obj) { + Node result = new Node(TokenStream.NEW, new Node(TokenStream.NAME, + "Object")); + Node temp = createNewTemp(result); + result = temp; + + java.util.Enumeration children = ((Node) obj).getChildIterator(); + + while (children.hasMoreElements()) { + Node elem = (Node)children.nextElement(); + + int op = (elem.getType() == TokenStream.NAME) + ? TokenStream.SETPROP + : TokenStream.SETELEM; + Node addelem = new Node(op, createUseTemp(temp), + elem, (Node)children.nextElement()); + result = new Node(TokenStream.COMMA, result, addelem); + } + return new Node(TokenStream.COMMA, result, createUseTemp(temp)); + } + + /** + * Regular expressions + */ + public Object createRegExp(String string, String flags) { + return flags.length() == 0 + ? new Node(TokenStream.OBJECT, + new Node(TokenStream.STRING, string)) + : new Node(TokenStream.OBJECT, + new Node(TokenStream.STRING, string), + new Node(TokenStream.STRING, flags)); + } + + /** + * If statement + */ + public Object createIf(Object cond, Object ifTrue, Object ifFalse, + int lineno) + { + Node result = new Node(TokenStream.BLOCK, new Integer(lineno)); + Node ifNotTarget = new Node(TokenStream.TARGET); + Node IFNE = new Node(TokenStream.IFNE, (Node) cond); + IFNE.putProp(Node.TARGET_PROP, ifNotTarget); + + result.addChildToBack(IFNE); + result.addChildrenToBack((Node)ifTrue); + + if (ifFalse != null) { + Node GOTOToEnd = new Node(TokenStream.GOTO); + Node endTarget = new Node(TokenStream.TARGET); + GOTOToEnd.putProp(Node.TARGET_PROP, endTarget); + result.addChildToBack(GOTOToEnd); + result.addChildToBack(ifNotTarget); + result.addChildrenToBack((Node)ifFalse); + result.addChildToBack(endTarget); + } else { + result.addChildToBack(ifNotTarget); + } + + return result; + } + + public Object createTernary(Object cond, Object ifTrue, Object ifFalse) { + return createIf(cond, ifTrue, ifFalse, -1); + } + + /** + * Unary + */ + public Object createUnary(int nodeType, Object child) { + Node childNode = (Node) child; + if (nodeType == TokenStream.DELPROP) { + int childType = childNode.getType(); + Node left; + Node right; + if (childType == TokenStream.NAME) { + // Transform Delete(Name "a") + // to Delete(Bind("a"), String("a")) + childNode.setType(TokenStream.BINDNAME); + left = childNode; + right = childNode.cloneNode(); + right.setType(TokenStream.STRING); + } else if (childType == TokenStream.GETPROP || + childType == TokenStream.GETELEM) + { + left = childNode.getFirstChild(); + right = childNode.getLastChild(); + childNode.removeChild(left); + childNode.removeChild(right); + } else { + reportError("msg.del.nonref"); + return new Node(TokenStream.PRIMARY, + new Integer(TokenStream.TRUE)); + } + return new Node(nodeType, left, right); + } + return new Node(nodeType, childNode); + } + + public Object createUnary(int nodeType, int nodeOp, Object child) { + Node childNode = (Node) child; + int childType = childNode.getType(); + if (nodeOp == TokenStream.TYPEOF && + childType == TokenStream.NAME) + { + childNode.setType(TokenStream.TYPEOF); + return childNode; + } + + if (nodeType == TokenStream.INC || nodeType == TokenStream.DEC) { + + if (!hasSideEffects(childNode) + && (nodeOp == TokenStream.POST) + && (childType == TokenStream.NAME + || childType == TokenStream.GETPROP + || childType == TokenStream.GETELEM)) + { + // if it's not a LHS type, createAssignment (below) will throw + // an exception. + return new Node(nodeType, childNode); + } + + /* + * Transform INC/DEC ops to +=1, -=1, + * expecting later optimization of all +/-=1 cases to INC, DEC. + */ + // we have to use Double for now, because + // 0.0 and 1.0 are stored as dconst_[01], + // and using a Float creates a stack mismatch. + Node rhs = (Node) createNumber(new Double(1.0)); + + return createAssignment(nodeType == TokenStream.INC + ? TokenStream.ADD + : TokenStream.SUB, + childNode, + rhs, + ScriptRuntime.NumberClass, + nodeOp == TokenStream.POST); + } + + Node result = new Node(nodeType, new Integer(nodeOp)); + result.addChildToBack((Node)child); + return result; + } + + /** + * 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; +/* + case TokenStream.AND: + temp = createNewTemp((Node) left); + return createTernary(temp, right, createUseTemp(temp)); + + case TokenStream.OR: + temp = createNewTemp((Node) left); + return createTernary(temp, createUseTemp(temp), right); +*/ + } + 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, + new Integer(nodeOp)); + } + + public Object createAssignment(int nodeOp, Node left, Node right, + Class convert, boolean postfix) + { + int nodeType = left.getType(); + String idString; + Node id = null; + switch (nodeType) { + case TokenStream.NAME: + return createSetName(nodeOp, left, right, convert, postfix); + + case TokenStream.GETPROP: + idString = (String) left.getProp(Node.SPECIAL_PROP_PROP); + if (idString != null) + id = new Node(TokenStream.STRING, idString); + /* fall through */ + case TokenStream.GETELEM: + if (id == null) + id = left.getLastChild(); + return createSetProp(nodeType, nodeOp, left.getFirstChild(), + id, right, convert, postfix); + default: + reportError("msg.bad.lhs.assign"); + return left; + } + } + + private Node createConvert(Class toType, Node expr) { + if (toType == null) + return expr; + Node result = new Node(TokenStream.CONVERT, expr); + result.putProp(Node.TYPE_PROP, ScriptRuntime.NumberClass); + return result; + } + + private Object createSetName(int nodeOp, Node left, Node right, + Class convert, boolean postfix) + { + if (nodeOp == TokenStream.NOP) { + left.setType(TokenStream.BINDNAME); + return new Node(TokenStream.SETNAME, left, right); + } + + String s = left.getString(); + + if (s.equals("__proto__") || s.equals("__parent__")) { + Node result = new Node(TokenStream.SETPROP, left, right); + result.putProp(Node.SPECIAL_PROP_PROP, s); + return result; + } + + Node opLeft = new Node(TokenStream.NAME, s); + if (convert != null) + opLeft = createConvert(convert, opLeft); + if (postfix) + opLeft = createNewTemp(opLeft); + Node op = new Node(nodeOp, opLeft, right); + + Node lvalueLeft = new Node(TokenStream.BINDNAME, s); + Node result = new Node(TokenStream.SETNAME, lvalueLeft, op); + if (postfix) { + result = new Node(TokenStream.COMMA, result, + createUseTemp(opLeft)); + } + return result; + } + + public Node createNewTemp(Node n) { + int type = n.getType(); + if (type == TokenStream.STRING || type == TokenStream.NUMBER) { + // Optimization: clone these values rather than storing + // and loading from a temp + return n; + } + Node result = new Node(TokenStream.NEWTEMP, n); + return result; + } + + public Node createUseTemp(Node newTemp) { + int type = newTemp.getType(); + if (type == TokenStream.NEWTEMP) { + Node result = new Node(TokenStream.USETEMP); + result.putProp(Node.TEMP_PROP, newTemp); + Integer n = (Integer) newTemp.getProp(Node.USES_PROP); + if (n == null) { + n = new Integer(1); + } else { + if (n.intValue() < Integer.MAX_VALUE) + n = new Integer(n.intValue() + 1); + } + newTemp.putProp(Node.USES_PROP, n); + return result; + } + return newTemp.cloneNode(); + } + + public Node createNewLocal(Node n) { + Node result = new Node(TokenStream.NEWLOCAL, n); + return result; + } + + public Node createUseLocal(Node newLocal) { + int type = newLocal.getType(); + if (type == TokenStream.NEWLOCAL) { + Node result = new Node(TokenStream.USELOCAL); + result.putProp(Node.LOCAL_PROP, newLocal); + return result; + } + return newLocal.cloneNode(); // what's this path for ? + } + + public static boolean hasSideEffects(Node exprTree) { + switch (exprTree.getType()) { + case TokenStream.SETPROP: + case TokenStream.SETELEM: + case TokenStream.CALL: + case TokenStream.NEW: + return true; + default: + Node child = exprTree.getFirstChild(); + while (child != null) { + if (hasSideEffects(child)) + return true; + else + child = child.getNextSibling(); + } + break; + } + return false; + } + + private Node createSetProp(int nodeType, int nodeOp, Node obj, Node id, + Node expr, Class convert, boolean postfix) + { + int type = nodeType == TokenStream.GETPROP + ? TokenStream.SETPROP + : TokenStream.SETELEM; + + Object datum = id.getDatum(); + if (type == TokenStream.SETPROP && datum != null && + datum instanceof String) + { + String s = (String) datum; + if (s.equals("__proto__") || s.equals("__parent__")) { + Node result = new Node(type, obj, expr); + result.putProp(Node.SPECIAL_PROP_PROP, s); + return result; + } + } + + if (nodeOp == TokenStream.NOP) + return new Node(type, obj, id, expr); +/* + if the RHS expression could modify the LHS we have + to construct a temporary to hold the LHS context + prior to running the expression + +*/ + Node tmp1, tmp2, opLeft; + if (hasSideEffects(expr) || (obj.getType() != TokenStream.NAME)) { + tmp1 = createNewTemp(obj); + Node useTmp1 = createUseTemp(tmp1); + + tmp2 = createNewTemp(id); + Node useTmp2 = createUseTemp(tmp2); + + opLeft = new Node(nodeType, useTmp1, useTmp2); + } else { + tmp1 = obj.cloneNode(); + tmp2 = id.cloneNode(); + opLeft = new Node(nodeType, obj, id); + } + + if (convert != null) + opLeft = createConvert(convert, opLeft); + if (postfix) + opLeft = createNewTemp(opLeft); + Node op = new Node(nodeOp, opLeft, expr); + + Node result = new Node(type, tmp1, tmp2, op); + if (postfix) { + result = new Node(TokenStream.COMMA, result, + createUseTemp(opLeft)); + } + + return result; + } + + private void reportError(String msgResource) { + String message = Context.getMessage(msgResource, null); + 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/mozilla/js/js2/java/Interpreter.java b/mozilla/js/js2/java/Interpreter.java new file mode 100644 index 00000000000..df7a26ceac7 --- /dev/null +++ b/mozilla/js/js2/java/Interpreter.java @@ -0,0 +1,31 @@ +public class Interpreter { + + + void executeScript(Node node) + { + Node child = node.getFirstChild(); + while (child != null) { + if (child.getType() != TokenStream.FUNCTION) + executeCode(child); + child = child.getNextSibling(); + } + } + + void executeCode(Node top) + { + PostorderNodeIterator ni = new PostorderNodeIterator(top); + + JSStack theStack = new JSStack(); + + Node n = ni.nextNode(); + while (n != null) { + ni = n.execute(theStack, ni); + n = ni.nextNode(); + } + + + } + + + +} \ No newline at end of file diff --git a/mozilla/js/js2/java/JSStack.java b/mozilla/js/js2/java/JSStack.java new file mode 100644 index 00000000000..f99d6731674 --- /dev/null +++ b/mozilla/js/js2/java/JSStack.java @@ -0,0 +1,18 @@ + +import java.util.Stack; + +class JSStack { + + Stack stack = new Stack(); + + void push(StackValue value) + { + stack.push(value); + } + + StackValue pop() + { + return (StackValue)stack.pop(); + } + +} \ No newline at end of file diff --git a/mozilla/js/js2/java/LineBuffer.java b/mozilla/js/js2/java/LineBuffer.java new file mode 100644 index 00000000000..f3b73cea8fb --- /dev/null +++ b/mozilla/js/js2/java/LineBuffer.java @@ -0,0 +1,333 @@ +/* -*- Mode: Java; tab-width: 8 -*- + * Copyright © 1998 Netscape Communications Corporation, + * All Rights Reserved. + */ + +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 { + if (end == offset && !fill()) + return -1; + + // Do only a bitmask + branch per character, at the cost of + // three branches per low-bits-only character. + if ((buffer[offset] & '\ufff0') == 0) { + if (buffer[offset] == '\r') { + // if the next character is a newline, skip past it. + if ((offset + 1) < end) { + if (buffer[offset + 1] == '\n') + offset++; + } else { + // set a flag for fill(), in case the first char of the + // next fill is a newline. + lastWasCR = true; + } + } + else if (buffer[offset] != '\n') { + return (int) buffer[offset++]; + } + offset++; + prevStart = lineStart; + lineStart = offset; + lineno++; + return '\n'; + } + + return (int) buffer[offset++]; + } + + void unread() { + if (offset == 0) + // We can get here when we're asked to unread() an + // implicit EOF_CHAR. + + // This would also 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. So we're OK. + return; + offset--; + if ((buffer[offset] & '\ufff0') == 0 + && (buffer[offset] == '\r' || buffer[offset] == '\n')) { + // back off from the line start we presumably just registered... + lineStart = prevStart; + lineno--; + } + } + + int peek() throws IOException { + if (end == offset && !fill()) + return -1; + + if (buffer[offset] == '\r') + return '\n'; + + return buffer[offset]; + } + + boolean match(char c) throws IOException { + if (end == offset && !fill()) + return false; + + // This'd be a place where we'd need to map '\r' to '\n' and + // do other updates, but TokenStream never looks ahead for + // '\n', so we don't bother. + if (buffer[offset] == c) { + offset++; + return true; + } + return false; + } + + // Reconstruct a source line from the buffers. This can be slow... + String getLine() { + StringBuffer result = new StringBuffer(); + + int start = lineStart; + if (start >= offset) { + // the line begins somewhere in the other buffer; get that first. + if (otherStart < otherEnd) + // if a line ending was seen in the other buffer... otherwise + // just ignore this strange case. + result.append(otherBuffer, otherStart, + otherEnd - otherStart); + start = 0; + } + + // get the part of the line in the current buffer. + result.append(buffer, start, offset - start); + + // Get the remainder of the line. + int i = offset; + while(true) { + if (i == buffer.length) { + // 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. + char[] newBuffer = new char[buffer.length * 2]; + System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; + int charsRead = 0; + try { + charsRead = in.read(buffer, end, buffer.length - end); + } catch (IOException ioe) { + // ignore it, we're already displaying an error... + } + if (charsRead < 0) + break; + end += charsRead; + } + if (buffer[i] == '\r' || buffer[i] == '\n') + break; + i++; + } + + result.append(buffer, offset, i - offset); + return result.toString(); + } + + // Get the offset of the current character, relative to + // the line that getLine() returns. + int getOffset() { + if (lineStart >= offset) + // The line begins somewhere in the other buffer. + return offset + (otherEnd - otherStart); + else + return offset - lineStart; + } + + // Set a mark to indicate that the reader should begin + // accumulating characters for getString(). The string begins + // with the last character read. + void startString() { + if (offset == 0) { + // We can get here if startString is called after a peek() + // or failed match() with offset past the end of the + // buffer. + + // We're at the beginning of the buffer, and the previous character + // (which we want to include) is at the end of the last one, so + // we just go to StringBuffer mode. + stringSoFar = new StringBuffer(); + stringSoFar.append(otherBuffer, otherEnd - 1, 1); + + stringStart = -1; // Set sentinel value. + } else { + // Support restarting strings + stringSoFar = null; + stringStart = offset - 1; + } + } + + // Get a string consisting of the characters seen since the last + // startString. + String getString() { + String result; + + /* + * There's one strange case here: If the character offset currently + * points to (which we never want to include in the string) is + * a newline, then if the previous character is a carriage return, + * we probably want to exclude that as well. If the offset is 0, + * then we hope that fill() handled excluding it from stringSoFar. + */ + int loseCR = (offset > 0 && + buffer[offset] == '\n' && buffer[offset - 1] == '\r') ? + 1 : 0; + + if (stringStart != -1) { + // String mark is valid, and in this buffer. + + result = new String(buffer, stringStart, + offset - stringStart - loseCR); + } else { + // Exclude cr as well as nl of newline. If offset is 0, then + // hopefully fill() did the right thing. + result = (stringSoFar.append(buffer, 0, offset - loseCR)).toString(); + } + + stringStart = -1; + stringSoFar = null; + return result; + } + + boolean fill() throws IOException { + // not sure I care... + if (end - offset != 0) + throw new IOException("fill of non-empty buffer"); + + // If there's a string currently being accumulated, save + // off the progress. + + /* + * Exclude an end-of-buffer carriage return. NOTE this is not + * fully correct in the general case, because we really only + * want to exclude the carriage return if it's followed by a + * linefeed at the beginning of the next buffer. But we fudge + * because the scanner doesn't do this. + */ + int loseCR = (offset > 0 && lastWasCR) ? 1 : 0; + + if (stringStart != -1) { + // The mark is in the current buffer, save off from the mark to the + // end. + stringSoFar = new StringBuffer(); + + stringSoFar.append(buffer, stringStart, end - stringStart - loseCR); + stringStart = -1; + } else if (stringSoFar != null) { + // the string began prior to the current buffer, so save the + // whole current buffer. + stringSoFar.append(buffer, 0, end - loseCR); + } + + // 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. + otherStart = lineStart; + otherEnd = end; + + // set lineStart to a sentinel value, unless this is the first + // time around. + prevStart = lineStart = (otherBuffer == null) ? 0 : buffer.length + 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 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; + + private int stringStart = -1; + private StringBuffer stringSoFar = null; +} + + diff --git a/mozilla/js/js2/java/Node.java b/mozilla/js/js2/java/Node.java new file mode 100644 index 00000000000..259aaee1045 --- /dev/null +++ b/mozilla/js/js2/java/Node.java @@ -0,0 +1,473 @@ +/* -*- Mode: java; tab-width: 8 -*- + * Copyright © 1997, 1998 Netscape Communications Corporation, + * All Rights Reserved. + */ + +import java.util.*; + +/** + * This class implements the root of the intermediate representation. + * + * @author Norris Boyd + * @author Mike McCabe + */ + +public class Node implements Cloneable { + + 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, Object datum) { + type = nodeType; + this.datum = datum; + } + + public Node(int nodeType, Node child, Object datum) { + this(nodeType, child); + this.datum = datum; + } + + public Node(int nodeType, Node left, Node right, Object datum) { + this(nodeType, left, right); + this.datum = datum; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public boolean hasChildren() { + return first != null; + } + + public Node getFirstChild() { + return first; + } + + public Node getLastChild() { + return last; + } + + public Node getNextSibling() { + return next; + } + + 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 ShallowNodeIterator getChildIterator() { + return new ShallowNodeIterator(first); + } + + public PreorderNodeIterator getPreorderIterator() { + return new PreorderNodeIterator(this); + } + + 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 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; + + 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 String propNames[]; + + private static final String propToString(int propType) { + if (Context.printTrees && propNames == null) { + // If Context.printTrees is false, the compiler + // can remove all these strings. + String[] a = { + "TARGET", + "BREAK", + "CONTINUE", + "ENUM", + "FUNCTION", + "TEMP", + "LOCAL", + "CODEOFFSET", + "FIXUPS", + "VARS", + "USES", + "REGEXP", + "SWITCHES", + "CASES", + "DEFAULT", + "CASEARRAY", + "SOURCENAME", + "SOURCE", + "TYPE", + "SPECIAL_PROP", + "LABEL", + "FINALLY", + "LOCALCOUNT", + "TARGETBLOCK", + "VARIABLE", + "LASTUSE", + "ISNUMBER", + "DIRECTCALL", + "BASE_LINENO", + "END_LINENO", + "SPECIALCALL" + }; + propNames = a; + } + return propNames[propType]; + } + + public Object getProp(int propType) { + if (props == null) + return null; + return props.get(new Integer(propType)); + } + + public void putProp(int propType, Object prop) { + if (props == null) + props = new Hashtable(2); + if (prop == null) + props.remove(new Integer(propType)); + else + props.put(new Integer(propType), prop); + } + + public Object getDatum() { + return datum; + } + + public void setDatum(Object datum) { + this.datum = datum; + } + + public int getInt() { + return ((Number) datum).intValue(); + } + + public double getDouble() { + return ((Number) datum).doubleValue(); + } + + public long getLong() { + return ((Number) datum).longValue(); + } + + public String getString() { + return (String) datum; + } + + 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 String toString() { + if (Context.printTrees) { + StringBuffer sb = new StringBuffer(TokenStream.tokenToName(type)); + if (type == TokenStream.TARGET) { + sb.append(" "); + sb.append(hashCode()); + } + if (datum != null) { + sb.append(' '); + sb.append(datum.toString()); + } + if (props == null) + return sb.toString(); + + Enumeration keys = props.keys(); + Enumeration elems = props.elements(); + while (keys.hasMoreElements()) { + Integer key = (Integer) keys.nextElement(); + Object elem = elems.nextElement(); + sb.append(" ["); + sb.append(propToString(key.intValue())); + sb.append(": "); + switch (key.intValue()) { + 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 : + sb.append(elem.toString()); + 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'); + ShallowNodeIterator iterator = getChildIterator(); + if (iterator != null) { + while (iterator.hasMoreElements()) { + Node n = (Node) iterator.nextElement(); + if (n.getType() == TokenStream.FUNCTION) { + Node p = (Node) n.getProp(Node.FUNCTION_PROP); + if (p != null) + n = p; + } + s.append(n.toStringTreeHelper(level+1)); + } + } + return s.toString(); + } + return ""; + } + + /************************************************************************************************/ + + PostorderNodeIterator execute(JSStack theStack, PostorderNodeIterator ni) + { + StackValue lhs; + StackValue rhs; + Number num; + + switch (type) { + + case TokenStream.NUMBER : + num = (Number)getDatum(); + System.out.println("number " + num.doubleValue()); + return ni; + + case TokenStream.SETNAME : + System.out.println("setname"); + return ni; + + case TokenStream.BINDNAME : + System.out.println("name " + getString()); + return ni; + + case TokenStream.ADD : + rhs = theStack.pop(); + lhs = theStack.pop(); + theStack.push(new StackValue(lhs.dbl + rhs.dbl)); + return ni; + + } + + return ni; + } + + + /************************************************************************************************/ + + + public Node getFirst() { return first; } + public Node getNext() { return next; } + + protected int type; // type of the node; TokenStream.NAME for example + protected Node next; // next sibling + protected Node first; // first element of a linked list of children + protected Node last; // last element of a linked list of children + protected Hashtable props; + protected Object datum; // encapsulated data; depends on type +} + diff --git a/mozilla/js/js2/java/PostorderNodeIterator.java b/mozilla/js/js2/java/PostorderNodeIterator.java new file mode 100644 index 00000000000..b75e816dd14 --- /dev/null +++ b/mozilla/js/js2/java/PostorderNodeIterator.java @@ -0,0 +1,41 @@ + +import java.util.Stack; + +class PostorderNodeIterator { + + PostorderNodeIterator(Node n) + { + stack = new Stack(); + while (n.first != null) { + stack.push(n); + n = n.first; + } + start = n; + } + + Node nextNode() + { + if (current == null) + return current = start; + + if (stack.isEmpty()) + return null; + else { + current = current.next; + if (current != null) { + while (current.first != null) { + stack.push(current); + current = current.first; + } + } + else + current = (Node)stack.pop(); + } + + return current; + } + + Node start; + Node current; + Stack stack; +} \ No newline at end of file diff --git a/mozilla/js/js2/java/PreorderNodeIterator.java b/mozilla/js/js2/java/PreorderNodeIterator.java new file mode 100644 index 00000000000..bc5fc623b38 --- /dev/null +++ b/mozilla/js/js2/java/PreorderNodeIterator.java @@ -0,0 +1,58 @@ +/* -*- Mode: java; tab-width: 8 -*- + * Copyright © 1997, 1998 Netscape Communications Corporation, All Rights Reserved. + */ + +import java.util.Stack; + +/** + * This class implements a preorder tree iterator for the Node class. + * + * @see Node + * @author Norris Boyd + */ +public class PreorderNodeIterator { + public PreorderNodeIterator(Node n) { + start = n; + stack = new Stack(); + } + + public Node currentNode() { + return current; + } + + public Node getCurrentParent() { + return currentParent; + } + + public Node nextNode() { + if (current == null) + return current = start; + if (current.first != null) { + stack.push(current); + currentParent = current; + current = current.first; + } else { + current = current.next; + boolean isEmpty; + for (;;) { + isEmpty = stack.isEmpty(); + if (isEmpty || current != null) + break; + current = (Node) stack.pop(); + current = current.next; + } + currentParent = isEmpty ? null : (Node) stack.peek(); + } + return current; + } + + public void replaceCurrent(Node newNode) { + currentParent.replaceChild(current, newNode); + current = newNode; + } + + private Node start; + private Node current; + private Node currentParent; + private Stack stack; +} diff --git a/mozilla/js/js2/java/ScriptRuntime.java b/mozilla/js/js2/java/ScriptRuntime.java new file mode 100644 index 00000000000..336a51a2ab2 --- /dev/null +++ b/mozilla/js/js2/java/ScriptRuntime.java @@ -0,0 +1,128 @@ +class ScriptRuntime { + /* + * There's such a huge space (and some time) waste for the Foo.class + * syntax: the compiler sticks in a test of a static field in the + * enclosing class for null and the code for creating the class value. + * It has to do this since the reference has to get pushed off til + * executiontime (i.e. can't force an early load), but for the + * 'standard' classes - especially those in java.lang, we can trust + * that they won't cause problems by being loaded early. + */ + + public final static Class StringClass = String.class; + public final static Class NumberClass = Number.class; + public final static Class BooleanClass = Boolean.class; + public final static Class ByteClass = Byte.class; + public final static Class ShortClass = Short.class; + public final static Class IntegerClass = Integer.class; + public final static Class LongClass = Long.class; + public final static Class FloatClass = Float.class; + public final static Class DoubleClass = Double.class; + public final static Class CharacterClass = Character.class; + public final static Class ObjectClass = Object.class; + + // This definition of NaN is identical to that in java.lang.Double + // except that it is not final. This is a workaround for a bug in + // the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses + // (returns at least) of Double.NaN to be converted to 1.0. + // So we use ScriptRuntime.NaN instead of Double.NaN. + public static double NaN = 0.0d / 0.0; + + /* + * 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; + } + +} \ No newline at end of file diff --git a/mozilla/js/js2/java/ShallowNodeIterator.java b/mozilla/js/js2/java/ShallowNodeIterator.java new file mode 100644 index 00000000000..991df032264 --- /dev/null +++ b/mozilla/js/js2/java/ShallowNodeIterator.java @@ -0,0 +1,35 @@ +/* -*- Mode: java; tab-width: 8 -*- + * Copyright © 1997, 1998 Netscape Communications Corporation, All Rights Reserved. + */ + +import java.util.Enumeration; + +/** + * This class implements a child iterator for the Node class. + * + * @see Node + * @author Norris Boyd + */ +class ShallowNodeIterator implements Enumeration { + + public ShallowNodeIterator(Node n) { + current = n; + } + + public boolean hasMoreElements() { + return current != null; + } + + public Object nextElement() { + return nextNode(); + } + + public Node nextNode() { + Node result = current; + current = current.next; + return result; + } + + private Node current; +} + diff --git a/mozilla/js/js2/java/StackValue.java b/mozilla/js/js2/java/StackValue.java new file mode 100644 index 00000000000..ca84ae8cce7 --- /dev/null +++ b/mozilla/js/js2/java/StackValue.java @@ -0,0 +1,11 @@ +class StackValue { + + StackValue(double d) + { + dbl = d; + } + + double dbl; + + +} \ No newline at end of file diff --git a/mozilla/js/js2/java/ThreadLinkException.java b/mozilla/js/js2/java/ThreadLinkException.java new file mode 100644 index 00000000000..f854577e98e --- /dev/null +++ b/mozilla/js/js2/java/ThreadLinkException.java @@ -0,0 +1,26 @@ +/* -*- Mode: java; tab-width: 8 -*- + * Copyright © 1998 Netscape Communications Corporation, + * All Rights Reserved. + */ + +// API class +/** + * Thrown if the thread association cannot be made. + * + * Thrown by Context.enter() if the context + * is already associated with a thread, or if the + * current thread is already associated with a context.

+ * + * Thrown by Context.exit() if the context is not + * associated with the current thread. + * + * @see com.netscape.javascript.Context#enter + * @see com.netscape.javascript.Context#exit + */ +public class ThreadLinkException extends Exception { + + public ThreadLinkException(String detail) { + super(detail); + } +} + diff --git a/mozilla/js/js2/java/TokenStream.java b/mozilla/js/js2/java/TokenStream.java new file mode 100644 index 00000000000..a8764526562 --- /dev/null +++ b/mozilla/js/js2/java/TokenStream.java @@ -0,0 +1,1299 @@ +/* -*- Mode: Java; tab-width: 8 -*- + * Copyright © 1997, 1998 Netscape Communications Corporation, + * All Rights Reserved. + */ + +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 com.netscape.javascript.Parser + * + * @author Mike McCabe + * @author Brendan Eich + */ + +public class TokenStream { + /* + * JSTokenStream flags, mirroring those in jsscan.h. These are used + * by the parser to change/check the state of the scanner. + */ + + public final static int + TSF_ERROR = 0x0001, // fatal error while scanning +// TSF_EOF = 0x0002, // hit end of file + TSF_NEWLINES = 0x0004, // tokenize newlines + TSF_FUNCTION = 0x0008, // scanning inside function body + TSF_RETURN_EXPR = 0x0010, // function has 'return expr;' + TSF_RETURN_VOID = 0x0020, // function has 'return;' +// TSF_INTERACTIVE = 0x0040, // interactive parsing mode +// TSF_COMMAND = 0x0080, // command parsing mode +// TSF_LOOKAHEAD = 0x0100, // looking ahead for a token + TSF_REGEXP = 0x0200; // looking for a regular expression + + /* + * 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, + OBJECT = 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, + JTHROW = 87, + // End of interpreter bytecodes + SEMI = 88, // semicolon + LB = 89, // left and right brackets + RB = 90, + LC = 91, // left and right curlies (braces) + RC = 92, + LP = 93, // left and right parentheses + RP = 94, + COMMA = 95, // comma operator + ASSIGN = 96, // assignment ops (= += -= etc.) + HOOK = 97, // conditional (?:) + COLON = 98, + OR = 99, // logical or (||) + AND = 100, // logical and (&&) + EQOP = 101, // equality ops (== !=) + RELOP = 102, // relational ops (< <= > >=) + SHOP = 103, // shift ops (<< >> >>>) + UNARYOP = 104, // unary prefix operator + INC = 105, // increment/decrement (++ --) + DEC = 106, + DOT = 107, // member operator (.) + PRIMARY = 108, // true, false, null, this, super + FUNCTION = 109, // function keyword + EXPORT = 110, // export keyword + IMPORT = 111, // import keyword + IF = 112, // if keyword + ELSE = 113, // else keyword + SWITCH = 114, // switch keyword + CASE = 115, // case keyword + DEFAULT = 116, // default keyword + WHILE = 117, // while keyword + DO = 118, // do keyword + FOR = 119, // for keyword + BREAK = 120, // break keyword + CONTINUE = 121, // continue keyword + VAR = 122, // var keyword + WITH = 123, // with keyword + CATCH = 124, // catch keyword + FINALLY = 125, // finally keyword + RESERVED = 126, // 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 = 127, // NOP + NOT = 128, // etc. + PRE = 129, // for INC, DEC nodes. + POST = 130, + + /** + * For JSOPs associated with keywords... + * eg. op = THIS; token = PRIMARY + */ + + VOID = 131, + + /* types used for the parse tree - these never get returned + * by the scanner. + */ + BLOCK = 132, // statement block + ARRAYLIT = 133, // array literal + OBJLIT = 134, // object literal + LABEL = 135, // label + TARGET = 136, + LOOP = 137, + ENUMDONE = 138, + EXPRSTMT = 139, + PARENT = 140, + CONVERT = 141, + JSR = 142, + NEWLOCAL = 143, + USELOCAL = 144, + SCRIPT = 145; // top-level node for entire script + // end enum + + + /* for mapping int token types to printable strings. + * make sure to add 1 to index before using these! + */ + private static String names[]; + private static void checkNames() { + if (Context.printTrees && names == null) { + String[] a = { + "error", + "eof", + "eol", + "popv", + "enterwith", + "leavewith", + "return", + "goto", + "ifeq", + "ifne", + "dup", + "setname", + "bitor", + "bitxor", + "bitand", + "eq", + "ne", + "lt", + "le", + "gt", + "ge", + "lsh", + "rsh", + "ursh", + "add", + "sub", + "mul", + "div", + "mod", + "bitnot", + "neg", + "new", + "delprop", + "typeof", + "nameinc", + "propinc", + "eleminc", + "namedec", + "propdec", + "elemdec", + "getprop", + "setprop", + "getelem", + "setelem", + "call", + "name", + "number", + "string", + "zero", + "one", + "null", + "this", + "false", + "true", + "sheq", + "shne", + "closure", + "object", + "pop", + "pos", + "varinc", + "vardec", + "bindname", + "throw", + "in", + "instanceof", + "gosub", + "retsub", + "callspecial", + "getthis", + "newtemp", + "usetemp", + "getbase", + "getvar", + "setvar", + "undefined", + "try", + "endtry", + "newscope", + "typeofname", + "enuminit", + "enumnext", + "getproto", + "getparent", + "setproto", + "setparent", + "scope", + "getscopeparent", + "jthrow", + "semi", + "lb", + "rb", + "lc", + "rc", + "lp", + "rp", + "comma", + "assign", + "hook", + "colon", + "or", + "and", + "eqop", + "relop", + "shop", + "unaryop", + "inc", + "dec", + "dot", + "primary", + "function", + "export", + "import", + "if", + "else", + "switch", + "case", + "default", + "while", + "do", + "for", + "break", + "continue", + "var", + "with", + "catch", + "finally", + "reserved", + "nop", + "not", + "pre", + "post", + "void", + "block", + "arraylit", + "objlit", + "label", + "target", + "loop", + "enumdone", + "exprstmt", + "parent", + "convert", + "jsr", + "newlocal", + "uselocal", + "script" + }; + names = a; + } + } + + /* 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) { + checkNames(); + if (token + 1 >= names.length) + return null; + + if (token == UNARYOP || + token == ASSIGN || + token == PRIMARY || + token == EQOP || + token == SHOP || + token == RELOP) { + return names[token + 1] + " " + names[this.op + 1]; + } + + if (token == STRING || token == OBJECT || token == NAME) + return names[token + 1] + " `" + this.string + "'"; + + if (token == NUMBER) + return "NUMBER " + this.number; + + return names[token + 1]; + } + return ""; + } + + public static String tokenToName(int type) { + checkNames(); + return names == null ? "" : names[type + 1]; + } + + private static java.util.Hashtable keywords; + + static { + String[] strings = { + "break", + "case", + "continue", + "default", + "delete", + "do", + "else", + "export", + "false", + "for", + "function", + "if", + "in", + "new", + "null", + "return", + "switch", + "this", + "true", + "typeof", + "var", + "void", + "while", + "with", + + // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c + "abstract", + "boolean", + "byte", + "catch", + "char", + "class", + "const", + "debugger", + "double", + "enum", + "extends", + "final", + "finally", + "float", + "goto", + "implements", + "import", + "instanceof", + "int", + "interface", + "long", + "native", + "package", + "private", + "protected", + "public", + "short", + "static", + "super", + "synchronized", + "throw", + "throws", + "transient", + "try", + "volatile" + }; + int[] values = { + BREAK, // break + CASE, // case + CONTINUE, // continue + DEFAULT, // default + DELPROP, // delete + DO, // do + ELSE, // else + EXPORT, // export + PRIMARY | (FALSE << 8), // false + FOR, // for + FUNCTION, // function + IF, // if + RELOP | (IN << 8), // in + NEW, // new + PRIMARY | (NULL << 8), // null + RETURN, // return + SWITCH, // switch + PRIMARY | (THIS << 8), // this + PRIMARY | (TRUE << 8), // true + UNARYOP | (TYPEOF << 8), // typeof + VAR, // var + UNARYOP | (VOID << 8), // void + WHILE, // while + WITH, // with + RESERVED, // abstract + RESERVED, // boolean + RESERVED, // byte + CATCH, // catch + RESERVED, // char + RESERVED, // class + RESERVED, // const + RESERVED, // debugger + RESERVED, // double + RESERVED, // enum + RESERVED, // extends + RESERVED, // final + FINALLY, // finally + RESERVED, // float + RESERVED, // goto + RESERVED, // implements + IMPORT, // import + RELOP | (INSTANCEOF << 8), // instanceof + RESERVED, // int + RESERVED, // interface + RESERVED, // long + RESERVED, // native + RESERVED, // package + RESERVED, // private + RESERVED, // protected + RESERVED, // public + RESERVED, // short + RESERVED, // static + PRIMARY | (NOP << 8), // super + RESERVED, // synchronized + THROW, // throw + RESERVED, // throws + RESERVED, // transient + TRY, // try + RESERVED // volatile + }; + keywords = new java.util.Hashtable(strings.length); + Integer res = new Integer(RESERVED); + for (int i=0; i < strings.length; i++) + keywords.put(strings[i], values[i] == RESERVED + ? res + : new Integer(values[i])); + } + + private int stringToKeyword(String name) { + Integer result = (Integer) keywords.get(name); + if (result == null) + return EOF; + int x = result.intValue(); + this.op = x >> 8; + return x & 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) { + Object[] errArgs = { tokenToString(tt), + tokenToString(this.pushbackToken) }; + String message = Context.getMessage("msg.token.replaces.pushback", + errArgs); + 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; + } + + /* helper functions... these might be better inlined. + * These are needed because the java.lang.Character.isWhatever + * functions accept unicode, and we want to limit + * identifiers to ASCII. + */ + protected static boolean isJSIdentifier(String s) { + int length = s.length(); + + if (length == 0 || !isJSIdentifierStart(s.charAt(0))) + return false; + + for (int i=1; i= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '_' + || c == '$'); + } + + protected static boolean isJSIdentifierPart(int c) { + return ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '_' + || c == '$'); + } + + private static boolean isAlpha(int c) { + return ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z')); + } + + static boolean isDigit(int c) { + return (c >= '0' && c <= '9'); + } + + static boolean isXDigit(int c) { + return ((c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')); + } + + /* 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. + */ + static boolean isJSSpace(int c) { + return (c == '\u0009' || c == '\u000B' + || c == '\u000C' || c == '\u0020'); + } + + public int getToken() throws IOException { + int c; + tokenno++; + // If there was a fatal error, keep returning TOK_ERROR. + if ((flags & TSF_ERROR) != 0) + return ERROR; + + // 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') + if ((flags & TSF_NEWLINES) != 0) + break; + } while (isJSSpace(c) || c == '\n'); + + if (c == EOF_CHAR) + return EOF; + + // identifier/keyword/instanceof? + if (isJSIdentifierStart(c)) { + in.startString(); + do { + c = in.read(); + } while (isJSIdentifierPart(c)); + in.unread(); + + int result; + + String str = in.getString(); + // 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 + if ((result = stringToKeyword(str)) != EOF) { + return result; + } + + this.string = str; + return NAME; + } + + // is it a number? + if (isDigit(c) || (c == '.' && isDigit(in.peek()))) { + int base = 10; + in.startString(); + + double dval = ScriptRuntime.NaN; + long longval = 0; + boolean isInteger = true; + + if (c == '0') { + c = in.read(); + if (c == 'x' || c == 'X') { + c = in.read(); + base = 16; + // restart the string, losing leading 0x + in.startString(); + } else if (isDigit(c)) { + if (c < '8') { + base = 8; + // Restart the string, losing the leading 0 + in.startString(); + } else { + /* Checking against c < '8' is non-ECMA, but + * is required to support legacy code; we've + * supported it in the past, and it's likely + * that "08" and "09" are in use in code + * having to do with dates. So we need to + * support it, which makes our behavior a + * superset of ECMA in this area. We raise a + * warning if a non-octal digit is + * encountered, then proceed as if it were + * decimal. + */ + Object[] errArgs = { String.valueOf((char)c) }; + Context.reportWarning + (Context.getMessage("msg.bad.octal.literal", + errArgs), + getSourceName(), in.getLineno(), + getLine(), getOffset()); + // implicitly retain the leading 0 + } + } else { + // implicitly retain the leading 0 + } + } + + while (isXDigit(c)) { + if (base < 16 && (isAlpha(c) + || (base == 8 && c >= '8'))) { + break; + } + c = in.read(); + } + + if (base == 10 && (c == '.' || c == 'e' || c == 'E')) { + isInteger = false; + if (c == '.') { + do { + c = in.read(); + } while (isDigit(c)); + } + + if (c == 'e' || c == 'E') { + c = in.read(); + if (c == '+' || c == '-') { + c = in.read(); + } + if (!isDigit(c)) { + in.getString(); // throw away string in progress + reportError("msg.missing.exponent", null); + return ERROR; + } + do { + c = in.read(); + } while (isDigit(c)); + } + } + in.unread(); + String numString = in.getString(); + + if (base == 10 && !isInteger) { + try { + // Use Java conversion to number from string... + dval = (Double.valueOf(numString)).doubleValue(); + } + catch (NumberFormatException ex) { + Object[] errArgs = { ex.getMessage() }; + reportError("msg.caught.nfe", errArgs); + return ERROR; + } + } else { + dval = ScriptRuntime.stringToNumber(numString, 0, base); + longval = (long) dval; + + // is it an integral fits-in-a-long value? + if (longval != dval) + isInteger = false; + } + + if (!isInteger) { + /* Can't handle floats right now, because postfix INC/DEC + generate Doubles, but I would generate a Float through this + path, and it causes a stack mismatch. FIXME (MS) + if (Float.MIN_VALUE <= dval && dval <= Float.MAX_VALUE) + this.number = new Xloat((float) dval); + else + */ + this.number = new Double(dval); + } else { + // We generate the smallest possible type here + if (Byte.MIN_VALUE <= longval && longval <= Byte.MAX_VALUE) + this.number = new Byte((byte)longval); + else if (Short.MIN_VALUE <= longval && + longval <= Short.MAX_VALUE) + this.number = new Short((short)longval); + else if (Integer.MIN_VALUE <= longval && + longval <= Integer.MAX_VALUE) + this.number = new Integer((int)longval); + else { + // May lose some precision here, but that's the + // appropriate semantics. + this.number = new Double(longval); + } + } + 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. + + StringBuffer stringBuf = null; + + int quoteChar = c; + int val = 0; + + c = in.read(); + in.startString(); // start after the first " + while(c != quoteChar) { + if (c == '\n' || c == EOF_CHAR) { + in.unread(); + in.getString(); // throw away the string in progress + reportError("msg.unterminated.string.lit", null); + return ERROR; + } + + if (c == '\\') { + // We've hit an escaped character; revert to the + // slow method of building a string. + if (stringBuf == null) { + // Don't include the backslash + in.unread(); + stringBuf = new StringBuffer(in.getString()); + in.read(); + } + + switch (c = in.read()) { + 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; + case 'v': c = '\u000B'; break; + // \v a late addition to the ECMA spec. + // '\v' doesn't seem to be valid Java. + + default: + if (isDigit(c) && c < '8') { + val = c - '0'; + c = in.read(); + if (isDigit(c) && c < '8') { + val = 8 * val + c - '0'; + c = in.read(); + if (isDigit(c) && c < '8') { + val = 8 * val + c - '0'; + c = in.read(); + } + } + in.unread(); + if (val > 0377) { + reportError("msg.oct.esc.too.large", null); + return ERROR; + } + c = val; + } else if (c == 'u') { + /* + * Get 4 hex digits; if the u escape is not + * followed by 4 hex digits, use 'u' + the literal + * character sequence that follows. Do some manual + * match (OK because we're in a string) to avoid + * multi-char match on the underlying stream. + */ + int c1, c2, c3, c4; + + c1 = in.read(); + if (!isXDigit(c1)) { + in.unread(); + c = 'u'; + } else { + val = Character.digit((char) c1, 16); + c2 = in.read(); + if (!isXDigit(c2)) { + in.unread(); + stringBuf.append('u'); + c = c1; + } else { + val = 16 * val + + Character.digit((char) c2, 16); + c3 = in.read(); + if (!isXDigit(c3)) { + in.unread(); + stringBuf.append('u'); + stringBuf.append((char)c1); + c = c2; + } else { + val = 16 * val + + Character.digit((char) c3, 16); + c4 = in.read(); + if (!isXDigit(c4)) { + in.unread(); + stringBuf.append('u'); + stringBuf.append((char)c1); + stringBuf.append((char)c2); + c = c3; + } else { + // got 4 hex digits! Woo Hoo! + val = 16 * val + + Character.digit((char) c4, 16); + c = val; + } + } + } + } + } else if (c == 'x') { + /* Get 2 hex digits, defaulting to 'x' + literal + * sequence, as above. + */ + int c1, c2; + + c1 = in.read(); + if (!isXDigit(c1)) { + in.unread(); + c = 'x'; + } else { + val = Character.digit((char) c1, 16); + c2 = in.read(); + if (!isXDigit(c2)) { + in.unread(); + stringBuf.append('x'); + c = c1; + } else { + // got 2 hex digits + val = 16 * val + + Character.digit((char) c2, 16); + c = val; + } + } + } + } + } + + if (stringBuf != null) + stringBuf.append((char) c); + c = in.read(); + } + + if (stringBuf != null) + this.string = stringBuf.toString(); + else { + in.unread(); // miss the trailing " + this.string = in.getString(); + in.read(); + } + 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 RP; + 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('-')) { + while ((c = in.read()) != EOF_CHAR && c != '\n') + /* skip to end of line */; + in.unread(); + return getToken(); // 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('/')) { + while ((c = in.read()) != EOF_CHAR && c != '\n') + /* skip to end of line */; + in.unread(); + return getToken(); + } + if (in.match('*')) { + while ((c = in.read()) != -1 + && !(c == '*' && in.match('/'))) { + if (c == '\n') { + } else if (c == '/' && in.match('*')) { + if (in.match('/')) + return getToken(); + reportError("msg.nested.comment", null); + return ERROR; + } + } + if (c == EOF_CHAR) { + reportError("msg.unterminated.comment", null); + return ERROR; + } + return getToken(); // `goto retry' + } + + // is it a regexp? + if ((flags & TSF_REGEXP) != 0) { + // We don't try to use the in.startString/in.getString + // approach, because escaped characters (which break it) + // seem likely to be common. + StringBuffer re = new StringBuffer(); + while ((c = in.read()) != '/') { + if (c == '\n' || c == EOF_CHAR) { + in.unread(); + reportError("msg.unterminated.re.lit", null); + return ERROR; + } + if (c == '\\') { + re.append((char) c); + c = in.read(); + } + + re.append((char) c); + } + + StringBuffer flagsBuf = new StringBuffer(); + while (true) { + if (in.match('g')) + flagsBuf.append('g'); + else if (in.match('i')) + flagsBuf.append('i'); + else if (in.match('m')) + flagsBuf.append('m'); + else + break; + } + + if (isAlpha(in.peek())) { + reportError("msg.invalid.re.flag", null); + return ERROR; + } + + this.string = re.toString(); + this.regExpFlags = flagsBuf.toString(); + return OBJECT; + } + + + 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 '+': + case '-': + if (in.match('=')) { + if (c == '+') { + this.op = ADD; + return ASSIGN; + } else { + this.op = SUB; + return ASSIGN; + } + } else if (in.match((char) c)) { + if (c == '+') { + return INC; + } else { + return DEC; + } + } else if (c == '-') { + return SUB; + } else { + return ADD; + } + + default: + reportError("msg.illegal.character", null); + return ERROR; + } + } + + private void reportError(String messageProperty, Object[] args) { + flags |= TSF_ERROR; + String message = Context.getMessage(messageProperty, args); + Context.reportError(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 Number 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(); + */ + public int flags; + public String regExpFlags; + + private String sourceName; + private String line; + 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 Number number; +} diff --git a/mozilla/js/js2/java/WrappedException.java b/mozilla/js/js2/java/WrappedException.java new file mode 100644 index 00000000000..e3ea145dc71 --- /dev/null +++ b/mozilla/js/js2/java/WrappedException.java @@ -0,0 +1,56 @@ +/* -*- Mode: java; tab-width: 8 -*- + * Copyright © 1997, 1998 Netscape Communications Corporation, + * All Rights Reserved. + */ + +/** + * A wrapper for runtime exceptions. + * + * Used by the JavaScript runtime to wrap and propagate exceptions that occur + * during runtime. + * + * @author Norris Boyd + */ +public class WrappedException extends RuntimeException { + + /** + * Create a new exception wrapped around an existing exception. + * + * @param exception the exception to wrap + */ + public WrappedException(Throwable exception) { + super(exception.getMessage()); + this.exception = exception.fillInStackTrace(); + } + + /** + * Get the message for the exception. + * + * Delegates to the wrapped exception. + */ + public String getMessage() { + return "WrappedException of " + exception.toString(); + } + + /** + * Get the wrapped exception. + * + * @return the exception that was presented as a argument to the + * constructor when this object was created + */ + public Throwable getWrappedException() { + return exception; + } + + /** + * Get the wrapped exception. + * + * @return the exception that was presented as a argument to the + * constructor when this object was created + */ + public Object unwrap() { + return exception; + } + + private Throwable exception; +}