* 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:
hns 2003-09-05 19:53:07 +00:00
parent 6eb8cf9442
commit 5577263f66
2 changed files with 83 additions and 75 deletions

View file

@ -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);

View file

@ -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;
}
/**