New class MemberBox wraps Method or Constructor instances to cache results of getParameterType() and other information and to to replace Method instance by more accessible one recovery from IllegalAccessException is possible.
This commit is contained in:
igor%mir2.org 2003-07-15 20:47:29 +00:00
parent 697f7be97f
commit 004397fe5e
6 changed files with 529 additions and 416 deletions

View File

@ -67,8 +67,7 @@ class JavaMembers
if (obj != null) {
return true;
} else {
Member member = this.findExplicitFunction(name, isStatic);
return member != null;
return null != findExplicitFunction(name, isStatic);
}
}
@ -95,16 +94,8 @@ class JavaMembers
try {
if (member instanceof BeanProperty) {
BeanProperty bp = (BeanProperty) member;
try {
rval = bp.getter.invoke(javaObject, null);
} catch (IllegalAccessException e) {
rval = NativeJavaMethod.retryIllegalAccessInvoke(
bp.getter,
javaObject,
null,
e);
}
type = bp.getter.getReturnType();
rval = bp.getter.invoke(javaObject, null);
type = bp.getter.method().getReturnType();
} else {
Field field = (Field) member;
rval = field.get(isStatic ? null : javaObject);
@ -118,84 +109,6 @@ class JavaMembers
return cx.getWrapFactory().wrap(cx, scope, rval, type);
}
private Member findExplicitFunction(String name, boolean isStatic)
{
int sigStart = name.indexOf('(');
if (sigStart < 0) { return null; }
Hashtable ht = isStatic ? staticMembers : members;
Member[] methodsOrCtors = null;
Class[][] methodOrCtorTypes = null;
boolean isCtor = (isStatic && sigStart == 0);
if (isCtor) {
// Explicit request for an overloaded constructor
methodsOrCtors = ctors;
methodOrCtorTypes = ctorTypes;
} else {
// Explicit request for an overloaded method
String trueName = name.substring(0,sigStart);
Object obj = ht.get(trueName);
if (!isStatic && obj == null) {
// Try to get static member from instance (LC3)
obj = staticMembers.get(trueName);
}
if (obj instanceof NativeJavaMethod) {
NativeJavaMethod njm = (NativeJavaMethod)obj;
methodsOrCtors = njm.methods;
methodOrCtorTypes = njm.methodTypes;
}
}
if (methodsOrCtors != null) {
for (int i = 0; i < methodsOrCtors.length; i++) {
Member member = methodsOrCtors[i];
Class[] type = methodOrCtorTypes[i];
String sig = NativeJavaMethod.memberSignature(member, type);
if (name.equals(sig)) {
return member;
}
}
}
return null;
}
private Object getExplicitFunction(Scriptable scope, String name,
Object javaObject, boolean isStatic)
{
Hashtable ht = isStatic ? staticMembers : members;
Object member = null;
Member methodOrCtor = this.findExplicitFunction(name, isStatic);
if (methodOrCtor != null) {
Scriptable prototype =
ScriptableObject.getFunctionPrototype(scope);
if (methodOrCtor instanceof Constructor) {
NativeJavaConstructor fun =
new NativeJavaConstructor((Constructor)methodOrCtor);
fun.setPrototype(prototype);
member = fun;
ht.put(name, fun);
} else {
String trueName = methodOrCtor.getName();
member = ht.get(trueName);
if (member instanceof NativeJavaMethod &&
((NativeJavaMethod)member).methods.length > 1 ) {
NativeJavaMethod fun =
new NativeJavaMethod((Method)methodOrCtor, name);
fun.setPrototype(prototype);
ht.put(name, fun);
member = fun;
}
}
}
return member;
}
public void put(Scriptable scope, String name, Object javaObject,
Object value, boolean isStatic)
{
@ -214,14 +127,15 @@ class JavaMembers
// Is this a bean property "set"?
if (member instanceof BeanProperty) {
BeanProperty bp = (BeanProperty)member;
if (bp.setter == null) {
throw reportMemberNotFound(name);
}
Class setType = bp.setter.argTypes[0];
Object[] args = { NativeJavaObject.coerceType(setType, value,
true) };
try {
Method method = ((BeanProperty) member).setter;
if (method == null)
throw reportMemberNotFound(name);
Class[] types = method.getParameterTypes();
Object[] args = { NativeJavaObject.coerceType(types[0], value,
true) };
method.invoke(javaObject, args);
bp.setter.invoke(javaObject, args);
} catch (Exception ex) {
throw ScriptRuntime.throwAsUncheckedException(ex);
}
@ -260,6 +174,125 @@ class JavaMembers
return result;
}
static String javaSignature(Class type)
{
if (!type.isArray()) {
return type.getName();
} else {
int arrayDimension = 0;
do {
++arrayDimension;
type = type.getComponentType();
} while (type.isArray());
String name = type.getName();
String suffix = "[]";
if (arrayDimension == 1) {
return name.concat(suffix);
} else {
int length = name.length() + arrayDimension * suffix.length();
StringBuffer sb = new StringBuffer(length);
sb.append(name);
while (arrayDimension != 0) {
--arrayDimension;
sb.append(suffix);
}
return sb.toString();
}
}
}
static String liveConnectSignature(Class[] argTypes)
{
int N = argTypes.length;
if (N == 0) { return "()"; }
StringBuffer sb = new StringBuffer();
sb.append('(');
for (int i = 0; i != N; ++i) {
if (i != 0) {
sb.append(',');
}
sb.append(javaSignature(argTypes[i]));
}
sb.append(')');
return sb.toString();
}
private MemberBox findExplicitFunction(String name, boolean isStatic)
{
int sigStart = name.indexOf('(');
if (sigStart < 0) { return null; }
Hashtable ht = isStatic ? staticMembers : members;
MemberBox[] methodsOrCtors = null;
boolean isCtor = (isStatic && sigStart == 0);
if (isCtor) {
// Explicit request for an overloaded constructor
methodsOrCtors = ctors;
} else {
// Explicit request for an overloaded method
String trueName = name.substring(0,sigStart);
Object obj = ht.get(trueName);
if (!isStatic && obj == null) {
// Try to get static member from instance (LC3)
obj = staticMembers.get(trueName);
}
if (obj instanceof NativeJavaMethod) {
NativeJavaMethod njm = (NativeJavaMethod)obj;
methodsOrCtors = njm.methods;
}
}
if (methodsOrCtors != null) {
for (int i = 0; i < methodsOrCtors.length; i++) {
Class[] type = methodsOrCtors[i].argTypes;
String sig = liveConnectSignature(type);
if (sigStart + sig.length() == name.length()
&& name.regionMatches(sigStart, sig, 0, sig.length()))
{
return methodsOrCtors[i];
}
}
}
return null;
}
private Object getExplicitFunction(Scriptable scope, String name,
Object javaObject, boolean isStatic)
{
Hashtable ht = isStatic ? staticMembers : members;
Object member = null;
MemberBox methodOrCtor = findExplicitFunction(name, isStatic);
if (methodOrCtor != null) {
Scriptable prototype =
ScriptableObject.getFunctionPrototype(scope);
if (methodOrCtor.isCtor()) {
NativeJavaConstructor fun =
new NativeJavaConstructor(methodOrCtor);
fun.setPrototype(prototype);
member = fun;
ht.put(name, fun);
} else {
String trueName = methodOrCtor.getName();
member = ht.get(trueName);
if (member instanceof NativeJavaMethod &&
((NativeJavaMethod)member).methods.length > 1 ) {
NativeJavaMethod fun =
new NativeJavaMethod(methodOrCtor, name);
fun.setPrototype(prototype);
ht.put(name, fun);
member = fun;
}
}
}
return member;
}
private void reflect(Scriptable scope)
{
// We reflect methods first, because we want overloaded field/method
@ -388,30 +421,31 @@ class JavaMembers
continue;
// Get the method by this name.
Object method = ht.get(name);
if (!(method instanceof NativeJavaMethod))
Object member = ht.get(name);
if (!(member instanceof NativeJavaMethod))
continue;
NativeJavaMethod getJavaMethod = (NativeJavaMethod) method;
Method getMethod = extractGetMethod(getJavaMethod, isStatic);
if (getMethod != null) {
NativeJavaMethod njmGet = (NativeJavaMethod)member;
MemberBox getter = extractGetMethod(njmGet.methods, isStatic);
if (getter != null) {
// We have a getter. Now, do we have a setter?
Method setMethod = null;
String setter = "set".concat(nameComponent);
if (ht.containsKey(setter)) {
NativeJavaMethod njmSet = null;
MemberBox setter = null;
String setterName = "set".concat(nameComponent);
if (ht.containsKey(setterName)) {
// Is this value a method?
method = ht.get(setter);
if (method instanceof NativeJavaMethod) {
NativeJavaMethod njm = (NativeJavaMethod) method;
Class type = getMethod.getReturnType();
setMethod = extractSetMethod(type, njm, isStatic);
member = ht.get(setterName);
if (member instanceof NativeJavaMethod) {
njmSet = (NativeJavaMethod)member;
Class type = getter.method().getReturnType();
setter = extractSetMethod(type, njmSet.methods,
isStatic);
}
}
// Make the property.
BeanProperty bp = new BeanProperty(getMethod, setMethod);
BeanProperty bp = new BeanProperty(getter, setter);
toAdd.put(beanPropertyName, bp);
}
}
@ -427,11 +461,11 @@ class JavaMembers
private void reflectCtors()
{
ctors = cl.getConstructors();
int N = ctors.length;
ctorTypes = new Class[N][];
Constructor[] constructors = cl.getConstructors();
int N = constructors.length;
ctors = new MemberBox[N];
for (int i = 0; i != N; ++i) {
ctorTypes[i] = ctors[i].getParameterTypes();
ctors[i] = new MemberBox(constructors[i]);
}
}
@ -440,16 +474,20 @@ class JavaMembers
Enumeration e = ht.keys();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
Method[] methods;
MemberBox[] methods;
Object value = ht.get(name);
if (value instanceof Method) {
methods = new Method[1];
methods[0] = (Method)value;
methods = new MemberBox[1];
methods[0] = new MemberBox((Method)value);
} else {
ObjArray overloadedMethods = (ObjArray)value;
if (overloadedMethods.size() < 2) Context.codeBug();
methods = new Method[overloadedMethods.size()];
overloadedMethods.toArray(methods);
int N = overloadedMethods.size();
if (N < 2) Context.codeBug();
methods = new MemberBox[N];
for (int i = 0; i != N; ++i) {
Method method = (Method)overloadedMethods.get(i);
methods[i] = new MemberBox(method);
}
}
NativeJavaMethod fun = new NativeJavaMethod(methods);
if (scope != null) {
@ -474,20 +512,18 @@ class JavaMembers
return fmht;
}
private static Method extractGetMethod(NativeJavaMethod njm,
boolean isStatic)
private static MemberBox extractGetMethod(MemberBox[] methods,
boolean isStatic)
{
// Grab and inspect the getter method; does it have an empty parameter
// list with a return value (eg. a getSomething() or isSomething())?
Method[] methods = njm.methods;
if (methods.length == 1) {
Method method = methods[0];
MemberBox method = methods[0];
// Make sure the method static-ness is preserved for this property.
if (!isStatic || Modifier.isStatic(method.getModifiers())) {
Class[] params = njm.methodTypes[0];
if (params != null && params.length == 0) {
Class type = method.getReturnType();
if (type != null && type != Void.TYPE) {
if (!isStatic || method.isStatic()) {
if (method.argTypes.length == 0) {
Class type = method.method().getReturnType();
if (type != Void.TYPE) {
return method;
}
}
@ -496,9 +532,8 @@ class JavaMembers
return null;
}
private static Method extractSetMethod(Class type,
NativeJavaMethod njm,
boolean isStatic)
private static MemberBox extractSetMethod(Class type, MemberBox[] methods,
boolean isStatic)
{
//
// Note: it may be preferable to allow NativeJavaMethod.findFunction()
@ -508,14 +543,13 @@ class JavaMembers
// Make two passes: one to find a method with direct type assignment,
// and one to find a widening conversion.
Method[] methods = njm.methods;
for (int pass = 1; pass <= 2; ++pass) {
for (int i = 0; i < methods.length; ++i) {
Method method = methods[i];
if (!isStatic || Modifier.isStatic(method.getModifiers())) {
if (method.getReturnType() == Void.TYPE) {
Class[] params = njm.methodTypes[i];
if (params != null && params.length == 1) {
MemberBox method = methods[i];
if (!isStatic || method.isStatic()) {
if (method.method().getReturnType() == Void.TYPE) {
Class[] params = method.argTypes;
if (params.length == 1) {
if (pass == 1) {
if (params[0] == type) {
return method;
@ -648,26 +682,25 @@ class JavaMembers
private Hashtable fieldAndMethods;
private Hashtable staticMembers;
private Hashtable staticFieldAndMethods;
Constructor[] ctors;
Class[][] ctorTypes;
MemberBox[] ctors;
}
class BeanProperty
{
BeanProperty(Method getter, Method setter)
BeanProperty(MemberBox getter, MemberBox setter)
{
this.getter = getter;
this.setter = setter;
}
Method getter;
Method setter;
MemberBox getter;
MemberBox setter;
}
class FieldAndMethods extends NativeJavaMethod
{
FieldAndMethods(Method[] methods, Field field)
FieldAndMethods(MemberBox[] methods, Field field)
{
super(methods);
this.field = field;

View File

@ -0,0 +1,262 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
* Felix Meschberger
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript;
import java.lang.reflect.*;
import java.io.*;
/**
* Wrappper class for Method and Constructor instances to cache
* getParameterTypes() results and to recover from IllegalAccessException
* in some cases.
*
* @author Igor Bukanov
*/
final class MemberBox implements Serializable
{
MemberBox(Method method)
{
init(method);
}
MemberBox(Constructor constructor)
{
init(constructor);
}
private void init(Method method)
{
this.memberObject = method;
this.argTypes = method.getParameterTypes();
}
private void init(Constructor constructor)
{
this.memberObject = constructor;
this.argTypes = constructor.getParameterTypes();
}
Method method()
{
return (Method)memberObject;
}
Constructor ctor()
{
return (Constructor)memberObject;
}
boolean isMethod()
{
return memberObject instanceof Method;
}
boolean isCtor()
{
return memberObject instanceof Constructor;
}
boolean isStatic()
{
return Modifier.isStatic(memberObject.getModifiers());
}
String getName()
{
return memberObject.getName();
}
Class getDeclaringClass()
{
return memberObject.getDeclaringClass();
}
String toJavaDeclaration()
{
StringBuffer sb = new StringBuffer();
if (isMethod()) {
Method method = method();
sb.append(method.getReturnType());
sb.append(' ');
sb.append(method.getName());
} else {
Constructor ctor = ctor();
String name = ctor.getDeclaringClass().getName();
int lastDot = name.lastIndexOf('.');
if (lastDot >= 0) {
name = name.substring(lastDot + 1);
}
sb.append(name);
}
sb.append(JavaMembers.liveConnectSignature(argTypes));
return sb.toString();
}
public String toString()
{
return memberObject.toString();
}
Object invoke(Object target, Object[] args)
throws IllegalAccessException, InvocationTargetException
{
Method method = method();
try {
return method.invoke(target, args);
} catch (IllegalAccessException ex) {
Method accessible = searchAccessibleMethod(method, argTypes);
if (accessible != null) {
memberObject = accessible;
method = accessible;
} else {
if (!tryToMakeAccessible(method)) {
throw ex;
}
}
return method.invoke(target, args);
}
}
Object newInstance(Object[] args)
throws InvocationTargetException, IllegalAccessException,
InstantiationException
{
Constructor ctor = ctor();
try {
return ctor.newInstance(args);
} catch (IllegalAccessException ex) {
if (!tryToMakeAccessible(ctor)) {
throw ex;
}
return ctor.newInstance(args);
}
}
private static Method searchAccessibleMethod(Method method, Class[] params)
{
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
Class c = method.getDeclaringClass();
if (!Modifier.isPublic(c.getModifiers())) {
String name = method.getName();
Class[] intfs = c.getInterfaces();
for (int i = 0, N = intfs.length; i != N; ++i) {
Class intf = intfs[i];
if (Modifier.isPublic(intf.getModifiers())) {
try {
return intf.getMethod(name, params);
} catch (NoSuchMethodException ex) {
} catch (SecurityException ex) { }
}
}
for (;;) {
c = c.getSuperclass();
if (c == null) { break; }
if (Modifier.isPublic(c.getModifiers())) {
try {
Method m = c.getMethod(name, params);
int mModifiers = m.getModifiers();
if (Modifier.isPublic(mModifiers)
&& !Modifier.isStatic(mModifiers))
{
return m;
}
} catch (NoSuchMethodException ex) {
} catch (SecurityException ex) { }
}
}
}
}
return null;
}
private static boolean tryToMakeAccessible(Member member)
{
/**
* 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.
*/
if (method_setAccessible != null) {
try {
Object[] args_wrapper = { Boolean.TRUE };
method_setAccessible.invoke(member, args_wrapper);
return true;
} catch (Exception ex) { }
}
return false;
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
Member member = FunctionObject.readMember(in);
if (member instanceof Method) {
init((Method)member);
} else {
init((Constructor)member);
}
}
private void writeObject(ObjectOutputStream out)
throws IOException
{
FunctionObject.writeMember(out, memberObject);
}
private transient Member memberObject;
transient Class[] argTypes;
private static Method method_setAccessible;
static {
try {
Class MethodClass = Class.forName("java.lang.reflect.Method");
method_setAccessible = MethodClass.getMethod(
"setAccessible", new Class[] { Boolean.TYPE });
} catch (Exception ex) {
// Assume any exceptions means the method does not exist.
}
}
}

View File

@ -172,9 +172,8 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
if (! (Modifier.isInterface(modifiers) ||
Modifier.isAbstract(modifiers)))
{
Constructor[] ctors = members.ctors;
Class[][] ctorTypes = members.ctorTypes;
int index = NativeJavaMethod.findFunction(ctors, ctorTypes, args);
MemberBox[] ctors = members.ctors;
int index = NativeJavaMethod.findFunction(ctors, args);
if (index < 0) {
String sig = NativeJavaMethod.scriptSignature(args);
throw Context.reportRuntimeError2(
@ -182,8 +181,7 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
}
// Found the constructor, so try invoking it.
return constructSpecific(cx, scope, this, args,
ctors[index], ctorTypes[index]);
return constructSpecific(cx, scope, this, args, ctors[index]);
} else {
Scriptable topLevel = ScriptableObject.getTopLevelScope(this);
String msg = "";
@ -209,19 +207,25 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
}
}
public static Scriptable constructSpecific(Context cx,
Scriptable scope,
Scriptable thisObj,
Object[] args,
Constructor ctor,
Class[] paramTypes)
static Scriptable constructSpecific(Context cx, Scriptable scope,
Scriptable thisObj, Object[] args,
MemberBox ctor)
throws JavaScriptException
{
Scriptable topLevel = ScriptableObject.getTopLevelScope(thisObj);
Class classObject = ctor.getDeclaringClass();
Class[] argTypes = ctor.argTypes;
Object[] origArgs = args;
for (int i = 0; i < args.length; i++) {
args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i], true);
Object arg = args[i];
Object x = NativeJavaObject.coerceType(argTypes[i], arg, true);
if (x != arg) {
if (args == origArgs) {
args = (Object[])origArgs.clone();
}
args[i] = x;
}
}
Object instance;
try {
@ -232,7 +236,7 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
instEx.getMessage(), classObject.getName());
} catch (IllegalArgumentException argEx) {
String signature = NativeJavaMethod.scriptSignature(args);
String ctorString = ctor.toString();
String ctorString = ctor.ctor().toString();
throw Context.reportRuntimeError3(
"msg.bad.ctor.sig", argEx.getMessage(), ctorString, signature);
} catch (IllegalAccessException accessEx) {

View File

@ -56,11 +56,10 @@ import java.io.*;
public class NativeJavaConstructor extends BaseFunction
{
public NativeJavaConstructor(Constructor ctor)
public NativeJavaConstructor(MemberBox ctor)
{
this.constructor = ctor;
this.constructorType = ctor.getParameterTypes();
String sig = NativeJavaMethod.memberSignature(ctor, constructorType);
this.ctor = ctor;
String sig = JavaMembers.liveConnectSignature(ctor.argTypes);
this.functionName = "<init>".concat(sig);
}
@ -68,37 +67,14 @@ public class NativeJavaConstructor extends BaseFunction
Object[] args)
throws JavaScriptException
{
// Find a method that matches the types given.
if (constructor == null) {
throw new RuntimeException("No constructor defined for call");
}
return NativeJavaClass.constructSpecific(cx, scope, this, args,
constructor, constructorType);
return NativeJavaClass.constructSpecific(cx, scope, this, args, ctor);
}
public String toString()
{
return "[JavaConstructor " + constructor.getName() + "]";
return "[JavaConstructor " + ctor.getName() + "]";
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
constructor = (Constructor)FunctionObject.readMember(in);
constructorType = constructor.getParameterTypes();
}
private void writeObject(ObjectOutputStream out)
throws IOException
{
out.defaultWriteObject();
FunctionObject.writeMember(out, constructor);
}
transient Constructor constructor;
private transient Class[] constructorType;
MemberBox ctor;
}

View File

@ -56,28 +56,16 @@ import java.io.*;
public class NativeJavaMethod extends BaseFunction
{
public NativeJavaMethod(Method[] methods)
public NativeJavaMethod(MemberBox[] methods)
{
this.functionName = methods[0].getName();
init(methods);
this.methods = methods;
}
public NativeJavaMethod(Method method, String name)
public NativeJavaMethod(MemberBox method, String name)
{
this.functionName = name;
Method[] methods = { method };
init(methods);
}
private void init(Method[] methods)
{
this.methods = methods;
int N = methods.length;
methodTypes = new Class[N][];
for (int i = 0; i != N; ++i) {
methodTypes[i] = methods[i].getParameterTypes();
}
this.methods = new MemberBox[] { method };
}
private static String scriptSignature(Object value)
@ -102,7 +90,7 @@ public class NativeJavaMethod extends BaseFunction
return "object";
}
} else {
return javaSignature(value.getClass());
return JavaMembers.javaSignature(value.getClass());
}
}
@ -117,57 +105,6 @@ public class NativeJavaMethod extends BaseFunction
return sig.toString();
}
static String javaSignature(Class type)
{
if (type == null) {
return "null";
} else if (!type.isArray()) {
return type.getName();
} else {
int arrayDimension = 0;
do {
++arrayDimension;
type = type.getComponentType();
} while (type.isArray());
String name = type.getName();
String suffix = "[]";
if (arrayDimension == 1) {
return name.concat(suffix);
} else {
int length = name.length() + arrayDimension * suffix.length();
StringBuffer sb = new StringBuffer(length);
sb.append(name);
while (arrayDimension != 0) {
--arrayDimension;
sb.append(suffix);
}
return sb.toString();
}
}
}
static String memberSignature(Member member, Class[] paramTypes)
{
String name;
if (member instanceof Method) {
Method method = (Method)member;
name = method.getName();
} else {
name = "";
}
StringBuffer sb = new StringBuffer();
sb.append(name);
sb.append('(');
for (int i = 0, N = paramTypes.length; i != N; ++i) {
if (i != 0) {
sb.append(',');
}
sb.append(javaSignature(paramTypes[i]));
}
sb.append(')');
return sb.toString();
}
public String decompile(Context cx, int indent, boolean justbody)
{
StringBuffer sb = new StringBuffer();
@ -192,10 +129,11 @@ public class NativeJavaMethod extends BaseFunction
private void toString(StringBuffer sb)
{
for (int i = 0, N = methods.length; i != N; ++i) {
Method method = methods[i];
sb.append(javaSignature(method.getReturnType()));
Method method = methods[i].method();
sb.append(JavaMembers.javaSignature(method.getReturnType()));
sb.append(' ');
sb.append(memberSignature(method, methodTypes[i]));
sb.append(method.getName());
sb.append(JavaMembers.liveConnectSignature(methods[i].argTypes));
sb.append('\n');
}
}
@ -208,22 +146,23 @@ public class NativeJavaMethod extends BaseFunction
if (methods.length == 0) {
throw new RuntimeException("No methods defined for call");
}
int index = findFunction(methods, methodTypes, args);
int index = findFunction(methods, args);
if (index < 0) {
Class c = methods[0].getDeclaringClass();
Class c = methods[0].method().getDeclaringClass();
String sig = c.getName() + '.' + functionName + '(' +
scriptSignature(args) + ')';
throw Context.reportRuntimeError1("msg.java.no_such_method", sig);
}
Method meth = methods[index];
Class paramTypes[] = methodTypes[index];
MemberBox meth = methods[index];
Class[] argTypes = meth.argTypes;
// First, we marshall the args.
Object[] origArgs = args;
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
Object coerced = NativeJavaObject.coerceType(paramTypes[i], arg,
Object coerced = NativeJavaObject.coerceType(argTypes[i], arg,
true);
if (coerced != arg) {
if (origArgs == args) {
@ -233,7 +172,7 @@ public class NativeJavaMethod extends BaseFunction
}
}
Object javaObject;
if (Modifier.isStatic(meth.getModifiers())) {
if (meth.isStatic()) {
javaObject = null; // don't need an object
} else {
Scriptable o = thisObj;
@ -247,16 +186,12 @@ public class NativeJavaMethod extends BaseFunction
javaObject = ((Wrapper) o).unwrap();
}
if (debug) {
printDebug("Calling ", meth, paramTypes, args);
printDebug("Calling ", meth, args);
}
Object retval;
try {
try {
retval = meth.invoke(javaObject, args);
} catch (IllegalAccessException e) {
retval = retryIllegalAccessInvoke(meth, javaObject, args, e);
}
retval = meth.invoke(javaObject, args);
} catch (IllegalAccessException accessEx) {
throw Context.reportRuntimeError(
"While attempting to call \"" + meth.getName() +
@ -265,7 +200,7 @@ public class NativeJavaMethod extends BaseFunction
} catch (Exception ex) {
throw ScriptRuntime.throwAsUncheckedException(ex);
}
Class staticType = meth.getReturnType();
Class staticType = meth.method().getReturnType();
if (debug) {
Class actualType = (retval == null) ? null
@ -291,81 +226,33 @@ public class NativeJavaMethod extends BaseFunction
return wrapped;
}
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;
}
}
}
/**
* 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;
}
/**
* Find the index of the correct function to call given the set of methods
* or constructors and the arguments.
* If no function can be found to call, return -1.
*/
static int findFunction(Member[] methodsOrCtors, Class[][] memberTypes,
Object[] args)
static int findFunction(MemberBox[] methodsOrCtors, Object[] args)
{
if (methodsOrCtors.length == 0) {
return -1;
} else if (methodsOrCtors.length == 1) {
Member member = methodsOrCtors[0];
Class paramTypes[] = memberTypes[0];
int plength = paramTypes.length;
if (plength != args.length) {
MemberBox member = methodsOrCtors[0];
Class[] argTypes = member.argTypes;
int alength = argTypes.length;
if (alength != args.length) {
return -1;
}
for (int j = 0; j != plength; ++j) {
if (!NativeJavaObject.canConvert(args[j], paramTypes[j])) {
for (int j = 0; j != alength; ++j) {
if (!NativeJavaObject.canConvert(args[j], argTypes[j])) {
if (debug) printDebug("Rejecting (args can't convert) ",
member, paramTypes, args);
member, args);
return -1;
}
}
if (debug) printDebug("Found ", member, paramTypes, args);
if (debug) printDebug("Found ", member, args);
return 0;
}
boolean hasMethods = methodsOrCtors[0] instanceof Method;
int bestFit = -1;
Class[] bestFitTypes = null;
@ -373,48 +260,45 @@ public class NativeJavaMethod extends BaseFunction
int ambiguousMethodCount = 0;
for (int i = 0; i < methodsOrCtors.length; i++) {
Member member = methodsOrCtors[i];
Class paramTypes[] = memberTypes[i];
if (paramTypes.length != args.length) {
MemberBox member = methodsOrCtors[i];
Class[] argTypes = member.argTypes;
if (argTypes.length != args.length) {
continue;
}
if (bestFit < 0) {
int j;
for (j = 0; j < paramTypes.length; j++) {
if (!NativeJavaObject.canConvert(args[j], paramTypes[j])) {
for (j = 0; j < argTypes.length; j++) {
if (!NativeJavaObject.canConvert(args[j], argTypes[j])) {
if (debug) printDebug("Rejecting (args can't convert) ",
member, paramTypes, args);
member, args);
break;
}
}
if (j == paramTypes.length) {
if (debug) printDebug("Found ", member, paramTypes, args);
if (j == argTypes.length) {
if (debug) printDebug("Found ", member, args);
bestFit = i;
bestFitTypes = paramTypes;
bestFitTypes = argTypes;
}
}
else {
int preference = preferSignature(args, paramTypes,
int preference = preferSignature(args, argTypes,
bestFitTypes);
if (preference == PREFERENCE_AMBIGUOUS) {
if (debug) printDebug("Deferring ",
member, paramTypes, args);
if (debug) printDebug("Deferring ", member, args);
// add to "ambiguity list"
if (ambiguousMethods == null)
ambiguousMethods = new int[methodsOrCtors.length];
ambiguousMethods[ambiguousMethodCount++] = i;
} else if (preference == PREFERENCE_FIRST_ARG) {
if (debug) printDebug("Substituting ",
member, paramTypes, args);
if (debug) printDebug("Substituting ", member, args);
bestFit = i;
bestFitTypes = paramTypes;
bestFitTypes = argTypes;
} else if (preference == PREFERENCE_SECOND_ARG) {
if (debug) printDebug("Rejecting ",
member, paramTypes, args);
if (debug) printDebug("Rejecting ", member, args);
} else {
if (preference != PREFERENCE_EQUAL) Context.codeBug();
Member best = methodsOrCtors[bestFit];
if (Modifier.isStatic(best.getModifiers())
MemberBox best = methodsOrCtors[bestFit];
if (best.isStatic()
&& best.getDeclaringClass().isAssignableFrom(
member.getDeclaringClass()))
{
@ -423,14 +307,12 @@ public class NativeJavaMethod extends BaseFunction
// a derived class's parameters match exactly.
// We want to call the dervied class's method.
if (debug) printDebug(
"Substituting (overridden static)",
member, paramTypes, args);
"Substituting (overridden static)", member, args);
bestFit = i;
bestFitTypes = paramTypes;
bestFitTypes = argTypes;
} else {
if (debug) printDebug(
"Ignoring same signature member ",
member, paramTypes, args);
"Ignoring same signature member ", member, args);
}
}
}
@ -444,28 +326,25 @@ public class NativeJavaMethod extends BaseFunction
int removedCount = 0;
for (int k = 0; k != ambiguousMethodCount; ++k) {
int i = ambiguousMethods[k];
Member member = methodsOrCtors[i];
Class paramTypes[] = memberTypes[i];
int preference = preferSignature(args, paramTypes,
MemberBox member = methodsOrCtors[i];
Class[] argTypes = member.argTypes;
int preference = preferSignature(args, argTypes,
bestFitTypes);
if (preference == PREFERENCE_FIRST_ARG) {
if (debug) printDebug("Substituting ",
member, paramTypes, args);
if (debug) printDebug("Substituting ", member, args);
bestFit = i;
bestFitTypes = paramTypes;
bestFitTypes = argTypes;
ambiguousMethods[k] = -1;
++removedCount;
}
else if (preference == PREFERENCE_SECOND_ARG) {
if (debug) printDebug("Rejecting ",
member, paramTypes, args);
if (debug) printDebug("Rejecting ", member, args);
ambiguousMethods[k] = -1;
++removedCount;
}
else {
if (debug) printDebug("UNRESOLVED: ",
member, paramTypes, args);
if (debug) printDebug("UNRESOLVED: ", member, args);
}
}
@ -484,20 +363,13 @@ public class NativeJavaMethod extends BaseFunction
if (!first) {
buf.append(", ");
}
Member member = methodsOrCtors[i];
if (hasMethods) {
Method method = (Method)member;
Class rtnType = method.getReturnType();
buf.append(rtnType);
buf.append(' ');
}
buf.append(memberSignature(member, memberTypes[i]));
buf.append(methodsOrCtors[i].toJavaDeclaration());
first = false;
}
Member best = methodsOrCtors[bestFit];
MemberBox best = methodsOrCtors[bestFit];
if (!hasMethods) {
if (methodsOrCtors[0].isMethod()) {
throw Context.reportRuntimeError3(
"msg.constructor.ambiguous",
best.getName(), scriptSignature(args), buf.toString());
@ -579,8 +451,8 @@ public class NativeJavaMethod extends BaseFunction
private static final boolean debug = false;
private static void printDebug(String msg, Member member,
Class[] paramTypes, Object[] args)
private static void printDebug(String msg, MemberBox member,
Object[] args)
{
if (debug) {
StringBuffer sb = new StringBuffer();
@ -588,50 +460,16 @@ public class NativeJavaMethod extends BaseFunction
sb.append(msg);
sb.append(member.getDeclaringClass().getName());
sb.append('.');
sb.append(memberSignature(member, paramTypes));
if (member.isMethod()) {
sb.append(member.getName());
}
sb.append(JavaMembers.liveConnectSignature(member.argTypes));
sb.append(" for arguments (");
sb.append(scriptSignature(args));
sb.append(')');
}
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
int N = in.readInt();
Method[] array = new Method[N];
for (int i = 0; i != N; ++i) {
array[i] = (Method)FunctionObject.readMember(in);
}
init(array);
}
private void writeObject(ObjectOutputStream out)
throws IOException
{
out.defaultWriteObject();
int N = methods.length;
out.writeInt(N);
for (int i = 0; i != N; ++i) {
FunctionObject.writeMember(out, methods[i]);
}
}
transient Method methods[];
transient Class[][] methodTypes;
private static Method method_setAccessible = null;
static {
try {
Class MethodClass = Class.forName("java.lang.reflect.Method");
method_setAccessible = MethodClass.getMethod(
"setAccessible", new Class[] { Boolean.TYPE });
} catch (Exception ex) {
// Assume any exceptions means the method does not exist.
}
}
MemberBox[] methods;
}

View File

@ -865,7 +865,7 @@ WrapFactory#wrap(Context cx, Scriptable scope, Object obj, Class)}
}
throw Context.reportRuntimeError2
("msg.conversion.not.allowed",
value.toString(), NativeJavaMethod.javaSignature(type));
value.toString(), JavaMembers.javaSignature(type));
}
public void writeExternal(ObjectOutput out)