Fix for the following problem:

I'm having problems getting inner class objects with Rhino.

I create a Hashmap, which is an implementation of Map. Map.Entry is an
inner interface of Map with key-value pairs. If I have a Map object,
"property", I should be able to get the key element with the expression
"property.key".

When I look at the "property" class name that Rhino returns I get:
"java.util.HashMap$Entry". I don't believe Rhino has a notion of the
inner Map.Entry object. The expression "property" succeeds. The
expression "property.key", which should retrieve the Map.Entry
keyValue(), fails with a "unexpected IllegalAccessException accessing
Java field".

I'm including a simple example that illustrates the problem. I hope you
can shed some light on this. Thanks!

Justyna
< Justyna.Horwat@Sun.com >

----
import java.io.*;
import java.util.*;
import org.mozilla.javascript.*;


public class MapTest {

    public static void main(String argv[]) {
        Test test = new Test();
        test.testMap();
    }
}

class Test {
    Map map;
    Set set;
    Iterator it;
    Map.Entry entry;

    public void testMap() {
System.out.println("testMap");
        map = new HashMap();

        populate();

        set = map.entrySet();
        it = set.iterator();

        // let's see if Map is populated correctly
        while (it.hasNext()) {
            entry = (Map.Entry) it.next();
            System.out.println("entry: " + entry.getClass().getName());
            System.out.println("key: " + entry.getKey());
            System.out.println("value: " + entry.getValue());
        }

        evaluate();
    }

    void populate() {
        map.put("firstKey", "firstValue");
        map.put("secondKey", "secondValue");
        map.put("thirdKey", "thirdValue");
        map.put("fourthKey", "fourthValue");
    }

    public void evaluate() {

        Context cx = Context.enter();
        Scriptable scope = cx.initStandardObjects(null);

        set = map.entrySet();
        it = set.iterator();

        while (it.hasNext()) {
            entry = (Map.Entry) it.next();
            scope.put("property", scope, cx.toObject(entry,scope));
        }

        Object eval = null;

        try {
            // attempt to get Map.Entry key value using Rhino
            eval = cx.evaluateString(scope, "property.key", "", 0,
null);
            // Unwrap scoped object
            if (eval instanceof Wrapper)
                eval = ((Wrapper) eval).unwrap();

        } catch (JavaScriptException jse) {
            System.out.println("EXCEPTION: " + jse.getMessage());
        }

        // DELETE
        System.out.println("RHINO result: " + eval + ":");
        System.out.println("RHINO class: " + eval.getClass().getName());
    }
}
This commit is contained in:
nboyd%atg.com 2001-10-07 18:42:09 +00:00
Родитель 7d17e005e1
Коммит 6642d0e4f2
2 изменённых файлов: 88 добавлений и 55 удалений

Просмотреть файл

@ -91,7 +91,15 @@ class JavaMembers {
try {
if (member instanceof BeanProperty) {
BeanProperty bp = (BeanProperty) member;
rval = bp.getter.invoke(javaObject, ScriptRuntime.emptyArgs);
try {
rval = bp.getter.invoke(javaObject, null);
} catch (IllegalAccessException e) {
rval = NativeJavaMethod.retryIllegalAccessInvoke(
bp.getter,
javaObject,
null,
e);
}
type = bp.getter.getReturnType();
} else {
Field field = (Field) member;

Просмотреть файл

@ -220,69 +220,94 @@ public class NativeJavaMethod extends NativeFunction implements Function {
}
javaObject = ((Wrapper) o).unwrap();
}
retry:
for (int attempt=0; ; attempt++) {
try {
if (debug) {
printDebug("Calling ", meth, args);
}
Object retval;
try {
if (debug) {
printDebug("Calling ", meth, args);
}
retval = meth.invoke(javaObject, args);
} catch (IllegalAccessException e) {
retval = retryIllegalAccessInvoke(meth, javaObject, args, e);
}
Class staticType = meth.getReturnType();
Object retval = meth.invoke(javaObject, args);
Class staticType = meth.getReturnType();
if (debug) {
Class actualType = (retval == null) ? null
: retval.getClass();
System.err.println(" ----- Returned " + retval +
" actual = " + actualType +
" expect = " + staticType);
}
if (debug) {
Class actualType = (retval == null) ? null
: retval.getClass();
System.err.println(" ----- Returned " + retval +
" actual = " + actualType +
" expect = " + staticType);
}
Object wrapped = NativeJavaObject.wrap(scope, retval, staticType);
Object wrapped = NativeJavaObject.wrap(scope, retval, staticType);
if (debug) {
Class actualType = (wrapped == null) ? null
: wrapped.getClass();
System.err.println(" ----- Wrapped as " + wrapped +
" class = " + actualType);
}
if (debug) {
Class actualType = (wrapped == null) ? null
: wrapped.getClass();
System.err.println(" ----- Wrapped as " + wrapped +
" class = " + actualType);
}
if (wrapped == Undefined.instance)
return wrapped;
if (wrapped == null && staticType == Void.TYPE)
return Undefined.instance;
if (wrapped == Undefined.instance)
return wrapped;
} catch (IllegalAccessException accessEx) {
if (Modifier.isPublic(meth.getModifiers()) && attempt == 0) {
/**
* Due to a bug in Suns VM, public methods in private
* classes are not accessible by default (Sun Bug #4071593).
* We have to explicitly set the method accessible
* via meth.setAccessible(true) but we have to use
* reflection because the setAccessible() in Method is
* not available under jdk 1.1. We wait until a failure
* to retry to avoid the overhead of this call on cases
* that don't require it.
*/
if (method_setAccessible != null) {
Object[] args_wrapper = { Boolean.TRUE };
try {
method_setAccessible.invoke(meth, args_wrapper);
}
catch (IllegalAccessException ex) { }
catch (IllegalArgumentException ex) { }
catch (InvocationTargetException ex) { }
}
continue retry;
if (wrapped == null && staticType == Void.TYPE)
return Undefined.instance;
return wrapped;
} catch (IllegalAccessException accessEx) {
throw Context.reportRuntimeError(
"While attempting to call \"" + meth.getName() +
"\" in class \"" + meth.getDeclaringClass().getName() +
"\" receieved " + accessEx.toString());
} catch (InvocationTargetException e) {
throw JavaScriptException.wrapException(scope, e);
}
}
static Object retryIllegalAccessInvoke(Method method, Object obj,
Object[] args,
IllegalAccessException illegalAccess)
throws IllegalAccessException, InvocationTargetException
{
if (Modifier.isPublic(method.getModifiers())) {
String name = method.getName();
Class[] parms = method.getParameterTypes();
Class c = method.getDeclaringClass();
Class[] intfs = c.getInterfaces();
for (int i=0; i < intfs.length; i++) {
c = intfs[i];
try {
Method m = c.getMethod(name, parms);
return m.invoke(obj, args);
} catch (NoSuchMethodException ex) {
continue;
} catch (IllegalAccessException ex) {
continue;
}
throw Context.reportRuntimeError(
"While attempting to call \"" + meth.getName() +
"\" in class \"" + meth.getDeclaringClass().getName() +
"\" receieved " + accessEx.toString());
} catch (InvocationTargetException e) {
throw JavaScriptException.wrapException(scope, e);
}
}
/**
* Due to a bug in Sun's VM, public methods in private
* classes are not accessible by default (Sun Bug #4071593).
* We have to explicitly set the method accessible
* via method.setAccessible(true) but we have to use
* reflection because the setAccessible() in Method is
* not available under jdk 1.1. We wait until a failure
* to retry to avoid the overhead of this call on cases
* that don't require it.
*/
if (method_setAccessible != null) {
Object[] args_wrapper = { Boolean.TRUE };
try {
method_setAccessible.invoke(method, args_wrapper);
}
catch (IllegalAccessException ex) { }
catch (IllegalArgumentException ex) { }
catch (InvocationTargetException ex) { }
return method.invoke(obj, args);
}
throw illegalAccess;
}
/**