* When there is a syntax error in any file associated with a prototype, always
throw an error and display a proper error message when on object of that type is accessed. * RhinoEngine.hasFunction() now checks on the actual object (wrapper) and thus is always right. * Unwrap WrappedException when caught in RhinoEngine.invoke() * Use helma.util.CacheMap instead of java.util.WeakHashMap for wrapper caching, possibly fixing a GC problem
This commit is contained in:
parent
6eb8cf9442
commit
5577263f66
2 changed files with 83 additions and 75 deletions
|
@ -46,7 +46,7 @@ public final class RhinoCore {
|
||||||
GlobalObject global;
|
GlobalObject global;
|
||||||
|
|
||||||
// caching table for JavaScript object wrappers
|
// caching table for JavaScript object wrappers
|
||||||
WeakHashMap wrappercache;
|
CacheMap wrappercache;
|
||||||
|
|
||||||
// table containing JavaScript prototypes
|
// table containing JavaScript prototypes
|
||||||
Hashtable prototypes;
|
Hashtable prototypes;
|
||||||
|
@ -64,7 +64,7 @@ public final class RhinoCore {
|
||||||
public RhinoCore(Application app) {
|
public RhinoCore(Application app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
// wrappercache = new CacheMap(500, .75f);
|
// wrappercache = new CacheMap(500, .75f);
|
||||||
wrappercache = new WeakHashMap();
|
wrappercache = new CacheMap(500);
|
||||||
prototypes = new Hashtable();
|
prototypes = new Hashtable();
|
||||||
|
|
||||||
Context context = Context.enter();
|
Context context = Context.enter();
|
||||||
|
@ -201,19 +201,21 @@ public final class RhinoCore {
|
||||||
// or it has changed...
|
// or it has changed...
|
||||||
setParentPrototype(prototype, op);
|
setParentPrototype(prototype, op);
|
||||||
|
|
||||||
|
info.error = null;
|
||||||
|
|
||||||
// loop through the prototype's code elements and evaluate them
|
// loop through the prototype's code elements and evaluate them
|
||||||
// first the zipped ones ...
|
// first the zipped ones ...
|
||||||
for (Iterator it = prototype.getZippedCode().values().iterator(); it.hasNext();) {
|
for (Iterator it = prototype.getZippedCode().values().iterator(); it.hasNext();) {
|
||||||
Object code = it.next();
|
Object code = it.next();
|
||||||
|
|
||||||
evaluate(prototype, op, code);
|
evaluate(prototype, info, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// then the unzipped ones (this is to make sure unzipped code overwrites zipped code)
|
// then the unzipped ones (this is to make sure unzipped code overwrites zipped code)
|
||||||
for (Iterator it = prototype.getCode().values().iterator(); it.hasNext();) {
|
for (Iterator it = prototype.getCode().values().iterator(); it.hasNext();) {
|
||||||
Object code = it.next();
|
Object code = it.next();
|
||||||
|
|
||||||
evaluate(prototype, op, code);
|
evaluate(prototype, info, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop through function properties defined for this proto and
|
// loop through function properties defined for this proto and
|
||||||
|
@ -355,6 +357,19 @@ public final class RhinoCore {
|
||||||
return (info == null) ? null : info.objectPrototype;
|
return (info == null) ? null : info.objectPrototype;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version of getPrototype() that retrieves a prototype and checks
|
||||||
|
* if it is valid, i.e. there were no errors when compiling it. If
|
||||||
|
* invalid, a ScriptingException is thrown.
|
||||||
|
*/
|
||||||
|
public Scriptable getValidPrototype(String protoName) {
|
||||||
|
TypeInfo info = getPrototypeInfo(protoName);
|
||||||
|
if (info != null && info.error != null) {
|
||||||
|
throw new RuntimeException(info.error.toString());
|
||||||
|
}
|
||||||
|
return info == null ? null : info.objectPrototype;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the object prototype for a prototype name and initialize/update it
|
* 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
|
* if necessary. The policy here is to update the prototype only if it
|
||||||
|
@ -362,6 +377,16 @@ public final class RhinoCore {
|
||||||
* by updatePrototypes(), which is called for each request.
|
* by updatePrototypes(), which is called for each request.
|
||||||
*/
|
*/
|
||||||
public Scriptable getPrototype(String protoName) {
|
public Scriptable getPrototype(String protoName) {
|
||||||
|
TypeInfo info = getPrototypeInfo(protoName);
|
||||||
|
return info == null ? null : info.objectPrototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helper function that retrieves a prototype's TypeInfo
|
||||||
|
* and creates it if not yet created. This is used by getPrototype() and
|
||||||
|
* getValidPrototype().
|
||||||
|
*/
|
||||||
|
private TypeInfo getPrototypeInfo(String protoName) {
|
||||||
if (protoName == null) {
|
if (protoName == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -387,7 +412,7 @@ public final class RhinoCore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (info == null) ? null : info.objectPrototype;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -399,33 +424,6 @@ public final class RhinoCore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an object has a function property (public method if it
|
|
||||||
* is a java object) with that name.
|
|
||||||
*/
|
|
||||||
public boolean hasFunction(String protoname, String fname) {
|
|
||||||
// System.err.println ("HAS_FUNC: "+fname);
|
|
||||||
try {
|
|
||||||
Scriptable op = getPrototype(protoname);
|
|
||||||
|
|
||||||
// if this is an untyped object return false
|
|
||||||
if (op == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object func = ScriptableObject.getProperty(op, fname);
|
|
||||||
|
|
||||||
if ((func != null) && func instanceof Function) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (Exception esx) {
|
|
||||||
// System.err.println ("Error in hasFunction: "+esx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an input argument from Java to the scripting runtime
|
* Convert an input argument from Java to the scripting runtime
|
||||||
* representation.
|
* representation.
|
||||||
|
@ -553,7 +551,6 @@ public final class RhinoCore {
|
||||||
* Get a script wrapper for an instance of helma.objectmodel.INode
|
* Get a script wrapper for an instance of helma.objectmodel.INode
|
||||||
*/
|
*/
|
||||||
public Scriptable getNodeWrapper(INode n) {
|
public Scriptable getNodeWrapper(INode n) {
|
||||||
// FIXME: is this needed? should this return ESNull.theNull?
|
|
||||||
if (n == null) {
|
if (n == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -561,25 +558,25 @@ public final class RhinoCore {
|
||||||
HopObject esn = (HopObject) wrappercache.get(n);
|
HopObject esn = (HopObject) wrappercache.get(n);
|
||||||
|
|
||||||
if (esn == null) {
|
if (esn == null) {
|
||||||
try {
|
|
||||||
String protoname = n.getPrototype();
|
|
||||||
|
|
||||||
Scriptable op = null;
|
String protoname = n.getPrototype();
|
||||||
|
|
||||||
// set the DbMapping of the node according to its prototype.
|
// set the DbMapping of the node according to its prototype.
|
||||||
// this *should* be done on the objectmodel level, but isn't currently
|
// this *should* be done on the objectmodel level, but isn't currently
|
||||||
// for embedded nodes since there's not enough type info at the objectmodel level
|
// for embedded nodes since there's not enough type info at the objectmodel level
|
||||||
// for those nodes.
|
// for those nodes.
|
||||||
if ((protoname != null) && (protoname.length() > 0) &&
|
/* if ((protoname != null) && (protoname.length() > 0) &&
|
||||||
(n.getDbMapping() == null)) {
|
(n.getDbMapping() == null)) {
|
||||||
n.setDbMapping(app.getDbMapping(protoname));
|
n.setDbMapping(app.getDbMapping(protoname));
|
||||||
}
|
} */
|
||||||
|
|
||||||
op = getPrototype(protoname);
|
Scriptable op = null;
|
||||||
|
|
||||||
|
op = getValidPrototype(protoname);
|
||||||
|
|
||||||
// no prototype found for this node?
|
// no prototype found for this node?
|
||||||
if (op == null) {
|
if (op == null) {
|
||||||
op = getPrototype("hopobject");
|
op = getValidPrototype("hopobject");
|
||||||
protoname = "hopobject";
|
protoname = "hopobject";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,12 +585,6 @@ public final class RhinoCore {
|
||||||
esn.setPrototype(op);
|
esn.setPrototype(op);
|
||||||
|
|
||||||
wrappercache.put(n, esn);
|
wrappercache.put(n, esn);
|
||||||
|
|
||||||
// app.logEvent ("Wrapper for "+n+" created");
|
|
||||||
} catch (Exception x) {
|
|
||||||
System.err.println("Error creating node wrapper: " + x);
|
|
||||||
throw new RuntimeException(x.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return esn;
|
return esn;
|
||||||
|
@ -644,7 +635,7 @@ public final class RhinoCore {
|
||||||
// private evaluation/compilation methods
|
// private evaluation/compilation methods
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
private synchronized void evaluate(Prototype prototype, Scriptable op, Object code) {
|
private synchronized void evaluate(Prototype prototype, TypeInfo info, Object code) {
|
||||||
if (code instanceof FunctionFile) {
|
if (code instanceof FunctionFile) {
|
||||||
FunctionFile funcfile = (FunctionFile) code;
|
FunctionFile funcfile = (FunctionFile) code;
|
||||||
File file = funcfile.getFile();
|
File file = funcfile.getFile();
|
||||||
|
@ -653,21 +644,21 @@ public final class RhinoCore {
|
||||||
try {
|
try {
|
||||||
FileReader fr = new FileReader(file);
|
FileReader fr = new FileReader(file);
|
||||||
|
|
||||||
updateEvaluator(prototype, op, fr, funcfile.getSourceName(), 1);
|
updateEvaluator(prototype, info, fr, funcfile.getSourceName(), 1);
|
||||||
} catch (IOException iox) {
|
} catch (IOException iox) {
|
||||||
app.logEvent("Error updating function file: " + iox);
|
app.logEvent("Error updating function file: " + iox);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
StringReader reader = new StringReader(funcfile.getContent());
|
StringReader reader = new StringReader(funcfile.getContent());
|
||||||
|
|
||||||
updateEvaluator(prototype, op, reader, funcfile.getSourceName(), 1);
|
updateEvaluator(prototype, info, reader, funcfile.getSourceName(), 1);
|
||||||
}
|
}
|
||||||
} else if (code instanceof ActionFile) {
|
} else if (code instanceof ActionFile) {
|
||||||
ActionFile action = (ActionFile) code;
|
ActionFile action = (ActionFile) code;
|
||||||
RhinoActionAdapter fa = new RhinoActionAdapter(action);
|
RhinoActionAdapter fa = new RhinoActionAdapter(action);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateEvaluator(prototype, op, new StringReader(fa.function),
|
updateEvaluator(prototype, info, new StringReader(fa.function),
|
||||||
action.getSourceName(), 0);
|
action.getSourceName(), 0);
|
||||||
} catch (Exception esx) {
|
} catch (Exception esx) {
|
||||||
app.logEvent("Error parsing " + action + ": " + esx);
|
app.logEvent("Error parsing " + action + ": " + esx);
|
||||||
|
@ -675,22 +666,29 @@ public final class RhinoCore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void updateEvaluator(Prototype prototype, Scriptable op,
|
private synchronized void updateEvaluator(Prototype prototype, TypeInfo info,
|
||||||
Reader reader, String sourceName, int firstline) {
|
Reader reader, String sourceName, int firstline) {
|
||||||
// System.err.println("UPDATE EVALUATOR: "+prototype+" - "+sourceName);
|
// System.err.println("UPDATE EVALUATOR: "+prototype+" - "+sourceName);
|
||||||
try {
|
try {
|
||||||
// get the current context
|
// get the current context
|
||||||
Context cx = Context.getCurrentContext();
|
Context cx = Context.getCurrentContext();
|
||||||
|
|
||||||
|
Scriptable op = info.objectPrototype;
|
||||||
|
|
||||||
// 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 (Exception e) {
|
||||||
app.logEvent("Error parsing file " + sourceName + ": " + e);
|
app.logEvent("Error parsing file " + sourceName + ": " + e.getClass());
|
||||||
// also write to standard out unless we're logging to it anyway
|
// also write to standard out unless we're logging to it anyway
|
||||||
if (!"console".equalsIgnoreCase(app.getProperty("logDir"))) {
|
if (!"console".equalsIgnoreCase(app.getProperty("logDir"))) {
|
||||||
System.err.println("Error parsing file " + sourceName + ": " + e);
|
System.err.println("Error parsing file " + sourceName + ": " + e);
|
||||||
}
|
}
|
||||||
|
// mark prototype as broken
|
||||||
|
if (info.error == null && e instanceof EcmaError) {
|
||||||
|
info.error = (EcmaError) e;
|
||||||
|
wrappercache.clear();
|
||||||
|
}
|
||||||
// e.printStackTrace();
|
// e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
if (reader != null) {
|
if (reader != null) {
|
||||||
|
@ -714,17 +712,24 @@ public final class RhinoCore {
|
||||||
* TypeInfo helper class
|
* TypeInfo helper class
|
||||||
*/
|
*/
|
||||||
class TypeInfo {
|
class TypeInfo {
|
||||||
|
|
||||||
// the object prototype for this type
|
// the object prototype for this type
|
||||||
ScriptableObject objectPrototype;
|
ScriptableObject objectPrototype;
|
||||||
|
|
||||||
// timestamp of last update
|
// timestamp of last update
|
||||||
long lastUpdate = 0;
|
long lastUpdate = 0;
|
||||||
|
|
||||||
// the prototype name
|
// the prototype name
|
||||||
String protoName;
|
String protoName;
|
||||||
|
|
||||||
// a set of property values that were defined in last script compliation
|
// a set of property values that were defined in last script compliation
|
||||||
Set compiledFunctions;
|
Set compiledFunctions;
|
||||||
|
|
||||||
// a set of property keys that were present before first script compilation
|
// a set of property keys that were present before first script compilation
|
||||||
final Set predefinedProperties;
|
final Set predefinedProperties;
|
||||||
|
|
||||||
|
EcmaError error;
|
||||||
|
|
||||||
public TypeInfo(ScriptableObject op, String name) {
|
public TypeInfo(ScriptableObject op, String name) {
|
||||||
objectPrototype = op;
|
objectPrototype = op;
|
||||||
protoName = name;
|
protoName = name;
|
||||||
|
|
|
@ -297,6 +297,8 @@ public class RhinoEngine implements ScriptingEngine {
|
||||||
String msg;
|
String msg;
|
||||||
if (x instanceof JavaScriptException) {
|
if (x instanceof JavaScriptException) {
|
||||||
msg = ((JavaScriptException) x).getValue().toString();
|
msg = ((JavaScriptException) x).getValue().toString();
|
||||||
|
} else if (x instanceof WrappedException) {
|
||||||
|
msg = ((WrappedException) x).getWrappedException().toString();
|
||||||
} else {
|
} else {
|
||||||
msg = x.toString();
|
msg = x.toString();
|
||||||
}
|
}
|
||||||
|
@ -336,14 +338,15 @@ public class RhinoEngine implements ScriptingEngine {
|
||||||
*/
|
*/
|
||||||
public boolean hasFunction(Object obj, String fname) {
|
public boolean hasFunction(Object obj, String fname) {
|
||||||
// System.err.println ("HAS_FUNC: "+obj+"."+fname);
|
// System.err.println ("HAS_FUNC: "+obj+"."+fname);
|
||||||
if (obj instanceof Scriptable) {
|
Scriptable op = obj == null ? global : Context.toObject(obj, global);
|
||||||
Scriptable scrpt = (Scriptable) obj;
|
|
||||||
Object func = scrpt.get(fname, scrpt);
|
Object func = ScriptableObject.getProperty(op, fname.replace('.', '_'));
|
||||||
if (func != null && func instanceof Function) {
|
|
||||||
|
if (func != null && func != Undefined.instance && func instanceof Function) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return core.hasFunction(app.getPrototypeName(obj), fname.replace('.', '_'));
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue