1060 lines
37 KiB
Java
1060 lines
37 KiB
Java
// Evaluator.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.Exceptions.*;
|
|
import FESI.Parser.*;
|
|
import FESI.AST.*;
|
|
import FESI.Extensions.Extension;
|
|
import FESI.jslib.*;
|
|
import FESI.Data.*;
|
|
|
|
import java.util.Vector;
|
|
import java.util.Hashtable;
|
|
import java.util.Enumeration;
|
|
import java.util.EventObject;
|
|
import java.util.StringTokenizer;
|
|
|
|
import java.io.*;
|
|
import java.util.zip.*;
|
|
|
|
/**
|
|
* Defines the evaluation interface and contains the evaluation context.
|
|
* <P><B>Important:</B> This object is also used as the synchronization
|
|
* object - all entries into the evaluation process must be synchronized
|
|
* on the evaluator, as the inside of the evaluator is not synchronized
|
|
* for speed reasons.
|
|
*/
|
|
public class Evaluator {
|
|
|
|
// used to stop thread, 06.12.99 Hannes Wallnoefer
|
|
public volatile Thread thread;
|
|
|
|
private static String eol = System.getProperty("line.separator", "\n");
|
|
|
|
/**
|
|
* Return the version identifier of the interpreter
|
|
*/
|
|
public static String getVersion() {
|
|
return "1.1.4 (30-Jan-2000)";
|
|
}
|
|
|
|
/**
|
|
* Return the welcome text (including copyright and version)
|
|
* of the interpreter (as two lines)
|
|
*/
|
|
public static String getWelcomeText() {
|
|
return
|
|
"FESI (pronounced like 'fuzzy'): an EcmaScript Interpreter" + eol +
|
|
"Copyright (c) Jean-Marc Lugrin, 1998 - Version: " + Evaluator.getVersion();
|
|
|
|
}
|
|
|
|
private boolean debugParse = false;
|
|
|
|
// All privileged objects of interest of the evaluator
|
|
|
|
private GlobalObject globalObject = null;
|
|
|
|
private ESObject objectPrototype = null;
|
|
private ESObject functionPrototype = null;
|
|
private ESObject functionObject = null;
|
|
private ESObject stringPrototype = null;
|
|
private ESObject numberPrototype = null;
|
|
private ESObject booleanPrototype = null;
|
|
private ESObject arrayPrototype = null;
|
|
private ESObject datePrototype = null;
|
|
private ESObject packageObject = null;
|
|
|
|
// Current environment
|
|
private ScopeChain theScopeChain = null;
|
|
private ESObject currentVariableObject = null;
|
|
private ESObject currentThisObject = null;
|
|
|
|
// Visitors used for interpretation
|
|
private EcmaScriptFunctionVisitor functionDeclarationVisitor = null;
|
|
private EcmaScriptVariableVisitor varDeclarationVisitor = null;
|
|
//private EcmaScriptEvaluateVisitor evaluationVisitor = null;
|
|
|
|
// List of loaded extensions
|
|
private Hashtable extensions = null;
|
|
|
|
/**
|
|
* Reset the evaluator, forgetting all global definitions and loaded extensions
|
|
*/
|
|
protected void reset() {
|
|
functionDeclarationVisitor = new EcmaScriptFunctionVisitor(this);
|
|
varDeclarationVisitor = new EcmaScriptVariableVisitor(this);
|
|
// evaluationVisitor = new EcmaScriptEvaluateVisitor(this);
|
|
globalObject = GlobalObject.makeGlobalObject(this);
|
|
packageObject = new ESPackages(this);
|
|
|
|
extensions = new Hashtable(); // forget extensions
|
|
|
|
}
|
|
|
|
/**
|
|
* Create a new empty evaluator
|
|
*/
|
|
public Evaluator () {
|
|
reset();
|
|
}
|
|
|
|
/**
|
|
* Get the variable visitor of this evaluator
|
|
* @return the Variable visitor
|
|
*/
|
|
public EcmaScriptVariableVisitor getVarDeclarationVisitor() {
|
|
return varDeclarationVisitor;
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
// Access to special objects of the environment
|
|
//------------------------------------------------------------
|
|
|
|
/**
|
|
* Get the this object of this evaluator
|
|
* @return the this object
|
|
*/
|
|
public ESObject getThisObject() {
|
|
return currentThisObject;
|
|
}
|
|
|
|
/**
|
|
* Get the global object of this evaluator
|
|
* @return the global object
|
|
*/
|
|
public GlobalObject getGlobalObject() {
|
|
return globalObject;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the debug mode for the parser
|
|
* @param dp true to set debug mode on
|
|
*/
|
|
public void setDebugParse(boolean dp) {
|
|
debugParse = dp;
|
|
}
|
|
|
|
/**
|
|
* Return the debug state for the parser
|
|
* @return true if debug on
|
|
*/
|
|
public boolean isDebugParse() {
|
|
return debugParse;
|
|
}
|
|
|
|
/**
|
|
* Set the object prototype object
|
|
* <P>Used only by initilization code
|
|
* @param o the object
|
|
*/
|
|
public void setObjectPrototype(ESObject o) {
|
|
objectPrototype = o;
|
|
}
|
|
/**
|
|
* Get the Object prototype object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getObjectPrototype() {
|
|
return objectPrototype;
|
|
}
|
|
|
|
/**
|
|
* Set the Function prototype object
|
|
* <P>Used only by initilization code
|
|
* @param o the object
|
|
*/
|
|
public void setFunctionPrototype(ESObject o) {
|
|
functionPrototype = o;
|
|
}
|
|
/**
|
|
* Get the Function prototype object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getFunctionPrototype() {
|
|
return functionPrototype;
|
|
}
|
|
|
|
/**
|
|
* Set the Function object
|
|
* <P>Used only by initilization code
|
|
* @param o the object
|
|
*/
|
|
public void setFunctionObject(ESObject o) {
|
|
functionObject = o;
|
|
}
|
|
/**
|
|
* Get the Function object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getFunctionObject() {
|
|
return functionObject;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the String object prototype
|
|
* <P>Used only by initilization code
|
|
* @param o the object
|
|
*/
|
|
public void setStringPrototype(ESObject o) {
|
|
stringPrototype = o;
|
|
}
|
|
/**
|
|
* Get the String prototype object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getStringPrototype() {
|
|
return stringPrototype;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the Number object prototpe
|
|
* <P>Used only by initilization code
|
|
* @param o the object
|
|
*/
|
|
public void setNumberPrototype(ESObject o) {
|
|
numberPrototype = o;
|
|
}
|
|
/**
|
|
* Get the Number prototype object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getNumberPrototype() {
|
|
return numberPrototype;
|
|
}
|
|
|
|
/**
|
|
* Set the Boolean object prototype
|
|
* <P>Used only by initilization code
|
|
* @param o the object
|
|
*/
|
|
public void setBooleanPrototype(ESObject o) {
|
|
booleanPrototype = o;
|
|
}
|
|
/**
|
|
* Get the Boolean prototype object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getBooleanPrototype() {
|
|
return booleanPrototype;
|
|
}
|
|
|
|
/**
|
|
* Set the Array prototype object
|
|
* <P>Used only by initilization code
|
|
* @param o the object
|
|
*/
|
|
public void setArrayPrototype(ESObject o) {
|
|
arrayPrototype = o;
|
|
}
|
|
/**
|
|
* Get the Array prototype object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getArrayPrototype() {
|
|
return arrayPrototype;
|
|
}
|
|
|
|
/**
|
|
* Set the Date object prototype
|
|
* <P>Used only by initilization code
|
|
* @param o the object
|
|
*/
|
|
public void setDatePrototype(ESObject o) {
|
|
datePrototype = o;
|
|
}
|
|
/**
|
|
* Get the Date prototype object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getDatePrototype() {
|
|
return datePrototype;
|
|
}
|
|
|
|
/**
|
|
* Get the Package object
|
|
* @return the ESObject
|
|
*/
|
|
public ESObject getPackageObject() {
|
|
return packageObject;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------
|
|
// Extension support
|
|
//------------------------------------------------------------
|
|
|
|
/**
|
|
* Get a loaded extension by name
|
|
* @param name Extension to look up
|
|
* @return the extension or null if not loaded
|
|
*/
|
|
public Extension getExtension(String name) {
|
|
return (Extension) extensions.get(name);
|
|
}
|
|
|
|
/**
|
|
* Get the list of all extensions
|
|
* @return The extensions enumnerator
|
|
*/
|
|
public Enumeration getExtensions() {
|
|
return extensions.keys();
|
|
}
|
|
|
|
/**
|
|
* Add an extension by name, load it if not already loaded
|
|
* @param name the name of the extension to load
|
|
* @return the loaded object or null in case of error
|
|
* @exception Error ini initalizing the extension
|
|
*/
|
|
public Object addExtension(String name) throws EcmaScriptException {
|
|
Object extension = getExtension(name);
|
|
if (extension == null) {
|
|
try {
|
|
extension = Class.forName(name).newInstance();
|
|
if (extension instanceof Extension) {
|
|
((Extension) extension).initializeExtension(this);
|
|
} else if (extension instanceof JSExtension) {
|
|
GlobalObject go = this.getGlobalObject();
|
|
JSGlobalWrapper jgo = new JSGlobalWrapper(go,this);
|
|
try {
|
|
((JSExtension) extension).initializeExtension(jgo);
|
|
} catch (JSException e) {
|
|
return null;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
extensions.put(name, extension);
|
|
} catch (ClassNotFoundException e) { // return null
|
|
extension = null;
|
|
} catch (NoClassDefFoundError e) { // return null
|
|
extension = null;
|
|
} catch (IllegalAccessException e) { // return null
|
|
extension = null;
|
|
} catch (InstantiationException e) { // return null
|
|
extension = null;
|
|
}
|
|
}
|
|
return extension;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add an extension by name, load it if not already loaded.
|
|
* Generate an error if not found
|
|
* @param name the name of the extension to load
|
|
* @return the loaded object
|
|
* @exception EcmaScriptException if the extension cannot be loaded or error during initilization
|
|
*/
|
|
public Object addMandatoryExtension(String name) throws EcmaScriptException {
|
|
Object extension = getExtension(name);
|
|
if (extension == null) {
|
|
try {
|
|
extension = Class.forName(name).newInstance();
|
|
if (extension instanceof Extension) {
|
|
((Extension) extension).initializeExtension(this);
|
|
} else if (extension instanceof JSExtension) {
|
|
GlobalObject go = this.getGlobalObject();
|
|
JSGlobalWrapper jgo = new JSGlobalWrapper(go,this);
|
|
try {
|
|
((JSExtension) extension).initializeExtension(jgo);
|
|
} catch (JSException e) {
|
|
throw new EcmaScriptException("Error initializing extension " + name, e);
|
|
}
|
|
} else {
|
|
throw new EcmaScriptException("Extenstion object " + name + " of wrong type " + extension.getClass());
|
|
}
|
|
extensions.put(name, extension);
|
|
} catch (ClassNotFoundException e) {
|
|
throw new EcmaScriptException("Error loading extension " + name, e);
|
|
} catch (NoClassDefFoundError e) {
|
|
throw new EcmaScriptException("Error loading extension " + name, e);
|
|
} catch (IllegalAccessException e) {
|
|
throw new EcmaScriptException("Error loading extension " + name, e);
|
|
} catch (InstantiationException e) {
|
|
throw new EcmaScriptException("Error loading extension " + name, e);
|
|
}
|
|
}
|
|
return extension;
|
|
}
|
|
|
|
/**
|
|
* Add an initialized extension.
|
|
* Generate an error if not found
|
|
* @param name the name of the extension to load
|
|
* @param extension The extension object
|
|
* @return the loaded object
|
|
* @exception EcmaScriptException if the extension cannot be loaded or error during initilization
|
|
*/
|
|
public Object addMandatoryExtension(String name,FESI.jslib.JSExtension extension) throws EcmaScriptException {
|
|
|
|
GlobalObject go = this.getGlobalObject();
|
|
JSGlobalWrapper jgo = new JSGlobalWrapper(go,this);
|
|
try {
|
|
((JSExtension) extension).initializeExtension(jgo);
|
|
} catch (JSException e) {
|
|
throw new EcmaScriptException("Error initializing extension " + name, e);
|
|
}
|
|
|
|
extensions.put(name, extension);
|
|
|
|
return extension;
|
|
}
|
|
|
|
/**
|
|
* Get a reference to an indentifier (when its hash code is not known)
|
|
* @param identifier The name of the variable
|
|
* @return A reference object
|
|
*/
|
|
public ESReference getReference(String identifier) throws EcmaScriptException {
|
|
return theScopeChain.getReference(identifier);
|
|
}
|
|
/**
|
|
* Get a reference to an indentifier (when its hash code is known)
|
|
* @param identifier The name of the variable
|
|
* @param hash Its hash code (must be exact!)
|
|
* @return A reference object
|
|
*/
|
|
public ESReference getReference(String identifier,int hash) throws EcmaScriptException {
|
|
return theScopeChain.getReference(identifier, hash);
|
|
}
|
|
/**
|
|
* Get the value of a variable in the scope chain (when its hash code is not known)
|
|
* @param identifier The name of the variable
|
|
* @return A value
|
|
*/
|
|
public ESValue getValue(String identifier) throws EcmaScriptException {
|
|
return theScopeChain.getValue(identifier);
|
|
}
|
|
/**
|
|
* Get the value of a variable in the scope chain (when its hash code is known)
|
|
* @param identifier The name of the variable
|
|
* @param hash Its hash code (must be exact!)
|
|
* @return A value
|
|
*/
|
|
public ESValue getValue(String identifier,int hash) throws EcmaScriptException {
|
|
return theScopeChain.getValue(identifier, hash);
|
|
}
|
|
|
|
/**
|
|
* Call a routine referenced by name (in the scope chain)
|
|
* @param thisObject the this of the called routine
|
|
* @param functionName The name of the function
|
|
* @param Its hash code
|
|
* @param arguments The argument array
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
* @return the resulting value
|
|
*/
|
|
public ESValue doIndirectCall(ESObject thisObject, String functionName,int hash, ESValue[] arguments) throws EcmaScriptException {
|
|
return theScopeChain.doIndirectCall(this, thisObject, functionName, hash, arguments);
|
|
}
|
|
|
|
/**
|
|
* Create variable only if does not already exist (do not overwrite parameters
|
|
* and functions of the same name)
|
|
* @param name the new variable name
|
|
* @param hashCode Its hash code
|
|
* @exception EmcaScriptException In case of any error during setting
|
|
*/
|
|
public void createVariable(String name, int hashCode) throws EcmaScriptException {
|
|
if (!currentVariableObject.hasProperty(name, hashCode)) {
|
|
ESReference newVar = new ESReference(currentVariableObject, name, hashCode);
|
|
newVar.putValue(currentVariableObject, ESUndefined.theUndefined);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Put a value in a variable (given as a reference)
|
|
* @param leftValue The reference to the variable to modify
|
|
* @param rightValue The value to set
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
public void putValue(ESReference leftValue, ESValue rightValue) throws EcmaScriptException {
|
|
leftValue.putValue(globalObject, rightValue);
|
|
}
|
|
|
|
/**
|
|
* Sub evaluator - evaluate an eval string in a program (not a top level evaluation !)
|
|
* @param theSource The string to evaluate
|
|
* @return The result of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
public ESValue evaluateEvalString(String theSource) throws EcmaScriptException {
|
|
ESValue theValue = ESUndefined.theUndefined;
|
|
java.io.StringReader is =
|
|
new java.io.StringReader(theSource);
|
|
EcmaScript parser = new EcmaScript(is);
|
|
ASTProgram programNode = null;
|
|
StringEvaluationSource es = new StringEvaluationSource(theSource, null);
|
|
try {
|
|
// ASTProgram n = parser.Program();
|
|
programNode = (ASTProgram)parser.Program();
|
|
if (debugParse) {
|
|
System.out.println();
|
|
System.out.println("Dump parse tree of eval (debugParse true)");
|
|
programNode.dump("");
|
|
}
|
|
|
|
} catch (ParseException e) {
|
|
if (debugParse) {
|
|
System.out.println("[[PARSING ERROR DETECTED: (debugParse true)]]");
|
|
System.out.println(e.getMessage());
|
|
System.out.println("[[BY ROUTINE:]]");
|
|
e.printStackTrace();
|
|
System.out.println();
|
|
}
|
|
throw new EcmaScriptParseException(e, es);
|
|
} catch (TokenMgrError e) {
|
|
if (debugParse) {
|
|
System.out.println("[[LEXICAL ERROR DETECTED: (debugParse true)]]");
|
|
System.out.println(e.getMessage());
|
|
System.out.println("[[BY ROUTINE:]]");
|
|
e.printStackTrace();
|
|
System.out.println();
|
|
}
|
|
throw new EcmaScriptLexicalException(e, es);
|
|
}
|
|
|
|
ESObject savedVariableObject = currentVariableObject;
|
|
currentVariableObject = globalObject;
|
|
|
|
try {
|
|
|
|
functionDeclarationVisitor.processFunctionDeclarations(programNode, es);
|
|
varDeclarationVisitor.processVariableDeclarations(programNode, es);
|
|
EcmaScriptEvaluateVisitor evaluationVisitor = new EcmaScriptEvaluateVisitor(this);
|
|
theValue = evaluationVisitor.evaluateProgram(programNode, es);
|
|
|
|
if (theValue==null) theValue = ESUndefined.theUndefined; // null is not a valid result
|
|
|
|
if (evaluationVisitor.getCompletionCode()!= EcmaScriptEvaluateVisitor.C_NORMAL) {
|
|
throw new EcmaScriptException("Unexpected " +
|
|
evaluationVisitor.getCompletionCodeString() +
|
|
" in eval parameter top level" );
|
|
}
|
|
} finally {
|
|
currentVariableObject = savedVariableObject;
|
|
}
|
|
|
|
|
|
return theValue;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sub evaluator - evaluate a loaded file as program (not a top level evaluation !)
|
|
* @param file The file to load
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
public ESValue evaluateLoadFile(File file) throws EcmaScriptException {
|
|
ESValue theValue = ESUndefined.theUndefined;
|
|
if (!file.isFile()) {
|
|
throw new EcmaScriptException("File '" + file.getPath() + "' does not exist or is not a text file");
|
|
}
|
|
EvaluationSource es = new FileEvaluationSource(file.getPath(), null);
|
|
FileReader fr=null;
|
|
try {
|
|
fr = new FileReader(file);
|
|
theValue = evaluate(fr, null, es, false); // no return on main file
|
|
if (theValue == null) theValue = ESUndefined.theUndefined;
|
|
} catch (IOException e) {
|
|
throw new EcmaScriptException("IO Error loading file " + file + ": " + e);
|
|
} finally {
|
|
if (fr!=null) {
|
|
try {
|
|
fr.close();
|
|
} catch (IOException ignore) {
|
|
}
|
|
}
|
|
}
|
|
return theValue;
|
|
}
|
|
|
|
/**
|
|
* Sub evaluator - evaluate a module (a file or jar entry loaded via
|
|
* the FESI.path) as program (not a top level evaluation !)
|
|
* @param moduleName The name of the module to load
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
public ESValue evaluateLoadModule(String moduleName) throws EcmaScriptException {
|
|
ESValue theValue = ESUndefined.theUndefined;
|
|
if (moduleName == null) throw new EcmaScriptException("Missing file or module name for load");
|
|
String path = System.getProperty("FESI.path", null);
|
|
if (path == null) path = System.getProperty("java.class.path",null);
|
|
// System.out.println("** Try loading via " + path);
|
|
|
|
ESValue value = ESUndefined.theUndefined;
|
|
if (path == null) {
|
|
File file = new File(moduleName);
|
|
try {
|
|
value = evaluateLoadFile(file);
|
|
} catch (EcmaScriptParseException e) {
|
|
e.setNeverIncomplete();
|
|
throw e;
|
|
}
|
|
}
|
|
String lcModuleName = moduleName.toLowerCase();
|
|
boolean hasSuffix = lcModuleName.endsWith(".es") ||
|
|
lcModuleName.endsWith(".esw") ||
|
|
lcModuleName.endsWith(".js");
|
|
String separator = System.getProperty("path.separator",";");
|
|
StringTokenizer st = new StringTokenizer(path, separator);
|
|
while (st.hasMoreTokens()) {
|
|
String tryPath = st.nextToken();
|
|
value = tryLoad(tryPath, moduleName, hasSuffix);
|
|
if (value != null) break; // Found
|
|
}
|
|
|
|
if (value == null) {
|
|
// Not found
|
|
throw new EcmaScriptException("Module " + moduleName + " not found in " + path);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Try to load in a single path (directory or jar) environment
|
|
* (Utility routine to try load of each path entry)
|
|
* @param tryPath The path to try
|
|
* @param moduleName the name of the module to load
|
|
* @param hasSuffix true if the module name has a specified suffix
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
private ESValue tryLoad(String tryPath, String moduleName, boolean hasSuffix) throws EcmaScriptException {
|
|
// System.out.println("** tryPath: " + tryPath);
|
|
File dir = new File(tryPath);
|
|
if (dir.isDirectory()) {
|
|
File file;
|
|
if (hasSuffix) {
|
|
file = new File(dir, moduleName);
|
|
} else {
|
|
file = new File(dir, moduleName+".es");
|
|
if (! file.exists()) {
|
|
file = new File(dir, moduleName+".esw");
|
|
}
|
|
if (! file.exists()) {
|
|
file = new File(dir, moduleName+".js");
|
|
}
|
|
}
|
|
if (!file.exists()) return null;
|
|
|
|
// A File is found, load it
|
|
String cp;
|
|
try {
|
|
cp = file.getCanonicalPath();
|
|
} catch (IOException e) {
|
|
throw new EcmaScriptException("IO error accessing module " + moduleName +
|
|
" in directory " + dir, e);
|
|
}
|
|
// System.out.println("** File found: " + cp);
|
|
return evaluateLoadFile(file);
|
|
|
|
} else if (dir.isFile()) {
|
|
// System.out.println("** Looking in jar/zip: " + dir);
|
|
ZipFile zipFile;
|
|
try {
|
|
String cp = dir.getCanonicalPath();
|
|
zipFile = new ZipFile(cp);
|
|
} catch (IOException e) {
|
|
return null; // Cannot open jar/zip, ignore
|
|
}
|
|
ZipEntry zipEntry;
|
|
if (hasSuffix) {
|
|
zipEntry = zipFile.getEntry(moduleName);
|
|
} else {
|
|
zipEntry = zipFile.getEntry(moduleName + ".es");
|
|
if (zipEntry==null) {
|
|
zipEntry = zipFile.getEntry(moduleName + ".esw");
|
|
}
|
|
if (zipEntry==null) {
|
|
zipEntry = zipFile.getEntry(moduleName + ".js");
|
|
}
|
|
}
|
|
if (zipEntry == null) return null; // Not found in this jar file
|
|
byte buf[] = null;
|
|
try {
|
|
InputStream inputStream = zipFile.getInputStream(zipEntry);
|
|
int limit = (int)zipEntry.getSize();
|
|
buf = new byte[limit];
|
|
|
|
int total = 0;
|
|
while (total < limit)
|
|
{
|
|
int ct = inputStream.read(buf,total,limit-total);
|
|
total = total + ct;
|
|
if (ct == 0) {
|
|
throw new IOException ("Only " +
|
|
total + " bytes out of " + limit + " read from entry '" +
|
|
moduleName + "' in jar '" + zipFile.getName() +"'");
|
|
}
|
|
}
|
|
inputStream.close();
|
|
} catch (IOException e) {
|
|
if (ESLoader.isDebugLoader()) System.out.println(" ** Error reading jar: " + e);
|
|
return null;
|
|
}
|
|
|
|
EvaluationSource es = new JarEvaluationSource(dir.getPath(), moduleName, null);
|
|
Reader r = new StringReader(new String(buf));
|
|
ESValue theValue = evaluate(r, null, es, false); // no return on main file
|
|
if (theValue == null) theValue = ESUndefined.theUndefined;
|
|
return theValue;
|
|
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* subevaluator - Evaluate a function node (inside a program evaluation)
|
|
* @param node The AST node representing the list of statements
|
|
* @param es The evaluation source information for backtracce
|
|
* @param localVariableNames The set of local variable to create
|
|
* @param thisObject The this of this evaluation (so to speak)
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
public ESValue evaluateFunction(ASTStatementList node,
|
|
EvaluationSource es,
|
|
ESObject variableObject,
|
|
Vector localVariableNames,
|
|
ESObject thisObject) throws EcmaScriptException {
|
|
ESValue theValue = ESUndefined.theUndefined;
|
|
|
|
ESObject savedVariableObject = currentVariableObject;
|
|
ESObject savedThisObject = currentThisObject;
|
|
ScopeChain previousScopeChain = theScopeChain;
|
|
|
|
currentVariableObject = variableObject;
|
|
currentThisObject = thisObject;
|
|
theScopeChain = new ScopeChain(globalObject, null);
|
|
theScopeChain = new ScopeChain(variableObject, theScopeChain);
|
|
// EvaluationSource savedEvaluationSource = currentEvaluationSource;
|
|
// currentEvaluationSource = es;
|
|
try {
|
|
for (Enumeration e = localVariableNames.elements() ; e.hasMoreElements() ;) {
|
|
|
|
String variable =(String)(e.nextElement());
|
|
createVariable(variable, variable.hashCode());
|
|
}
|
|
EcmaScriptEvaluateVisitor evaluationVisitor = new EcmaScriptEvaluateVisitor(this);
|
|
theValue = evaluationVisitor.evaluateFunction(node, es);
|
|
int cc = evaluationVisitor.getCompletionCode();
|
|
if ((cc!= EcmaScriptEvaluateVisitor.C_NORMAL) &&
|
|
(cc!= EcmaScriptEvaluateVisitor.C_RETURN)) {
|
|
throw new EcmaScriptException("Unexpected " +
|
|
evaluationVisitor.getCompletionCodeString() +
|
|
" in function" );
|
|
}
|
|
} finally {
|
|
currentVariableObject = savedVariableObject;
|
|
theScopeChain = previousScopeChain;
|
|
currentThisObject = savedThisObject;
|
|
// currentEvaluationSource = savedEvaluationSource;
|
|
}
|
|
|
|
return theValue;
|
|
}
|
|
|
|
/**
|
|
* Sub evaluator - evaluate a with node (inside a program evaluation)
|
|
* @param node The with statement body
|
|
* @param scopeObject the new scope for this with
|
|
* @param es The evaluation source for back trace
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
public ESValue evaluateWith(ASTStatement node,
|
|
ESObject scopeObject,
|
|
EvaluationSource es) throws EcmaScriptException {
|
|
ESValue theValue = ESUndefined.theUndefined;
|
|
|
|
theScopeChain = new ScopeChain(scopeObject, theScopeChain);
|
|
try {
|
|
EcmaScriptEvaluateVisitor evaluationVisitor = new EcmaScriptEvaluateVisitor(this);
|
|
theValue = evaluationVisitor.evaluateWith(node, es);
|
|
} finally {
|
|
theScopeChain = theScopeChain.previousScope();
|
|
}
|
|
|
|
return theValue;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Top level core evaluator (on parsed program),
|
|
* Must be called from a function synchronized on the evaluator
|
|
* @param program The parsed program information
|
|
* @param thisObject The this of the evaluation (usually the global object)
|
|
* @param acceptReturn If true accept a return statement in the body
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
|
|
public ESValue evaluate(ParsedProgram program,
|
|
ESObject thisObject,
|
|
boolean acceptReturn) throws EcmaScriptException {
|
|
ASTProgram node = program.getProgramNode();
|
|
ESValue theValue = ESUndefined.theUndefined;
|
|
|
|
ESObject savedVariableObject = currentVariableObject;
|
|
ESObject savedThisObject = currentThisObject;
|
|
ScopeChain previousScopeChain = theScopeChain;
|
|
|
|
theScopeChain = new ScopeChain(globalObject, null);
|
|
currentVariableObject = globalObject;
|
|
currentThisObject = (thisObject != null) ? thisObject : globalObject;
|
|
|
|
EcmaScriptEvaluateVisitor evaluationVisitor = new EcmaScriptEvaluateVisitor(this);
|
|
try {
|
|
|
|
functionDeclarationVisitor.processFunctionDeclarations(node, program.getEvaluationSource(), thisObject);
|
|
Vector variables = program.getVariableNames();
|
|
for (Enumeration e = variables.elements() ; e.hasMoreElements() ;) {
|
|
String variable = (String)(e.nextElement());
|
|
createVariable(variable,variable.hashCode());
|
|
}
|
|
theValue = evaluationVisitor.evaluateProgram(node, program.getEvaluationSource());
|
|
} finally {
|
|
currentVariableObject = savedVariableObject;
|
|
theScopeChain = previousScopeChain;
|
|
currentThisObject = savedThisObject;
|
|
}
|
|
int completionCode = evaluationVisitor.getCompletionCode();
|
|
if (completionCode != EcmaScriptEvaluateVisitor.C_NORMAL) {
|
|
|
|
if (completionCode != EcmaScriptEvaluateVisitor.C_RETURN) {
|
|
throw new EcmaScriptException("Unexpected " +
|
|
evaluationVisitor.getCompletionCodeString() +
|
|
" in main program" );
|
|
} else if (!acceptReturn) {
|
|
throw new EcmaScriptException(
|
|
"Return is not accepted in main program with the 'eval' interface" );
|
|
}
|
|
}
|
|
return theValue;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* subevaluator - Evaluate an event function - must be synchronized
|
|
* @param sourceObject The source of the event (wrapped)
|
|
* @param theFunction The function to call
|
|
* @param args The arguments of the event
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
synchronized public void evaluateEvent(
|
|
ESWrapper sourceObject,
|
|
ESObject theFunction,
|
|
Object [] args) throws EcmaScriptException {
|
|
ESValue [] esArgs = new ESValue[args.length];
|
|
for (int i=0; i<args.length; i++) {
|
|
// esArgs[i] = new ESWrapper(args[i], this);
|
|
esArgs[i] = ESLoader.normalizeValue(args[i], this);
|
|
}
|
|
theFunction.callFunction(sourceObject, esArgs);
|
|
}
|
|
|
|
|
|
/**
|
|
* Top eval (synchronized) evaluate on identified stream
|
|
* @param is Input stream to evaluate
|
|
* @param thisObject The this of this evaluation
|
|
* @param es the identification of the source for back trace
|
|
* @param acceptReturn If true accepts return in main body
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
|
|
synchronized public ESValue evaluate(java.io.Reader is,
|
|
ESObject thisObject,
|
|
EvaluationSource es,
|
|
boolean acceptReturn) throws EcmaScriptException {
|
|
ESValue theValue = ESUndefined.theUndefined;
|
|
EcmaScript parser = new EcmaScript(is);
|
|
ASTProgram programNode = null;
|
|
try {
|
|
programNode = (ASTProgram) parser.Program();
|
|
if (debugParse) {
|
|
System.out.println();
|
|
System.out.println("@@ Dumping parse tree (debugParse true)");
|
|
programNode.dump("");
|
|
}
|
|
|
|
} catch (ParseException e) {
|
|
if (debugParse) {
|
|
System.out.println("[[PARSING ERROR DETECTED: (debugParse true)]]");
|
|
System.out.println(e.getMessage());
|
|
System.out.println("[[BY ROUTINE:]]");
|
|
e.printStackTrace();
|
|
System.out.println();
|
|
}
|
|
throw new EcmaScriptParseException(e, es);
|
|
} catch (TokenMgrError e) {
|
|
if (debugParse) {
|
|
System.out.println("[[LEXICAL ERROR DETECTED: (debugParse true)]]");
|
|
System.out.println(e.getMessage());
|
|
System.out.println("[[BY ROUTINE:]]");
|
|
e.printStackTrace();
|
|
System.out.println();
|
|
}
|
|
throw new EcmaScriptLexicalException(e, es);
|
|
}
|
|
|
|
Vector variableList = varDeclarationVisitor.processVariableDeclarations(programNode, es);
|
|
|
|
ParsedProgram program = new ParsedProgram(programNode, variableList, es);
|
|
|
|
theValue = evaluate(program, thisObject, acceptReturn);
|
|
|
|
return theValue;
|
|
}
|
|
|
|
/**
|
|
* Top eval (synchronized) evaluate on anonymous stream
|
|
* @param is Input stream to evaluate
|
|
* @param thisObject The this of this evaluation
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
synchronized public ESValue evaluate(Reader is, ESObject thisObject) throws EcmaScriptException {
|
|
EvaluationSource es = new UserEvaluationSource("<Anonymous stream>", null);
|
|
return evaluate (is, thisObject, es, false); // no return on anonymous streams
|
|
}
|
|
|
|
/**
|
|
* Top eval (synchronized) evaluate on anonymous stream with null thisObject
|
|
* @param is Input stream to evaluate
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
synchronized public ESValue evaluate(Reader is) throws EcmaScriptException {
|
|
return evaluate (is, null);
|
|
}
|
|
|
|
/**
|
|
* Top eval (synchronized) evaluate on an identified string (used by the GUI)
|
|
* @param text source to evaluate
|
|
* @param source Identification of the source
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
synchronized public ESValue evaluate(String text, String source) throws EcmaScriptException {
|
|
java.io.StringReader is = null;
|
|
ESValue v = null;
|
|
EvaluationSource es = new UserEvaluationSource(source, null);
|
|
try {
|
|
is = new java.io.StringReader(text);
|
|
v = evaluate(is, globalObject, es, false);
|
|
} finally {
|
|
if (is != null) is.close();
|
|
}
|
|
return v;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Top eval (synchronized) evaluate a file
|
|
* @param file file to evaluate
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
synchronized public ESValue evaluate(File file)
|
|
throws EcmaScriptException, IOException {
|
|
return evaluate(file, null);
|
|
}
|
|
|
|
/**
|
|
* Top eval (synchronized) evaluate a file
|
|
* @param file file to evaluate
|
|
* @param thisObject The this to use for the evaluation
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
synchronized public ESValue evaluate(File file, ESObject thisObject)
|
|
throws EcmaScriptException, IOException {
|
|
EvaluationSource es = new FileEvaluationSource(file.getPath(), null);
|
|
|
|
FileReader fr = null;
|
|
ESValue value = null;
|
|
try {
|
|
fr = new FileReader(file);
|
|
value = evaluate (fr, thisObject, es, false); // No return on a main file
|
|
} finally {
|
|
if (fr!= null) {
|
|
try {
|
|
fr.close();
|
|
} catch (IOException ignore) {
|
|
}
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Top eval (synchronized) evaluate on an anonymous string
|
|
* @param theSource source to evaluate
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
synchronized public ESValue evaluate(String theSource) throws EcmaScriptException {
|
|
return evaluate(theSource, null, false);
|
|
}
|
|
|
|
//synchronized public ESValue evaluate(String theSource, ESObject thisObject) throws EcmaScriptException {
|
|
// return evaluate(theSource, thisObject, false); // No return allowed
|
|
//}
|
|
/**
|
|
* Top eval (synchronized) evaluate on an anonymous string with
|
|
* thisObject and acceptBoolean
|
|
* @param theSource source to evaluate
|
|
* @param thisObject this for the evaluation
|
|
* @param returnAccepted If true a return is accepted in the main body
|
|
* @return The last value of the evaluation
|
|
* @exception EmcaScriptException In case of any error during evaluation
|
|
*/
|
|
synchronized public ESValue evaluate(String theSource,
|
|
ESObject thisObject,
|
|
boolean returnAccepted)
|
|
throws EcmaScriptException {
|
|
java.io.StringReader is = null;
|
|
ESValue v = null;
|
|
EvaluationSource es = new StringEvaluationSource(theSource, null);
|
|
try {
|
|
is = new java.io.StringReader(theSource);
|
|
v = evaluate(is, thisObject, es, returnAccepted);
|
|
} finally {
|
|
if (is != null) is.close();
|
|
}
|
|
return v;
|
|
}
|
|
|
|
}
|