* 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
req.setAction(action);
// make sure we have a valid function name by replacing dots with underscores
action = action.replace('.', '_');
// reset skin recursion detection counter
skinDepth = 0;
@ -344,7 +342,8 @@ public final class RequestEvaluator implements Runnable {
scriptingEngine.invoke(currentElement,
"onRequest",
new Object[0],
ScriptingEngine.ARGS_WRAP_DEFAULT);
ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
}
} catch (RedirectException redir) {
throw redir;
@ -360,13 +359,18 @@ public final class RequestEvaluator implements Runnable {
.getInputStream());
Vector args = xreq.getParameters();
args.add(0, xreq.getMethodName());
result = scriptingEngine.invoke(currentElement, action,
args.toArray(), ScriptingEngine.ARGS_WRAP_XMLRPC);
result = scriptingEngine.invoke(currentElement,
action,
args.toArray(),
ScriptingEngine.ARGS_WRAP_XMLRPC,
false);
res.writeXmlRpcResponse(result);
} else {
scriptingEngine.invoke(currentElement, action,
new Object[0],
ScriptingEngine.ARGS_WRAP_DEFAULT);
scriptingEngine.invoke(currentElement,
action,
new Object[0],
ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
}
} catch (RedirectException redirect) {
// 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
skinDepth = 0;
result = scriptingEngine.invoke(currentElement, functionName, args,
ScriptingEngine.ARGS_WRAP_XMLRPC);
result = scriptingEngine.invoke(currentElement,
functionName, args,
ScriptingEngine.ARGS_WRAP_XMLRPC,
false);
commitTransaction();
} catch (Exception x) {
abortTransaction();
@ -475,8 +481,11 @@ public final class RequestEvaluator implements Runnable {
// reset skin recursion detection counter
skinDepth = 0;
result = scriptingEngine.invoke(thisObject, functionName, args,
ScriptingEngine.ARGS_WRAP_DEFAULT);
result = scriptingEngine.invoke(thisObject,
functionName,
args,
ScriptingEngine.ARGS_WRAP_DEFAULT,
true);
commitTransaction();
} catch (Exception x) {
abortTransaction();
@ -836,7 +845,8 @@ public final class RequestEvaluator implements Runnable {
*/
public Object invokeDirectFunction(Object obj, String functionName, Object[] args)
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 {
if (scriptingEngine.hasFunction(obj, "getChildElement")) {
return scriptingEngine.invoke(obj, "getChildElement", new Object[] {name},
ScriptingEngine.ARGS_WRAP_DEFAULT);
ScriptingEngine.ARGS_WRAP_DEFAULT, false);
}
if (obj instanceof IPathElement) {

View file

@ -88,12 +88,14 @@ public interface ScriptingEngine {
* 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, int argsWrapMode)
Object[] args, int argsWrapMode, boolean resolve)
throws ScriptingException;

View file

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

View file

@ -28,11 +28,27 @@ import helma.scripting.ScriptingException;
* @see helma.main.launcher.FilteredClassLoader
*/
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,
int argsWrapMode) throws ScriptingException {
return super.invoke(thisObject, functionName, args, argsWrapMode);
int argsWrapMode, boolean resolve) throws ScriptingException {
return super.invoke(thisObject, functionName, args, argsWrapMode, resolve);
}
}

View file

@ -678,7 +678,8 @@ public final class RhinoCore implements ScopeProvider {
try {
result = engine.invoke(handler, hrefFunction,
new Object[] { basicHref },
ScriptingEngine.ARGS_WRAP_DEFAULT);
ScriptingEngine.ARGS_WRAP_DEFAULT,
false);
} catch (ScriptingException 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.
* 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,
int argsWrapMode) throws ScriptingException {
Scriptable eso = null;
if (thisObject == null) {
eso = global;
} else {
eso = Context.toObject(thisObject, global);
}
int argsWrapMode, boolean resolve) throws ScriptingException {
try {
for (int i = 0; i < args.length; i++) {
switch (argsWrapMode) {
@ -279,15 +287,37 @@ public class RhinoEngine implements ScriptingEngine {
args[i] = core.processXmlRpcArgument(args[i]);
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)) {
return null;
}
Object retval = ((Function) f).call(context, global, eso, args);
Object retval = ((Function) f).call(context, global, obj, args);
if (retval instanceof Wrapper) {
retval = ((Wrapper) retval).unwrap();
@ -372,16 +402,18 @@ public class RhinoEngine implements ScriptingEngine {
* is a java object) with that name.
*/
public boolean hasFunction(Object obj, String fname) {
// Convert '.' to '_' in function name
fname = fname.replace('.', '_');
// Treat HopObjects separately - otherwise we risk to fetch database
// references/child objects just to check for function properties.
if (obj instanceof INode) {
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);
Object func = ScriptableObject.getProperty(op, fname.replace('.', '_'));
Object func = ScriptableObject.getProperty(op, fname);
if (func != null && func != Undefined.instance && func instanceof Function) {
return true;
@ -395,7 +427,6 @@ public class RhinoEngine implements ScriptingEngine {
* is a java object) with that name.
*/
public Object get(Object obj, String propname) {
// System.err.println ("GET: "+propname);
if ((obj == null) || (propname == null)) {
return null;
}