* 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:
parent
f4eb8a9735
commit
cb92ec8469
6 changed files with 96 additions and 33 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue