Removing copies of Rhino code
git-svn-id: svn://10.0.0.236/trunk@29406 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
aed1ccc028
commit
dda3ccddad
@ -1,43 +0,0 @@
|
||||
/* -*- 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;
|
||||
}
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
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);
|
||||
};
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
/* -*- 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,994 +0,0 @@
|
||||
/* -*- 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
|
||||
* <BR>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
|
||||
* <BR> 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;
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
while (!theStack.isEmpty()) {
|
||||
System.out.println("Stack Slot contains " + theStack.pop());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
class JSStack {
|
||||
|
||||
Stack stack = new Stack();
|
||||
int frameTop;
|
||||
|
||||
void newFrame(StackValue returnAddress)
|
||||
{
|
||||
stack.push(returnAddress);
|
||||
stack.push(new StackValue(frameTop));
|
||||
frameTop = stack.size();
|
||||
}
|
||||
|
||||
StackValue popFrame()
|
||||
{
|
||||
stack.setSize(frameTop);
|
||||
StackValue oldFrame = (StackValue)(stack.pop());
|
||||
frameTop = oldFrame.i;
|
||||
return (StackValue)stack.pop();
|
||||
}
|
||||
|
||||
void push(StackValue v)
|
||||
{
|
||||
stack.push(v);
|
||||
}
|
||||
|
||||
boolean isEmpty()
|
||||
{
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
StackValue pop()
|
||||
{
|
||||
return (StackValue)stack.pop();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,333 +0,0 @@
|
||||
/* -*- 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;
|
||||
}
|
||||
|
||||
|
||||
@ -1,524 +0,0 @@
|
||||
/* -*- 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 findTryNode(JSStack theStack, PostorderNodeIterator ni)
|
||||
{
|
||||
while (true) {
|
||||
Node p = ni.peekParent();
|
||||
if (p == null) {
|
||||
StackValue ret = theStack.popFrame();
|
||||
ni = ret.p;
|
||||
if (ni == null) return null;
|
||||
}
|
||||
else {
|
||||
if (p.type == TokenStream.TRY)
|
||||
return ni;
|
||||
ni.pop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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());
|
||||
theStack.push(new StackValue(num.doubleValue()));
|
||||
return ni;
|
||||
|
||||
case TokenStream.SETNAME :
|
||||
rhs = theStack.pop(); // the new value
|
||||
lhs = theStack.pop(); // the target lvalue
|
||||
System.out.println("setname " + lhs.s + " to " + rhs.d);
|
||||
return ni;
|
||||
|
||||
case TokenStream.BINDNAME :
|
||||
System.out.println("bindname " + getString());
|
||||
theStack.push(new StackValue(getString()));
|
||||
return ni;
|
||||
|
||||
case TokenStream.NAME :
|
||||
System.out.println("name " + getString());
|
||||
theStack.push(new StackValue(22.0));
|
||||
return ni;
|
||||
|
||||
case TokenStream.CALL :
|
||||
System.out.println("call");
|
||||
lhs = theStack.pop(); // the function object
|
||||
/*
|
||||
here we look up the first arg, find that it's a known function
|
||||
and return the nodeIterator for it's tree. Also, begin execution
|
||||
for that function by constructing a frame on the stack.
|
||||
*/
|
||||
theStack.newFrame(new StackValue(ni));
|
||||
ni = new PostorderNodeIterator(Brenda.tf);
|
||||
return ni;
|
||||
|
||||
case TokenStream.RETURN :
|
||||
lhs = theStack.pop(); // the return value
|
||||
rhs = theStack.pop(); // the return address
|
||||
ni = rhs.p;
|
||||
theStack.push(lhs);
|
||||
return ni;
|
||||
|
||||
case TokenStream.ADD :
|
||||
rhs = theStack.pop();
|
||||
lhs = theStack.pop();
|
||||
theStack.push(new StackValue(lhs.d + rhs.d));
|
||||
return ni;
|
||||
|
||||
case TokenStream.THROW :
|
||||
lhs = theStack.pop(); // the exception object
|
||||
ni = findTryNode(theStack, ni);
|
||||
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
|
||||
}
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
|
||||
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 peekParent()
|
||||
{
|
||||
if (stack.isEmpty())
|
||||
return null;
|
||||
else
|
||||
return (Node)stack.peek();
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
if (stack.isEmpty())
|
||||
current = null;
|
||||
else
|
||||
current = (Node)stack.pop();
|
||||
}
|
||||
|
||||
Node nextNode()
|
||||
{
|
||||
if (current == null)
|
||||
return current = start;
|
||||
|
||||
if (stack.isEmpty())
|
||||
return current = 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;
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
/* -*- 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;
|
||||
}
|
||||
@ -1,128 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/* -*- 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;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,56 +0,0 @@
|
||||
/* -*- 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user