Intercept field access of java objects in Rhino
Asked Answered
P

2

6

Is it possible to intercept field (and method) access of Java objects in the executed JavaScript code? Just like the delegate in JSCocoa that allows to handle property and method access.

Prefect answered 7/5, 2012 at 13:58 Comment(0)
N
5

You can use Context.setWrapFactory to specify how to wrap Java Objects. Here shows wrappers that prints field access and method call:

InterceptWrapFactory.java

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.WrapFactory;
public class InterceptWrapFactory extends WrapFactory{
    @Override
    public Scriptable wrapAsJavaObject(Context cx, Scriptable scope,
            Object javaObject, Class<?> staticType) {
        return new InterceptNativeObject(scope, javaObject, staticType);
    }
}

InterceptNativeObject.java

import org.mozilla.javascript.NativeJavaMethod;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.Scriptable;
public class InterceptNativeObject extends NativeJavaObject {
    @Override
    public Object get(String name, Scriptable start) {
        Object res = super.get(name, start);
        System.out.println("Field get name="+name+" result="+res);
        if (res instanceof NativeJavaMethod) {
            NativeJavaMethod method = (NativeJavaMethod) res;
            return new JavaMethodWrapper(method);
        }
        return res;
    }
    public InterceptNativeObject(Scriptable scope, Object javaObject,
            Class<?> staticType) {
        super(scope, javaObject, staticType);
    }
}

JavaMethodWrapper.java

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeJavaMethod;
import org.mozilla.javascript.Scriptable;

public class JavaMethodWrapper implements Function {
    NativeJavaMethod method;
    public JavaMethodWrapper(NativeJavaMethod method) {
        this.method = method;
    }
    public boolean hasInstance(Scriptable instance) {
        return method.hasInstance(instance);
    }
    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
            Object[] args) {
        System.out.println("Call method: "+method);
        return method.call(cx, scope, thisObj, args);
    }
    public boolean has(int index, Scriptable start) {
        return method.has(index, start);
    }
    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
        return method.construct(cx, scope, args);
    }
    public void put(int index, Scriptable start, Object value) {
        method.put(index, start, value);
    }
    public void delete(int index) {
        method.delete(index);
    }
    public Scriptable createObject(Context cx, Scriptable scope) {
        return method.createObject(cx, scope);
    }
    public boolean has(String name, Scriptable start) {
        return method.has(name, start);
    }
    public void defineConst(String name, Scriptable start) {
        method.defineConst(name, start);
    }
    public void put(String name, Scriptable start, Object value) {
        method.put(name, start, value);
    }
    public void delete(String name) {
        method.delete(name);
    }
    public Scriptable getPrototype() {
        return method.getPrototype();
    }
    public void setPrototype(Scriptable m) {
        method.setPrototype(m);
    }
    public Scriptable getParentScope() {
        return method.getParentScope();
    }
    public void setParentScope(Scriptable m) {
        method.setParentScope(m);
    }
    public Object[] getIds() {
        return method.getIds();
    }
    public Object get(int index, Scriptable start) {
        return method.get(index, start);
    }
    public Object get(String name, Scriptable start) {
        return method.get(name, start);
    }
    public String getClassName() {
        return method.getClassName();
    }
    public Object getDefaultValue(Class<?> typeHint) {
        return method.getDefaultValue(typeHint);
    }
}

And this is a test code:

import java.util.Vector;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;


public class InterceptTest {
    public static void main(String[] args) {
        Context cx=Context.enter();
        cx.setWrapFactory(new InterceptWrapFactory());
        Scriptable root=cx.initStandardObjects();
        ScriptableObject.putProperty(root,"v", new Vector<String>());
        cx.evaluateString(root, "v.add('foo'); v.get(0)", "src", 1, null);
    }
}
Nelson answered 11/5, 2012 at 14:49 Comment(1)
Thanks a lot, this is just what I was looking for. I'm curious, where did you find information on the topic?Prefect
P
0

As an alternative to what hog1e3 suggested it is also possible to implement the Scriptable interface to intercept calls to methods/fields of a class.

Prefect answered 16/5, 2012 at 7:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.