// EcmaScriptEvaluateVisitor.java // FESI Copyright (c) Jean-Marc Lugrin, 1999 // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package FESI.Interpreter; import FESI.Parser.*; import FESI.AST.*; import FESI.Data.*; import FESI.Exceptions.*; import java.util.Enumeration; /** * Exception used to package any exception encountered during visiting * to an exception accepted by the visitor interface as defined by * JavaCC. Eventually the exception will be unpackaged and reraised. */ class PackagedException extends RuntimeException { EcmaScriptException exception; SimpleNode node; PackagedException (EcmaScriptException exception, SimpleNode node) { super(); this.exception = exception; this.node = node; } public String getMessage() { return exception.getMessage(); } } /** * The evaluate visitor use the visitor pattern to evaluate the * parsed code. Use for both main program and functions (using * different entries). A new evaluator is used everytime as it * contains the return code (because we can return only one variable). *

The parse tree must have been preprocessed by the variable and * function visitors first. */ public class EcmaScriptEvaluateVisitor implements EcmaScriptVisitor, EcmaScriptConstants { // Continuation codes public static final int C_NORMAL = 0; public static final int C_RETURN = 1; public static final int C_BREAK = 2; public static final int C_CONTINUE = 3; // Result of comparison private static final int COMPARE_TRUE = -1; private static final int COMPARE_UNDEFINED = 0; private static final int COMPARE_FALSE = 1; private boolean debug = false; // Indicator for the data to be returned by the accept private static final Object FOR_VALUE = new Object(); private static final Object FOR_REFERENCE = new Object(); // The visitor work on behalf on an evaluator which provide // the context information (as global variables) private Evaluator evaluator; // This is the final completion code - mark unused to protect against recursive use private int completionCode = -1; /** * Create a new visitor * @param evaluator On behalf of this evaluator */ public EcmaScriptEvaluateVisitor(Evaluator evaluator) { super(); this.evaluator = evaluator; } /** * Return the completion code of this evaluation * @return the last completion code as an int */ public int getCompletionCode() { return completionCode; } /** * Return the completion code of this evaluation * @return the last completion code as a String */ public String getCompletionCodeString() { final String[] data = {"normal", "return", "break", "continue"}; return data[completionCode]; } /** * Evaluate a tree which represents a main program or the source * of an eval function. * @param node The parsed tree (annotated for variables) * @param es A description of the source for error messages * @return The result of the evaluation * @exception EcmaScriptException In case of any error during the evaluation */ public ESValue evaluateProgram(ASTProgram node, EvaluationSource es) throws EcmaScriptException { EvaluationSource evaluationSource = es; if (completionCode != -1) { throw new ProgrammingError("Multiple use of evalution visitor"); } completionCode = C_NORMAL; ESValue result = null; if (debug) System.out.println("evaluateProgram for: " + node); try { result = (ESValue) node.jjtAccept(this, FOR_VALUE); } catch (PackagedException e) { e.exception.appendEvaluationSource(new LineEvaluationSource(e.node.getLineNumber(),es)); throw e.exception; } if (debug) System.out.println("evaluateProgram result: " + result); return result; } /** * Evaluate a tree which represents a function. The local variables * must have been established by the caller. * @param node The parsed tree (annotated for variables) * @param es A description of the source for error messages * @return The result of the evaluation * @exception EcmaScriptException In case of any error during the evaluation */ public ESValue evaluateFunction(ASTStatementList node, EvaluationSource es) throws EcmaScriptException { if (completionCode != -1) { throw new ProgrammingError("Multiple use of evaluation visitor"); } completionCode = C_NORMAL; EvaluationSource evaluationSource = es; ESValue result = null; if (debug) System.out.println("evaluateFunction for: " + node); // Thread.yields inserted by Hannes Wallnoefer, workaround for poor thread schedulers // Thread.yield (); if (evaluator.thread != Thread.currentThread()) throw new helma.framework.TimeoutException(); try { result = (ESValue) node.jjtAccept(this, FOR_VALUE); } catch (PackagedException e) { e.exception.appendEvaluationSource(new LineEvaluationSource(e.node.getLineNumber(),es)); throw e.exception; } if (debug) System.out.println("evaluateFunction result: " + result); return result; } /** * This is a subevaluator. It evaluates a tree which represents * a with statement. It is called indirectly via the * evaluator when a with statement is encountered in the tree. * @param node The parsed tree (annotated for variables) * @param es A description of the source for error messages * @return The result of the evaluation * @exception EcmaScriptException In case of any error during the evaluation */ public ESValue evaluateWith(ASTStatement node, EvaluationSource es) throws EcmaScriptException { if (completionCode != -1) { throw new ProgrammingError("Multiple use of evalution visitor"); } completionCode = C_NORMAL; ESValue result = null; if (debug) System.out.println("evaluateWith for: " + node); try { result = (ESValue) node.jjtAccept(this, FOR_VALUE); } catch (PackagedException e) { e.exception.appendEvaluationSource(new LineEvaluationSource(e.node.getLineNumber(),es)); throw e.exception; } if (debug) System.out.println("evaluateWith result: " + result); return result; } /*-------------------------------------------------------------------- * The following routines implement the interpretation process * For detail see the EcmaScript standard to which they refer *------------------------------------------------------------------*/ // EcmaScript standard 11.8.5 private int compare(ESValue v1, ESValue v2) throws EcmaScriptException { ESValue v1p = v1.toESPrimitive(ESValue.EStypeNumber); ESValue v2p = v2.toESPrimitive(ESValue.EStypeNumber); // System.out.println("v1p = " + v1 + " v2p = " + v2); if ((v1p instanceof ESString) && (v2p instanceof ESString)) { // Note: Convert v1/2 instead of v1/2p for correct // behaviour of "" + Object; String s1 =v1.toString(); String s2 =v2.toString(); int c = s1.compareTo(s2); //System.out.println("CS: '"+ s1 + "' " +c+ " '" + s2 + "'"); return (c < 0) ? COMPARE_TRUE : COMPARE_FALSE; } else { double d1 = v1.doubleValue(); double d2 = v2.doubleValue(); if (Double.isNaN(d1) || Double.isNaN(d2)) return COMPARE_UNDEFINED; int c = (v1.doubleValue()1 child"); } return result; } public Object visit(ASTVariableDeclaration node, Object data) { int nChildren = node.jjtGetNumChildren(); if (nChildren<1 || nChildren>2) { throw new ProgrammingError("Bad AST in variable declaration"); } Object result = null; if (nChildren == 2) { try { Object lvo = node.jjtGetChild(0).jjtAccept(this,FOR_REFERENCE); ESReference lv; if (lvo instanceof ESReference) { lv = (ESReference) lvo; } else { // Should not happen as first node should be an identifier throw new ProgrammingError("Value '"+lvo.toString()+"' is not a variable"); } ESValue rv = (ESValue) node.jjtGetChild(1).jjtAccept(this,FOR_VALUE); lv.putValue(null, rv); // null because the variable should be undefined! result = rv; } catch (EcmaScriptException e) { throw new PackagedException(e,node); } } return result; } public Object visit(ASTIfStatement node, Object data) { Object result = null; int nChildren = node.jjtGetNumChildren(); if (nChildren<2 || nChildren>3) { throw new ProgrammingError("Bad AST in IF statement"); } try { ESValue testValue = (ESValue) node.jjtGetChild(0).jjtAccept(this, FOR_VALUE); boolean test = testValue.booleanValue(); if (test) { result = node.jjtGetChild(1).jjtAccept(this,FOR_VALUE); } else { if (nChildren==3) { result = node.jjtGetChild(2).jjtAccept(this,FOR_VALUE); } } } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } public Object visit(ASTWhileStatement node, Object data) { Object result = null; node.assertTwoChildren(); try { ESValue testValue = (ESValue) node.jjtGetChild(0).jjtAccept(this,FOR_VALUE); while (testValue.booleanValue()) { // Thread.yield (); if (evaluator.thread != Thread.currentThread()) throw new helma.framework.TimeoutException(); result = node.jjtGetChild(1).jjtAccept(this, FOR_VALUE); if (completionCode == C_RETURN) { return result; } else if (completionCode == C_BREAK) { completionCode = C_NORMAL; return result; } else if (completionCode == C_CONTINUE) { testValue = (ESValue) node.jjtGetChild(0).jjtAccept(this,FOR_VALUE); completionCode = C_NORMAL; } else { testValue = (ESValue) node.jjtGetChild(0).jjtAccept(this,FOR_VALUE); } } } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } public Object visit(ASTForStatement node, Object data) { Object result = null; try { node.assertFourChildren(); // Evaluate first expression if present node.jjtGetChild(0).jjtAccept(this,FOR_VALUE); Node testNode = node.jjtGetChild(1); ESValue testValue; if (testNode instanceof ASTEmptyExpression) { testValue = ESBoolean.makeBoolean(true); } else { testValue = (ESValue) testNode.jjtAccept(this,FOR_VALUE); } while (testValue.booleanValue()) { // Thread.yield (); if (evaluator.thread != Thread.currentThread()) throw new helma.framework.TimeoutException(); result = node.jjtGetChild(3).jjtAccept(this, FOR_VALUE); if (completionCode == C_RETURN) { return result; } else if (completionCode == C_BREAK) { completionCode = C_NORMAL; return result; } else if (completionCode == C_CONTINUE) { node.jjtGetChild(2).jjtAccept(this,FOR_VALUE); if (testNode instanceof ASTEmptyExpression) { testValue = ESBoolean.makeBoolean(true); } else { testValue = (ESValue) testNode.jjtAccept(this,FOR_VALUE); } completionCode = C_NORMAL; } else { node.jjtGetChild(2).jjtAccept(this,FOR_VALUE); if (testNode instanceof ASTEmptyExpression) { testValue = ESBoolean.makeBoolean(true); } else { testValue = (ESValue) testNode.jjtAccept(this,FOR_VALUE); } } } } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } // Assume that in 12.6.2, for var, step 7, should be goto 17 public Object visit(ASTForVarStatement node, Object data) { Object result = null; // No value by default try { node.assertFourChildren(); node.jjtGetChild(0).jjtAccept(this,FOR_VALUE); Node testNode = node.jjtGetChild(1); ESValue testValue; if (testNode instanceof ASTEmptyExpression) { testValue = ESBoolean.makeBoolean(true); } else { testValue = (ESValue) testNode.jjtAccept(this,FOR_VALUE); } while (testValue.booleanValue()) { result = node.jjtGetChild(3).jjtAccept(this,FOR_VALUE); if (completionCode == C_RETURN) { return result; } else if (completionCode == C_BREAK) { completionCode = C_NORMAL; return result; } else if (completionCode == C_CONTINUE) { node.jjtGetChild(2).jjtAccept(this,FOR_VALUE); if (testNode instanceof ASTEmptyExpression) { testValue = ESBoolean.makeBoolean(true); } else { testValue = (ESValue) testNode.jjtAccept(this,FOR_VALUE); } completionCode = C_NORMAL; } else { node.jjtGetChild(2).jjtAccept(this,FOR_VALUE); if (testNode instanceof ASTEmptyExpression) { testValue = ESBoolean.makeBoolean(true); } else { testValue = (ESValue) testNode.jjtAccept(this,FOR_VALUE); } } // Thread.yield (); if (evaluator.thread != Thread.currentThread()) throw new helma.framework.TimeoutException(); } } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } public Object visit(ASTForInStatement node, Object data) { Object result = null; // No value by default node.assertThreeChildren(); try { ESValue ob = (ESValue) node.jjtGetChild(1).jjtAccept(this,FOR_VALUE); ESObject obj = (ESObject) ob.toESObject(evaluator); boolean directEnumeration = obj.isDirectEnumerator(); for (Enumeration e = obj.getProperties() ; e.hasMoreElements() ;) { Object en = e.nextElement(); ESValue s; if (directEnumeration) { s = ESLoader.normalizeValue(en, evaluator); } else { s = new ESString((String) (en.toString())); } Object lvo = node.jjtGetChild(0).jjtAccept(this,FOR_REFERENCE); ESReference lv; if (lvo instanceof ESReference) { lv = (ESReference) lvo; } else { throw new EcmaScriptException("Value '"+lvo.toString()+"' is not an assignable object or property"); } evaluator.putValue(lv, s); result = node.jjtGetChild(2).jjtAccept(this,FOR_VALUE); if (completionCode == C_RETURN) { break; } else if (completionCode == C_BREAK) { completionCode = C_NORMAL; break; } else if (completionCode == C_CONTINUE) { completionCode = C_NORMAL; continue; } } } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } public Object visit(ASTForVarInStatement node, Object data) { Object result = null; // No value by default node.assertFourChildren(); try { Object lvo = node.jjtGetChild(0).jjtAccept(this,FOR_REFERENCE); ESReference lv; if (lvo instanceof ESReference) { lv = (ESReference) lvo; } else { // Should not happen as it should be an identifier throw new ProgrammingError("Value '"+lvo.toString()+"' is not a variable"); } ESValue init = (ESValue) node.jjtGetChild(1).jjtAccept(this, FOR_VALUE); evaluator.putValue(lv, init); ESValue ob = (ESValue) node.jjtGetChild(2).jjtAccept(this,FOR_VALUE); ESObject obj = (ESObject) ob.toESObject(evaluator); boolean directEnumeration = obj.isDirectEnumerator(); for (Enumeration e = obj.getProperties() ; e.hasMoreElements() ;) { Object en = e.nextElement(); ESValue s; if (directEnumeration) { s = ESLoader.normalizeValue(en, evaluator); } else { s = new ESString((String) (en.toString())); } // Typing already checked above - will generate an error anyhow lv = (ESReference) node.jjtGetChild(0).jjtAccept(this,FOR_REFERENCE); evaluator.putValue(lv, s); result = node.jjtGetChild(3).jjtAccept(this,FOR_VALUE); if (completionCode == C_RETURN) { break; } else if (completionCode == C_BREAK) { completionCode = C_NORMAL; break; } else if (completionCode == C_CONTINUE) { completionCode = C_NORMAL; continue; } } } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } public Object visit(ASTContinueStatement node, Object data) { node.assertNoChildren(); completionCode = C_CONTINUE; return null; } public Object visit(ASTBreakStatement node, Object data) { node.assertNoChildren(); completionCode = C_BREAK; return null; } public Object visit(ASTReturnStatement node, Object data) { node.assertOneChild(); Object result = node.jjtGetChild(0).jjtAccept(this,FOR_VALUE); completionCode = C_RETURN; return result; } public Object visit(ASTWithStatement node, Object data) { node.assertTwoChildren(); ESValue result = null; try { EvaluationSource es = (EvaluationSource) node.getEvaluationSource(); ESValue scopeValue = (ESValue) node.jjtGetChild(0).jjtAccept(this,FOR_VALUE); ASTStatement statementNode = (ASTStatement) (node.jjtGetChild(1)); ESObject scopeObject = (ESObject) scopeValue.toESObject(evaluator); result = evaluator.evaluateWith(statementNode, scopeObject, es); } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } public Object visit(ASTThisReference node, Object data) { node.assertNoChildren(); return evaluator.getThisObject(); } /* * Attempt to minimize the creation of intermediate ESReferences. * This is prettry tricky. The trick is to keep the last result * as a delayed reference while looking ahead for parameter or * array index. A delayed reference is represented by a non null * currentProperty with an object (which is the last result * returned). If the last Result is null, this indicates access to * the global environment. */ public Object visit(ASTCompositeReference node, Object forWhat) { int nChildren = node.jjtGetNumChildren(); if (nChildren<2) throw new ProgrammingError("Bad ast"); try { // The base node is the first node in the serie. // If it is an identifier, it is a reference to a property // of the global object, so it can be a delayed reference to the // specified property of the global object. Otherwise it is // some kind of expression returning a value, and therefore cannot // be a delayed reference. ESValue lastResult; ESValue currentProperty ; { Node baseNode = node.jjtGetChild(0); if (baseNode instanceof ASTIdentifier) { lastResult = null; // Means lookup in global environment (with scope) String id = ((ASTIdentifier)baseNode).getName(); currentProperty = new ESString(id); } else { lastResult = (ESValue) baseNode.jjtAccept(this,FOR_VALUE); currentProperty = null; // No reference so far } } // Here the currentProperty and lastResult are initialized. //**** //System.out.println("--->ASTCompositeReference: for: " + // (forWhat==FOR_VALUE ? "VALUE" : "REF") + " lr="+lastResult+ // ", cp="+currentProperty + "<---"); for (int i = 1; i < nChildren; i++) { Node compositor = node.jjtGetChild(i); if ((compositor instanceof ASTPropertyValueReference) || (compositor instanceof ASTPropertyIdentifierReference)) { // Object property accessor, will build a new delayed reference. // First dereference any indirect reference left by a previous iteration if (currentProperty != null) { ESValue newBase; String propertyName = currentProperty.toString(); if (lastResult == null) { newBase = evaluator.getValue(propertyName,propertyName.hashCode()); //**** //System.out.println("--->NB = " + newBase + "<---"); if (newBase instanceof ESUndefined) { throw new EcmaScriptException("The property '"+propertyName+ "' is not defined in global object"); } } else { ESObject currentBase = (ESObject) lastResult.toESObject(evaluator); //**** //System.out.println("--->CB *** " + currentBase.getClass() + " " + propertyName + "<---"); newBase = currentBase.getProperty(propertyName, propertyName.hashCode()); if (newBase instanceof ESUndefined) { throw new EcmaScriptException("The property '"+propertyName+ "' is not defined in object '"+currentBase.toString()+"'"); } } lastResult = newBase; currentProperty = null; // Assure invariant at end of if } // Here the lastResult contains the result of the expression before the // current compositor, as a value. currentProperty is null as this is not a // delayed reference. // We get the current property name (in principle any expression, for example // in obj['base'+index.toString()]. currentProperty = (ESValue) compositor.jjtAccept(this,FOR_VALUE); //System.out.println("--->LR = " + lastResult + // " currentProperty = " + currentProperty.toString() + "<---"); // ********* } else if (compositor instanceof ASTFunctionCallParameters) { // We have parameters. The function object may be represented // by a Function value, or a delayed reference to a base object. // First get the arguments (evaluated) ESValue[] arguments = (ESValue []) compositor.jjtAccept(this, FOR_VALUE); // Find the 'this' for the function call. If it is a delayed // reference, the this object is represented by the last result (the // base of the reference). ESObject thisObject; //System.out.println("--->CP: " + currentProperty + "<---"); // ************* if (currentProperty != null) { // Delayed reference, find base object (global if base is null) //System.out.println("--->LR: " + lastResult + "<---"); // ************* if (lastResult == null) { thisObject = evaluator.getGlobalObject(); //System.out.println("--->GO: " + thisObject + "<---"); // ************* } else { thisObject = (ESObject) lastResult.toESObject(evaluator); // if function is called via an object } // Special case (see standard document) if (thisObject instanceof ESArguments) { thisObject = evaluator.getGlobalObject(); } } else { // Assume that global may never be an ESArgument (for performance) thisObject = evaluator.getGlobalObject(); // Global object by default } // The thisObject will be the target of the call. // If we have a delayed reference, we can do an indirect call, // leaving it up to the target object to find the routine to call. // This allow native objects to implement their own lookup without // requiring the creating of an intermediate Function object. //System.out.println("--->THIS: " + thisObject.toDetailString() + "<---"); // ************* if (currentProperty != null) { // Use lastResult and property name for indirect call String functionName = currentProperty.toString(); // System.out.println("--->FN: " + functionName + ", LR: " + lastResult + "<---"); // ************* if (lastResult == null) { lastResult=evaluator.doIndirectCall(thisObject, functionName, functionName.hashCode(), arguments); } else { try { lastResult = thisObject.doIndirectCall(evaluator, thisObject, functionName, arguments); } catch (NoSuchMethodException e) { throw new EcmaScriptException(e.getMessage()); } } currentProperty = null; } else { System.out.println("--->Last result: " + lastResult + " " + lastResult.getClass() + "<---"); // ******** // Via global or WITH, use current object ESValue theFunction = (ESObject) lastResult.toESObject(evaluator); // Conversion needed ? lastResult = theFunction.callFunction(thisObject,arguments); } completionCode = C_NORMAL; } else { throw new ProgrammingError("Bad AST"); } } // for // Either build reference or return object depending on type of request // Here, if propertyName is not null, then lastResult.propertyName contains // the value (delayed dereferencing). Otherwise the value is in // lastResult. Object result; // Will be the returned value if (forWhat == FOR_VALUE) { // We want a value //System.out.println("--->Build value cp: " + currentProperty + " lr: " + lastResult + "<---"); // ******** if (currentProperty != null) { // Must dereference value ESObject currentBase = (ESObject) lastResult.toESObject(evaluator); String propertyName = currentProperty.toString(); //System.out.println("--->getProperty in cb: " + currentBase + " pn: " + propertyName + "<---"); // ******* result = currentBase.getProperty(propertyName,propertyName.hashCode()); } else { // Last value is already the final value result = lastResult; } } else { // We want a reference - therefore it cannot be just a value, it // must be a delayed reference. if (currentProperty == null) { throw new EcmaScriptException("'"+lastResult.toString()+"' is not an assignable value"); } ESObject currentBase = (ESObject) lastResult.toESObject(evaluator); String propertyName = currentProperty.toString(); //System.out.println("--->Build ref cb: " + currentBase + " pn: " + propertyName + "<---"); // ******** result = new ESReference(currentBase, propertyName, propertyName.hashCode()); } return result; } catch (EcmaScriptException e) { throw new PackagedException(e,node); } } public Object visit(ASTFunctionCallParameters node, Object data) { int nChildren=node.jjtGetNumChildren(); ESValue[] arguments = new ESValue[nChildren]; for (int i=0; i>v2.toUInt32()); } break; case RUNSIGNEDSHIFT: { result = new ESNumber( v1.toUInt32()>>>v2.toUInt32()); } break; case LT: { int compareCode = compare(v1,v2); if (compareCode == COMPARE_TRUE) { result=ESBoolean.makeBoolean(true); } else { result=ESBoolean.makeBoolean(false); } } break; case GT: { int compareCode = compare(v2,v1); if (compareCode == COMPARE_TRUE) { result=ESBoolean.makeBoolean(true); } else { result=ESBoolean.makeBoolean(false); } } break; case LE: { int compareCode = compare(v2,v1); if (compareCode == COMPARE_FALSE) { result=ESBoolean.makeBoolean(true); } else { result=ESBoolean.makeBoolean(false); } } break; case GE: { int compareCode = compare(v1,v2); if (compareCode == COMPARE_FALSE) { result=ESBoolean.makeBoolean(true); } else { result=ESBoolean.makeBoolean(false); } } break; case EQ: { result=ESBoolean.makeBoolean(equal(v1,v2)); } break; case NE: { result=ESBoolean.makeBoolean(!equal(v1,v2)); } break; case BIT_AND: { int iv1 = v1.toInt32(); int iv2 = v2.toInt32(); result = new ESNumber(iv1 & iv2); } break; case BIT_OR: { int iv1 = v1.toInt32(); int iv2 = v2.toInt32(); result = new ESNumber(iv1 | iv2); } break; case XOR: { int iv1 = v1.toInt32(); int iv2 = v2.toInt32(); result = new ESNumber(iv1 ^ iv2); } break; default: throw new ProgrammingError("Unimplemented binary"); } // switch v1 = result; } // for } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } public Object visit(ASTAndExpressionSequence node, Object data) { ESValue result = null; int nChildren = node.jjtGetNumChildren(); try { result = (ESValue) node.jjtGetChild(0).jjtAccept(this,FOR_VALUE); int i = 1; while (result.booleanValue() && (i>v2.toUInt32()); } break; case RUNSIGNEDSHIFTASSIGN: { result = new ESNumber( v1.toUInt32()>>>v2.toUInt32()); } break; default: throw new ProgrammingError("Unimplemented assign operator"); } // switch evaluator.putValue(lv, result); v2 = result; } } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } public Object visit(ASTExpressionList node, Object data) { int n = node.jjtGetNumChildren(); Object result = null; if (n<=0) throw new ProgrammingError("Empty expression list"); result = node.jjtGetChild(0).jjtAccept(this, FOR_VALUE); for (int i = 1; i < node.jjtGetNumChildren(); i++) { Node statement = node.jjtGetChild(i); result = statement.jjtAccept(this, FOR_VALUE); } return result; } public Object visit(ASTLiteral node, Object data) { node.assertNoChildren(); return (ESValue) node.getValue(); } public Object visit(ASTIdentifier node, Object forWhat) { Object result; try { if (forWhat == FOR_VALUE) { result = evaluator.getValue(node.getName(),node.hashCode()); } else { result = evaluator.getReference(node.getName(),node.hashCode()); } } catch (EcmaScriptException e) { throw new PackagedException(e,node); } return result; } }