* Add boolean resolve argument to ScriptingEngine.invoke() to tell the engine if

functionName argument should be resolved as member expression. Use this feature
  to allow calling nested/deep functions in internal invocations (e.g. scheduler calls).
  Fixes bug 290.
This commit is contained in:
hns 2006-01-11 15:28:52 +00:00
parent f4eb8a9735
commit cb92ec8469
6 changed files with 96 additions and 33 deletions

View file

@ -330,8 +330,6 @@ public final class RequestEvaluator implements Runnable {
// set the req.action property, cutting off the _action suffix // set the req.action property, cutting off the _action suffix
req.setAction(action); req.setAction(action);
// make sure we have a valid function name by replacing dots with underscores
action = action.replace('.', '_');
// reset skin recursion detection counter // reset skin recursion detection counter
skinDepth = 0; skinDepth = 0;
@ -344,7 +342,8 @@ public final class RequestEvaluator implements Runnable {
scriptingEngine.invoke(currentElement, scriptingEngine.invoke(currentElement,
"onRequest", "onRequest",
new Object[0], new Object[0],
ScriptingEngine.ARGS_WRAP_DEFAULT); ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
} }
} catch (RedirectException redir) { } catch (RedirectException redir) {
throw redir; throw redir;
@ -360,13 +359,18 @@ public final class RequestEvaluator implements Runnable {
.getInputStream()); .getInputStream());
Vector args = xreq.getParameters(); Vector args = xreq.getParameters();
args.add(0, xreq.getMethodName()); args.add(0, xreq.getMethodName());
result = scriptingEngine.invoke(currentElement, action, result = scriptingEngine.invoke(currentElement,
args.toArray(), ScriptingEngine.ARGS_WRAP_XMLRPC); action,
args.toArray(),
ScriptingEngine.ARGS_WRAP_XMLRPC,
false);
res.writeXmlRpcResponse(result); res.writeXmlRpcResponse(result);
} else { } else {
scriptingEngine.invoke(currentElement, action, scriptingEngine.invoke(currentElement,
new Object[0], action,
ScriptingEngine.ARGS_WRAP_DEFAULT); new Object[0],
ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
} }
} catch (RedirectException redirect) { } catch (RedirectException redirect) {
// if there is a message set, save it on the user object for the next request // if there is a message set, save it on the user object for the next request
@ -420,8 +424,10 @@ public final class RequestEvaluator implements Runnable {
// reset skin recursion detection counter // reset skin recursion detection counter
skinDepth = 0; skinDepth = 0;
result = scriptingEngine.invoke(currentElement, functionName, args, result = scriptingEngine.invoke(currentElement,
ScriptingEngine.ARGS_WRAP_XMLRPC); functionName, args,
ScriptingEngine.ARGS_WRAP_XMLRPC,
false);
commitTransaction(); commitTransaction();
} catch (Exception x) { } catch (Exception x) {
abortTransaction(); abortTransaction();
@ -475,8 +481,11 @@ public final class RequestEvaluator implements Runnable {
// reset skin recursion detection counter // reset skin recursion detection counter
skinDepth = 0; skinDepth = 0;
result = scriptingEngine.invoke(thisObject, functionName, args, result = scriptingEngine.invoke(thisObject,
ScriptingEngine.ARGS_WRAP_DEFAULT); functionName,
args,
ScriptingEngine.ARGS_WRAP_DEFAULT,
true);
commitTransaction(); commitTransaction();
} catch (Exception x) { } catch (Exception x) {
abortTransaction(); abortTransaction();
@ -836,7 +845,8 @@ public final class RequestEvaluator implements Runnable {
*/ */
public Object invokeDirectFunction(Object obj, String functionName, Object[] args) public Object invokeDirectFunction(Object obj, String functionName, Object[] args)
throws Exception { throws Exception {
return scriptingEngine.invoke(obj, functionName, args, ScriptingEngine.ARGS_WRAP_DEFAULT); return scriptingEngine.invoke(obj, functionName, args,
ScriptingEngine.ARGS_WRAP_DEFAULT, false);
} }
/** /**
@ -961,7 +971,7 @@ public final class RequestEvaluator implements Runnable {
private Object getChildElement(Object obj, String name) throws ScriptingException { private Object getChildElement(Object obj, String name) throws ScriptingException {
if (scriptingEngine.hasFunction(obj, "getChildElement")) { if (scriptingEngine.hasFunction(obj, "getChildElement")) {
return scriptingEngine.invoke(obj, "getChildElement", new Object[] {name}, return scriptingEngine.invoke(obj, "getChildElement", new Object[] {name},
ScriptingEngine.ARGS_WRAP_DEFAULT); ScriptingEngine.ARGS_WRAP_DEFAULT, false);
} }
if (obj instanceof IPathElement) { if (obj instanceof IPathElement) {

View file

@ -88,12 +88,14 @@ public interface ScriptingEngine {
* one of <code>ARGS_WRAP_NONE</code>, * one of <code>ARGS_WRAP_NONE</code>,
* <code>ARGS_WRAP_DEFAULT</code>, * <code>ARGS_WRAP_DEFAULT</code>,
* <code>ARGS_WRAP_XMLRPC</code> * <code>ARGS_WRAP_XMLRPC</code>
* @param resolve indicates whether functionName may contain an object path
* or just the plain function name
* @return the return value of the function * @return the return value of the function
* @throws ScriptingException to indicate something went wrong * @throws ScriptingException to indicate something went wrong
* with the invocation * with the invocation
*/ */
public Object invoke(Object thisObject, String functionName, public Object invoke(Object thisObject, String functionName,
Object[] args, int argsWrapMode) Object[] args, int argsWrapMode, boolean resolve)
throws ScriptingException; throws ScriptingException;

View file

@ -147,7 +147,10 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
hobj.init(core, node); hobj.init(core, node);
if (proto != null) { if (proto != null) {
engine.invoke(hobj, "__constructor__", args, ScriptingEngine.ARGS_WRAP_NONE); engine.invoke(hobj,
"__constructor__",
args, ScriptingEngine.ARGS_WRAP_NONE,
false);
} }
return hobj; return hobj;

View file

@ -28,11 +28,27 @@ import helma.scripting.ScriptingException;
* @see helma.main.launcher.FilteredClassLoader * @see helma.main.launcher.FilteredClassLoader
*/ */
public final class PhantomEngine extends RhinoEngine { public final class PhantomEngine extends RhinoEngine {
/** /**
* Invoke a function on some object, using the given arguments and global vars.
* XML-RPC calls require special input and output parameter conversion.
* *
* @param thisObject the object to invoke the function on, or null for
* global functions
* @param functionName the name of the function to be invoked
* @param args array of argument objects
* @param argsWrapMode indicated the way to process the arguments. Must be
* one of <code>ARGS_WRAP_NONE</code>,
* <code>ARGS_WRAP_DEFAULT</code>,
* <code>ARGS_WRAP_XMLRPC</code>
* @param resolve indicates whether functionName may contain an object path
* or just the plain function name
* @return the return value of the function
* @throws ScriptingException to indicate something went wrong
* with the invocation
*/ */
public Object invoke(Object thisObject, String functionName, Object[] args, public Object invoke(Object thisObject, String functionName, Object[] args,
int argsWrapMode) throws ScriptingException { int argsWrapMode, boolean resolve) throws ScriptingException {
return super.invoke(thisObject, functionName, args, argsWrapMode); return super.invoke(thisObject, functionName, args, argsWrapMode, resolve);
} }
} }

View file

@ -678,7 +678,8 @@ public final class RhinoCore implements ScopeProvider {
try { try {
result = engine.invoke(handler, hrefFunction, result = engine.invoke(handler, hrefFunction,
new Object[] { basicHref }, new Object[] { basicHref },
ScriptingEngine.ARGS_WRAP_DEFAULT); ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
} catch (ScriptingException x) { } catch (ScriptingException x) {
throw new EvaluatorException("Error in hrefFunction: " + x); throw new EvaluatorException("Error in hrefFunction: " + x);
} }

View file

@ -255,16 +255,24 @@ public class RhinoEngine implements ScriptingEngine {
/** /**
* Invoke a function on some object, using the given arguments and global vars. * Invoke a function on some object, using the given arguments and global vars.
* XML-RPC calls require special input and output parameter conversion.
*
* @param thisObject the object to invoke the function on, or null for
* global functions
* @param functionName the name of the function to be invoked
* @param args array of argument objects
* @param argsWrapMode indicated the way to process the arguments. Must be
* one of <code>ARGS_WRAP_NONE</code>,
* <code>ARGS_WRAP_DEFAULT</code>,
* <code>ARGS_WRAP_XMLRPC</code>
* @param resolve indicates whether functionName may contain an object path
* or just the plain function name
* @return the return value of the function
* @throws ScriptingException to indicate something went wrong
* with the invocation
*/ */
public Object invoke(Object thisObject, String functionName, Object[] args, public Object invoke(Object thisObject, String functionName, Object[] args,
int argsWrapMode) throws ScriptingException { int argsWrapMode, boolean resolve) throws ScriptingException {
Scriptable eso = null;
if (thisObject == null) {
eso = global;
} else {
eso = Context.toObject(thisObject, global);
}
try { try {
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
switch (argsWrapMode) { switch (argsWrapMode) {
@ -279,15 +287,37 @@ public class RhinoEngine implements ScriptingEngine {
args[i] = core.processXmlRpcArgument(args[i]); args[i] = core.processXmlRpcArgument(args[i]);
break; break;
} }
}
Object f = ScriptableObject.getProperty(eso, functionName.replace('.', '_')); }
Scriptable obj = thisObject == null ? global : Context.toObject(thisObject, global);
// if function name should be resolved interpret it as member expression,
// otherwise replace dots with underscores.
if (resolve) {
if (functionName.indexOf('.') > 0) {
StringTokenizer st = new StringTokenizer(functionName, ".");
for (int i=0; i<st.countTokens()-1; i++) {
String propName = st.nextToken();
Object propValue = ScriptableObject.getProperty(obj, propName);
if (propValue instanceof Scriptable) {
obj = (Scriptable) propValue;
} else {
throw new RuntimeException("Can't resolve function name " +
functionName + " in " + thisObject);
}
}
functionName = st.nextToken();
}
} else {
functionName = functionName.replace('.', '_');
}
Object f = ScriptableObject.getProperty(obj, functionName);
if ((f == ScriptableObject.NOT_FOUND) || !(f instanceof Function)) { if ((f == ScriptableObject.NOT_FOUND) || !(f instanceof Function)) {
return null; return null;
} }
Object retval = ((Function) f).call(context, global, eso, args); Object retval = ((Function) f).call(context, global, obj, args);
if (retval instanceof Wrapper) { if (retval instanceof Wrapper) {
retval = ((Wrapper) retval).unwrap(); retval = ((Wrapper) retval).unwrap();
@ -372,16 +402,18 @@ public class RhinoEngine implements ScriptingEngine {
* is a java object) with that name. * is a java object) with that name.
*/ */
public boolean hasFunction(Object obj, String fname) { public boolean hasFunction(Object obj, String fname) {
// Convert '.' to '_' in function name
fname = fname.replace('.', '_');
// Treat HopObjects separately - otherwise we risk to fetch database // Treat HopObjects separately - otherwise we risk to fetch database
// references/child objects just to check for function properties. // references/child objects just to check for function properties.
if (obj instanceof INode) { if (obj instanceof INode) {
String protoname = ((INode) obj).getPrototype(); String protoname = ((INode) obj).getPrototype();
return core.hasFunction(protoname, fname.replace('.', '_')); return core.hasFunction(protoname, fname);
} }
Scriptable op = obj == null ? global : Context.toObject(obj, global); Scriptable op = obj == null ? global : Context.toObject(obj, global);
Object func = ScriptableObject.getProperty(op, fname.replace('.', '_')); Object func = ScriptableObject.getProperty(op, fname);
if (func != null && func != Undefined.instance && func instanceof Function) { if (func != null && func != Undefined.instance && func instanceof Function) {
return true; return true;
@ -395,7 +427,6 @@ public class RhinoEngine implements ScriptingEngine {
* is a java object) with that name. * is a java object) with that name.
*/ */
public Object get(Object obj, String propname) { public Object get(Object obj, String propname) {
// System.err.println ("GET: "+propname);
if ((obj == null) || (propname == null)) { if ((obj == null) || (propname == null)) {
return null; return null;
} }