* 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
|
// 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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue