Implemented removing of compiled functions that no longer exist as

proposed by Stefan Matthias Aust. General refactoring of the class.
This commit is contained in:
hns 2003-07-16 13:25:43 +00:00
parent 28c6810fb0
commit 260cca8971

View file

@ -42,7 +42,7 @@ public final class RhinoCore {
public final Application app; public final Application app;
// the global object // the global object
Scriptable global; GlobalObject global;
// caching table for JavaScript object wrappers // caching table for JavaScript object wrappers
WeakHashMap wrappercache; WeakHashMap wrappercache;
@ -82,7 +82,7 @@ public final class RhinoCore {
try { try {
GlobalObject g = new GlobalObject(this, app, context); GlobalObject g = new GlobalObject(this, app, context);
global = context.initStandardObjects(g); global = (GlobalObject) context.initStandardObjects(g);
ScriptableObject.defineClass(global, HopObject.class); ScriptableObject.defineClass(global, HopObject.class);
ScriptableObject.defineClass(global, FileObject.class); ScriptableObject.defineClass(global, FileObject.class);
ScriptableObject.defineClass(global, FtpObject.class); ScriptableObject.defineClass(global, FtpObject.class);
@ -90,7 +90,8 @@ public final class RhinoCore {
XmlRpcObject.init(global); XmlRpcObject.init(global);
MailObject.init(global, app.getProperties()); MailObject.init(global, app.getProperties());
putPrototype("hopobject", putPrototype("hopobject",
ScriptableObject.getClassPrototype(global, "HopObject")); (ScriptableObject) ScriptableObject
.getClassPrototype(global, "HopObject"));
putPrototype("global", global); putPrototype("global", global);
// add some convenience functions to string, date and number prototypes // add some convenience functions to string, date and number prototypes
@ -135,53 +136,28 @@ public final class RhinoCore {
/** /**
* Initialize a prototype without fully parsing its script files. * Initialize a prototype without fully parsing its script files.
*
* @param prototype the prototype to be created
*/ */
synchronized void initPrototype(Prototype prototype) { synchronized void initPrototype(Prototype prototype) {
// System.err.println ("FESI INIT PROTO "+prototype);
Scriptable op = null;
String name = prototype.getName(); String name = prototype.getName();
// get the prototype's prototype if possible and necessary // check if the prototype info exists already
Scriptable opp = null; ScriptableObject op = getRawPrototype(name);
Prototype parent = prototype.getParentPrototype();
if (parent != null) {
// see if parent prototype is already registered. if not, register it
opp = getRawPrototype(parent.getName());
if (opp == null) {
initPrototype(parent);
opp = getRawPrototype(parent.getName());
}
}
if (!"global".equalsIgnoreCase(name) && !"hopobject".equalsIgnoreCase(name) &&
(opp == null)) {
if (app.isJavaPrototype(name)) {
opp = null;
} else {
opp = getRawPrototype("hopobject");
}
}
// if prototype doesn't exist (i.e. is a standard prototype built by HopExtension), create it.
op = getRawPrototype(name);
// if prototype info doesn't exist (i.e. is a standard prototype
// built by HopExtension), create it.
if (op == null) { if (op == null) {
try { try {
op = new HopObject(name); // context.newObject (global /*, "HopObject" */); op = new HopObject(name);
op.setPrototype(opp);
op.setParentScope(global); op.setParentScope(global);
op.put("prototypename", op, name); // op.put("prototypename", op, name);
} catch (Exception ignore) { } catch (Exception ignore) {
System.err.println("Error creating prototype: " + ignore); System.err.println("Error creating prototype: " + ignore);
ignore.printStackTrace(); ignore.printStackTrace();
} }
putPrototype(name, op); putPrototype(name, op);
} else {
// set parent prototype just in case it has been changed
op.setPrototype(opp);
} }
// Register a constructor for all types except global. // Register a constructor for all types except global.
@ -201,10 +177,76 @@ public final class RhinoCore {
/** /**
* Set up a prototype, parsing and compiling all its script files. * Set up a prototype, parsing and compiling all its script files.
*
* @param prototype the prototype to update/evaluate/compile
* @param info the info, containing the object proto, last update time and
* the set of compiled functions properties
*/ */
synchronized void evaluatePrototype(Prototype prototype) { synchronized void evaluatePrototype(Prototype prototype, TypeInfo info) {
// System.err.println ("FESI EVALUATE PROTO "+prototype+" FOR "+this); // System.err.println("EVALUATING PROTO: "+prototype);
Scriptable op = null; Scriptable op = info.objectPrototype;
// set the parent prototype in case it hasn't been done before
// or it has changed...
setParentPrototype(prototype, op);
// loop through the prototype's code elements and evaluate them
// first the zipped ones ...
for (Iterator it = prototype.getZippedCode().values().iterator(); it.hasNext();) {
Object code = it.next();
evaluate(prototype, op, code);
}
// then the unzipped ones (this is to make sure unzipped code overwrites zipped code)
for (Iterator it = prototype.getCode().values().iterator(); it.hasNext();) {
Object code = it.next();
evaluate(prototype, op, code);
}
// loop through function properties defined for this proto and
// remove those which are left over from the previous generation
// and haven't been renewed in this pass.
Set oldFunctions = info.compiledFunctions;
Set newFunctions = new HashSet();
Object[] keys = ((ScriptableObject) op).getAllIds();
for (int i = 0; i < keys.length; i++) {
String key = keys[i].toString();
if (info.predefinedProperties.contains(key)) {
// don't mess with properties we didn't set
continue;
}
Object prop = op.get(key, op);
if (oldFunctions.contains(prop) && prop instanceof NativeFunction) {
// if this is a function compiled from script, it's from the
// old generation and wasn't renewed -- delete it.
System.err.println("DELETING OLD FUNC: "+key);
try {
((ScriptableObject) op).setAttributes(key, op, 0);
op.delete(key);
} catch (PropertyException px) {
System.err.println("Error unsetting property "+key+" on "+prototype);
}
} else {
newFunctions.add(prop);
}
}
info.compiledFunctions = newFunctions;
info.lastUpdate = prototype.getLastUpdate();
}
/**
* Set the parent prototype on the ObjectPrototype.
*
* @param prototype the prototype spec
* @param op the prototype object
*/
private void setParentPrototype(Prototype prototype, Scriptable op) {
String name = prototype.getName();
if (!"global".equalsIgnoreCase(name) && !"hopobject".equalsIgnoreCase(name)) {
// get the prototype's prototype if possible and necessary // get the prototype's prototype if possible and necessary
Scriptable opp = null; Scriptable opp = null;
@ -213,57 +255,14 @@ public final class RhinoCore {
if (parent != null) { if (parent != null) {
// see if parent prototype is already registered. if not, register it // see if parent prototype is already registered. if not, register it
opp = getPrototype(parent.getName()); opp = getPrototype(parent.getName());
if (opp == null) {
evaluatePrototype(parent);
opp = getPrototype(parent.getName());
}
} }
String name = prototype.getName(); if (opp == null && !app.isJavaPrototype(name)) {
// FIXME: does this ever occur?
if (!"global".equalsIgnoreCase(name) && !"hopobject".equalsIgnoreCase(name) &&
(opp == null)) {
if (app.isJavaPrototype(name)) {
opp = null;
} else {
opp = getPrototype("hopobject"); opp = getPrototype("hopobject");
} }
}
// if prototype doesn't exist (i.e. is a standard prototype built by HopExtension), create it.
op = getPrototype(name);
if (op == null) {
try {
op = new HopObject(name); // context.newObject (global /*, "HopObject" */);
op.setPrototype(opp); op.setPrototype(opp);
op.setParentScope(global);
op.put("prototypename", op, name);
} catch (Exception ignore) {
System.err.println("Error creating prototype: " + ignore);
ignore.printStackTrace();
}
putPrototype(name, op);
} else {
// reset prototype to original state
resetPrototype(op);
// set parent prototype just in case it has been changed
op.setPrototype(opp);
}
for (Iterator it = prototype.getZippedCode().values().iterator(); it.hasNext();) {
Object code = it.next();
evaluate(prototype, code);
}
for (Iterator it = prototype.getCode().values().iterator(); it.hasNext();) {
Object code = it.next();
evaluate(prototype, code);
} }
} }
@ -271,40 +270,28 @@ public final class RhinoCore {
* This is a version of org.mozilla.javascript.FunctionObject.addAsConstructor() * This is a version of org.mozilla.javascript.FunctionObject.addAsConstructor()
* that does not set the constructor property in the prototype. This is because * that does not set the constructor property in the prototype. This is because
* we want our own scripted constructor function to prevail, if it is defined. * we want our own scripted constructor function to prevail, if it is defined.
*
* @param name the name of the constructor
* @param op the object prototype
*/ */
private void installConstructor(String name, Scriptable prototype) { private void installConstructor(String name, Scriptable op) {
FunctionObject fo = new FunctionObject(name, HopObject.hopObjCtor, global); FunctionObject fo = new FunctionObject(name, HopObject.hopObjCtor, global);
ScriptRuntime.setFunctionProtoAndParent(global, fo); ScriptRuntime.setFunctionProtoAndParent(global, fo);
fo.setImmunePrototypeProperty(prototype); fo.setImmunePrototypeProperty(op);
prototype.setParentScope(fo); op.setParentScope(fo);
ScriptableObject.defineProperty(global, name, fo, ScriptableObject.DONTENUM); ScriptableObject.defineProperty(global, name, fo, ScriptableObject.DONTENUM);
fo.setParentScope(global); fo.setParentScope(global);
} }
/**
* Return an object prototype to its initial state, removing all application specific
* functions.
*/
synchronized void resetPrototype(Scriptable op) {
Object[] ids = op.getIds();
for (int i = 0; i < ids.length; i++) {
/* String prop = en.nextElement ().toString ();
try {
ESValue esv = op.getProperty (prop, prop.hashCode ());
if (esv instanceof ConstructedFunctionObject || esv instanceof FesiActionAdapter.ThrowException)
op.deleteProperty (prop, prop.hashCode());
} catch (Exception x) {} */
}
}
/** /**
* This method is called before an execution context is entered to let the * This method is called before an execution context is entered to let the
* engine know it should update its prototype information. * engine know it should update its prototype information. The update policy
* here is to check for update those prototypes which already have been compiled
* before. Others will be updated/compiled on demand.
*/ */
public synchronized void updatePrototypes() { public synchronized void updatePrototypes() {
if ((System.currentTimeMillis() - lastUpdate) < 1000L) { if ((System.currentTimeMillis() - lastUpdate) < 1000L) {
@ -334,8 +321,7 @@ public final class RhinoCore {
app.typemgr.updatePrototype(p); app.typemgr.updatePrototype(p);
if (p.getLastUpdate() > info.lastUpdate) { if (p.getLastUpdate() > info.lastUpdate) {
evaluatePrototype(p); evaluatePrototype(p, info);
info.lastUpdate = p.getLastUpdate();
} }
} }
} }
@ -344,6 +330,64 @@ public final class RhinoCore {
lastUpdate = System.currentTimeMillis(); lastUpdate = System.currentTimeMillis();
} }
/**
* Get a raw prototype, i.e. in potentially unfinished state
* without checking if it needs to be updated.
*/
private ScriptableObject getRawPrototype(String protoName) {
if (protoName == null) {
return null;
}
TypeInfo info = (TypeInfo) prototypes.get(protoName);
return (info == null) ? null : info.objectPrototype;
}
/**
* Get the object prototype for a prototype name and initialize/update it
* if necessary. The policy here is to update the prototype only if it
* hasn't been updated before, otherwise we assume it already was updated
* by updatePrototypes(), which is called for each request.
*/
public Scriptable getPrototype(String protoName) {
if (protoName == null) {
return null;
}
TypeInfo info = (TypeInfo) prototypes.get(protoName);
if ((info != null) && (info.lastUpdate == 0)) {
Prototype p = app.typemgr.getPrototype(protoName);
if (p != null) {
app.typemgr.updatePrototype(p);
if (p.getLastUpdate() > info.lastUpdate) {
evaluatePrototype(p, info);
}
// set info.lastUpdate to 1 if it is 0 so we know we
// have initialized this prototype already, even if
// it is empty (i.e. doesn't contain any scripts/skins/actions)
if (info.lastUpdate == 0) {
info.lastUpdate = 1;
}
}
}
return (info == null) ? null : info.objectPrototype;
}
/**
* Register an object prototype for a prototype name.
*/
private void putPrototype(String protoName, ScriptableObject op) {
if ((protoName != null) && (op != null)) {
prototypes.put(protoName, new TypeInfo(op, protoName));
}
}
/** /**
* Check if an object has a function property (public method if it * Check if an object has a function property (public method if it
* is a java object) with that name. * is a java object) with that name.
@ -375,7 +419,6 @@ public final class RhinoCore {
* Convert an input argument from Java to the scripting runtime * Convert an input argument from Java to the scripting runtime
* representation. * representation.
*/ */
public Object processXmlRpcArgument (Object what) throws Exception { public Object processXmlRpcArgument (Object what) throws Exception {
if (what == null) if (what == null)
return null; return null;
@ -412,7 +455,6 @@ public final class RhinoCore {
/** /**
* convert a JavaScript Object object to a generic Java object stucture. * convert a JavaScript Object object to a generic Java object stucture.
*/ */
public Object processXmlRpcResponse (Object what) throws Exception { public Object processXmlRpcResponse (Object what) throws Exception {
if (what instanceof NativeJavaObject) { if (what instanceof NativeJavaObject) {
what = ((NativeJavaObject) what).unwrap(); what = ((NativeJavaObject) what).unwrap();
@ -454,51 +496,6 @@ public final class RhinoCore {
} }
} }
return what; return what;
/* if (what == null || what instanceof ESNull)
return null;
if (what instanceof ArrayPrototype) {
ArrayPrototype a = (ArrayPrototype) what;
int l = a.size ();
Vector v = new Vector ();
for (int i=0; i<l; i++) {
Object nj = processXmlRpcResponse (a.getProperty (i));
v.addElement (nj);
}
return v;
}
if (what instanceof ObjectPrototype) {
ObjectPrototype o = (ObjectPrototype) what;
Hashtable t = new Hashtable ();
for (Enumeration e=o.getProperties (); e.hasMoreElements (); ) {
String next = (String) e.nextElement ();
// We don't do deep serialization of HopObjects to avoid
// that the whole web site structure is sucked out with one
// object. Instead we return some kind of "proxy" objects
// that only contain the prototype and id of the HopObject property.
Object nj = null;
ESValue esv = o.getProperty (next, next.hashCode ());
if (esv instanceof ESNode) {
INode node = ((ESNode) esv).getNode ();
if (node != null) {
Hashtable nt = new Hashtable ();
nt.put ("id", node.getID());
if (node.getPrototype() != null)
nt.put ("prototype", node.getPrototype ());
nj = nt;
}
} else
nj = processXmlRpcResponse (esv);
if (nj != null) // can't put null as value in hashtable
t.put (next, nj);
}
return t;
}
if (what instanceof ESUndefined || what instanceof ESNull)
return null;
Object jval = what.toJavaObject ();
if (jval instanceof Byte || jval instanceof Short)
jval = new Integer (jval.toString ());
return jval; */
} }
@ -509,63 +506,6 @@ public final class RhinoCore {
return app; return app;
} }
/**
* Get a raw prototype, i.e. in potentially unfinished state
* without checking if it needs to be updated.
*/
private Scriptable getRawPrototype(String protoName) {
if (protoName == null) {
return null;
}
TypeInfo info = (TypeInfo) prototypes.get(protoName);
return (info == null) ? null : info.objectPrototype;
}
/**
* Get the object prototype for a prototype name and initialize/update it
* if necessary.
*/
public Scriptable getPrototype(String protoName) {
if (protoName == null) {
return null;
}
TypeInfo info = (TypeInfo) prototypes.get(protoName);
if ((info != null) && (info.lastUpdate == 0)) {
Prototype p = app.typemgr.getPrototype(protoName);
if (p != null) {
app.typemgr.updatePrototype(p);
if (p.getLastUpdate() > info.lastUpdate) {
info.lastUpdate = p.getLastUpdate();
evaluatePrototype(p);
}
// set info.lastUpdate to 1 if it is 0 so we know we
// have initialized this prototype already, even if
// it is empty (i.e. doesn't contain any scripts/skins/actoins
if (info.lastUpdate == 0) {
info.lastUpdate = 1;
}
}
}
return (info == null) ? null : info.objectPrototype;
}
/**
* Register an object prototype for a certain prototype name.
*/
private void putPrototype(String protoName, Scriptable op) {
if ((protoName != null) && (op != null)) {
prototypes.put(protoName, new TypeInfo(op, protoName));
}
}
/** /**
* Get a Script wrapper for any given object. If the object implements the IPathElement * Get a Script wrapper for any given object. If the object implements the IPathElement
@ -645,44 +585,9 @@ public final class RhinoCore {
return esn; return esn;
} }
/** /////////////////////////////////////////////
* Register a new Node wrapper with the wrapper cache. This is used by the // skin related methods
* Node constructor. /////////////////////////////////////////////
*/
/* public void putNodeWrapper(INode n, Scriptable esn) {
wrappercache.put(n, esn);
} */
private synchronized void evaluate(Prototype prototype, Object code) {
if (code instanceof FunctionFile) {
FunctionFile funcfile = (FunctionFile) code;
File file = funcfile.getFile();
if (file != null) {
try {
FileReader fr = new FileReader(file);
updateEvaluator(prototype, fr, funcfile.getSourceName(), 1);
} catch (IOException iox) {
app.logEvent("Error updating function file: " + iox);
}
} else {
StringReader reader = new StringReader(funcfile.getContent());
updateEvaluator(prototype, reader, funcfile.getSourceName(), 1);
}
} else if (code instanceof ActionFile) {
ActionFile action = (ActionFile) code;
RhinoActionAdapter fa = new RhinoActionAdapter(action);
try {
updateEvaluator(prototype, new StringReader(fa.function),
action.getSourceName(), 0);
} catch (Exception esx) {
app.logEvent("Error parsing " + action + ": " + esx);
}
}
}
protected static Object[] unwrapSkinpath(Object[] skinpath) { protected static Object[] unwrapSkinpath(Object[] skinpath) {
if (skinpath != null) { if (skinpath != null) {
@ -721,25 +626,64 @@ public final class RhinoCore {
return param; return param;
} }
private synchronized void updateEvaluator(Prototype prototype, Reader reader, ////////////////////////////////////////////////
String sourceName, int firstline) { // private evaluation/compilation methods
// context = Context.enter(context); ////////////////////////////////////////////////
try {
Scriptable op = getPrototype(prototype.getName());
private synchronized void evaluate(Prototype prototype, Scriptable op, Object code) {
if (code instanceof FunctionFile) {
FunctionFile funcfile = (FunctionFile) code;
File file = funcfile.getFile();
if (file != null) {
try {
FileReader fr = new FileReader(file);
updateEvaluator(prototype, op, fr, funcfile.getSourceName(), 1);
} catch (IOException iox) {
app.logEvent("Error updating function file: " + iox);
}
} else {
StringReader reader = new StringReader(funcfile.getContent());
updateEvaluator(prototype, op, reader, funcfile.getSourceName(), 1);
}
} else if (code instanceof ActionFile) {
ActionFile action = (ActionFile) code;
RhinoActionAdapter fa = new RhinoActionAdapter(action);
try {
updateEvaluator(prototype, op, new StringReader(fa.function),
action.getSourceName(), 0);
} catch (Exception esx) {
app.logEvent("Error parsing " + action + ": " + esx);
}
}
}
private synchronized void updateEvaluator(Prototype prototype, Scriptable op,
Reader reader, String sourceName, int firstline) {
// System.err.println("UPDATE EVALUATOR: "+prototype+" - "+sourceName);
try {
// get the current context // get the current context
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
// do the update, evaluating the file // do the update, evaluating the file
cx.evaluateReader(op, reader, sourceName, firstline, null); cx.evaluateReader(op, reader, sourceName, firstline, null);
} catch (Throwable e) { } catch (Throwable e) {
app.logEvent("Error parsing function file " + sourceName + ": " + e); app.logEvent("Error parsing file " + sourceName + ": " + e);
// also write to standard out unless we're logging to it anyway
if (!"console".equalsIgnoreCase(app.getProperty("logDir"))) {
System.err.println("Error parsing file " + sourceName + ": " + e);
}
// e.printStackTrace(); // e.printStackTrace();
} finally { } finally {
if (reader != null) { if (reader != null) {
try { try {
reader.close(); reader.close();
} catch (IOException ignore) { } catch (IOException ignore) {
// shouldn't happen
} }
} }
} }
@ -756,13 +700,27 @@ public final class RhinoCore {
* TypeInfo helper class * TypeInfo helper class
*/ */
class TypeInfo { class TypeInfo {
Scriptable objectPrototype; // the object prototype for this type
ScriptableObject objectPrototype;
// timestamp of last update
long lastUpdate = 0; long lastUpdate = 0;
// the prototype name
String protoName; String protoName;
// a set of property values that were defined in last script compliation
Set compiledFunctions;
// a set of property keys that were present before first script compilation
final Set predefinedProperties;
public TypeInfo(Scriptable op, String name) { public TypeInfo(ScriptableObject op, String name) {
objectPrototype = op; objectPrototype = op;
protoName = name; protoName = name;
compiledFunctions = new HashSet(0);
// remember properties already defined on this object prototype
predefinedProperties = new HashSet();
Object[] keys = op.getAllIds();
for (int i = 0; i < keys.length; i++) {
predefinedProperties.add(keys[i].toString());
}
} }
public String toString() { public String toString() {