* 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;
|
||||
|
||||
// caching table for JavaScript object wrappers
|
||||
WeakHashMap wrappercache;
|
||||
CacheMap wrappercache;
|
||||
|
||||
// table containing JavaScript prototypes
|
||||
Hashtable prototypes;
|
||||
|
@ -64,7 +64,7 @@ public final class RhinoCore {
|
|||
public RhinoCore(Application app) {
|
||||
this.app = app;
|
||||
// wrappercache = new CacheMap(500, .75f);
|
||||
wrappercache = new WeakHashMap();
|
||||
wrappercache = new CacheMap(500);
|
||||
prototypes = new Hashtable();
|
||||
|
||||
Context context = Context.enter();
|
||||
|
@ -201,19 +201,21 @@ public final class RhinoCore {
|
|||
// or it has changed...
|
||||
setParentPrototype(prototype, op);
|
||||
|
||||
info.error = null;
|
||||
|
||||
// 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);
|
||||
evaluate(prototype, info, 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);
|
||||
evaluate(prototype, info, code);
|
||||
}
|
||||
|
||||
// loop through function properties defined for this proto and
|
||||
|
@ -355,6 +357,19 @@ public final class RhinoCore {
|
|||
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
|
||||
* 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.
|
||||
*/
|
||||
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) {
|
||||
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
|
||||
* representation.
|
||||
|
@ -553,7 +551,6 @@ public final class RhinoCore {
|
|||
* Get a script wrapper for an instance of helma.objectmodel.INode
|
||||
*/
|
||||
public Scriptable getNodeWrapper(INode n) {
|
||||
// FIXME: is this needed? should this return ESNull.theNull?
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -561,39 +558,33 @@ public final class RhinoCore {
|
|||
HopObject esn = (HopObject) wrappercache.get(n);
|
||||
|
||||
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.
|
||||
// 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 those nodes.
|
||||
if ((protoname != null) && (protoname.length() > 0) &&
|
||||
(n.getDbMapping() == null)) {
|
||||
n.setDbMapping(app.getDbMapping(protoname));
|
||||
}
|
||||
// set the DbMapping of the node according to its prototype.
|
||||
// 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 those nodes.
|
||||
/* if ((protoname != null) && (protoname.length() > 0) &&
|
||||
(n.getDbMapping() == null)) {
|
||||
n.setDbMapping(app.getDbMapping(protoname));
|
||||
} */
|
||||
|
||||
op = getPrototype(protoname);
|
||||
Scriptable op = null;
|
||||
|
||||
// no prototype found for this node?
|
||||
if (op == null) {
|
||||
op = getPrototype("hopobject");
|
||||
protoname = "hopobject";
|
||||
}
|
||||
op = getValidPrototype(protoname);
|
||||
|
||||
esn = new HopObject(protoname);
|
||||
esn.init(this, n);
|
||||
esn.setPrototype(op);
|
||||
|
||||
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());
|
||||
// no prototype found for this node?
|
||||
if (op == null) {
|
||||
op = getValidPrototype("hopobject");
|
||||
protoname = "hopobject";
|
||||
}
|
||||
|
||||
esn = new HopObject(protoname);
|
||||
esn.init(this, n);
|
||||
esn.setPrototype(op);
|
||||
|
||||
wrappercache.put(n, esn);
|
||||
}
|
||||
|
||||
return esn;
|
||||
|
@ -644,7 +635,7 @@ public final class RhinoCore {
|
|||
// 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) {
|
||||
FunctionFile funcfile = (FunctionFile) code;
|
||||
File file = funcfile.getFile();
|
||||
|
@ -653,21 +644,21 @@ public final class RhinoCore {
|
|||
try {
|
||||
FileReader fr = new FileReader(file);
|
||||
|
||||
updateEvaluator(prototype, op, fr, funcfile.getSourceName(), 1);
|
||||
updateEvaluator(prototype, info, 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);
|
||||
updateEvaluator(prototype, info, 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),
|
||||
updateEvaluator(prototype, info, new StringReader(fa.function),
|
||||
action.getSourceName(), 0);
|
||||
} catch (Exception 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) {
|
||||
// System.err.println("UPDATE EVALUATOR: "+prototype+" - "+sourceName);
|
||||
try {
|
||||
// get the current context
|
||||
Context cx = Context.getCurrentContext();
|
||||
|
||||
Scriptable op = info.objectPrototype;
|
||||
|
||||
// do the update, evaluating the file
|
||||
cx.evaluateReader(op, reader, sourceName, firstline, null);
|
||||
|
||||
} catch (Throwable e) {
|
||||
app.logEvent("Error parsing file " + sourceName + ": " + e);
|
||||
} catch (Exception e) {
|
||||
app.logEvent("Error parsing file " + sourceName + ": " + e.getClass());
|
||||
// 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);
|
||||
}
|
||||
// mark prototype as broken
|
||||
if (info.error == null && e instanceof EcmaError) {
|
||||
info.error = (EcmaError) e;
|
||||
wrappercache.clear();
|
||||
}
|
||||
// e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
|
@ -714,17 +712,24 @@ public final class RhinoCore {
|
|||
* TypeInfo helper class
|
||||
*/
|
||||
class TypeInfo {
|
||||
|
||||
// the object prototype for this type
|
||||
ScriptableObject objectPrototype;
|
||||
|
||||
// timestamp of last update
|
||||
long lastUpdate = 0;
|
||||
|
||||
// the prototype name
|
||||
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;
|
||||
|
||||
EcmaError error;
|
||||
|
||||
public TypeInfo(ScriptableObject op, String name) {
|
||||
objectPrototype = op;
|
||||
protoName = name;
|
||||
|
@ -747,7 +752,7 @@ public final class RhinoCore {
|
|||
*/
|
||||
class WrapMaker extends WrapFactory {
|
||||
|
||||
public Object wrap(Context cx, Scriptable scope, Object obj, Class staticType) {
|
||||
public Object wrap(Context cx, Scriptable scope, Object obj, Class staticType) {
|
||||
// System.err.println ("Wrapping: "+obj);
|
||||
if (obj instanceof INode) {
|
||||
return getNodeWrapper((INode) obj);
|
||||
|
|
|
@ -297,6 +297,8 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
String msg;
|
||||
if (x instanceof JavaScriptException) {
|
||||
msg = ((JavaScriptException) x).getValue().toString();
|
||||
} else if (x instanceof WrappedException) {
|
||||
msg = ((WrappedException) x).getWrappedException().toString();
|
||||
} else {
|
||||
msg = x.toString();
|
||||
}
|
||||
|
@ -336,14 +338,15 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
*/
|
||||
public boolean hasFunction(Object obj, String fname) {
|
||||
// System.err.println ("HAS_FUNC: "+obj+"."+fname);
|
||||
if (obj instanceof Scriptable) {
|
||||
Scriptable scrpt = (Scriptable) obj;
|
||||
Object func = scrpt.get(fname, scrpt);
|
||||
if (func != null && func instanceof Function) {
|
||||
return true;
|
||||
}
|
||||
Scriptable op = obj == null ? global : Context.toObject(obj, global);
|
||||
|
||||
Object func = ScriptableObject.getProperty(op, fname.replace('.', '_'));
|
||||
|
||||
if (func != null && func != Undefined.instance && func instanceof Function) {
|
||||
return true;
|
||||
}
|
||||
return core.hasFunction(app.getPrototypeName(obj), fname.replace('.', '_'));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue