Changeset 12345


Ignore:
Timestamp:
01/08/10 19:55:05 (12 years ago)
Author:
astalla
Message:

Use of the "intended class" of an object before the actual class to access it via reflection.

Location:
trunk/abcl/src/org/armedbear/lisp
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/abcl/src/org/armedbear/lisp/Java.java

    r12314 r12345  
    4646import java.lang.reflect.Method;
    4747import java.lang.reflect.Modifier;
    48 import java.util.HashMap;
    49 import java.util.Map;
     48import java.util.*;
    5049
    5150public final class Java
     
    202201                    return args[3];
    203202            }
    204             return JavaObject.getInstance(f.get(instance), translate);
     203            return JavaObject.getInstance(f.get(instance), translate, f.getType());
    205204        }
    206205        catch (NoSuchFieldException e) {
     
    366365                    String methodName = methodRef.getStringValue();
    367366                    Method[] methods = c.getMethods();
     367        List<Method> staticMethods = new ArrayList<Method>();
    368368                    int argCount = args.length - 2;
    369                     for (int i = 0; i < methods.length; i++) {
    370                         Method method = methods[i];
    371                         if (!Modifier.isStatic(method.getModifiers())
    372                             || method.getParameterTypes().length != argCount)
    373                             continue;
    374                         if (method.getName().equals(methodName)) {
    375                             m = method;
    376                             break;
    377                         }
    378                     }
     369        for(Method m1 : methods) {
     370      if(Modifier.isStatic(m1.getModifiers())) {
     371          staticMethods.add(m1);
     372      }
     373        }
     374        if(staticMethods.size() > 0) {
     375      m = findMethod(staticMethods.toArray(new Method[staticMethods.size()]), methodName, args);
     376        }
    379377                    if (m == null)
    380378                        error(new LispError("no such method"));
     
    392390            }
    393391            Object result = m.invoke(null, methodArgs);
    394             return JavaObject.getInstance(result, translate);
     392      return JavaObject.getInstance(result, translate, m.getReturnType());
    395393        }
    396394        catch (ControlTransfer c) {
     
    631629        if (args.length < 2)
    632630            error(new WrongNumberOfArgumentsException(fun));
    633         final LispObject methodArg = args[0];
    634         final LispObject instanceArg = args[1];
    635         final Object instance;
    636         if (instanceArg instanceof AbstractString)
    637             instance = instanceArg.getStringValue();
    638         else if (instanceArg instanceof JavaObject)
    639             instance = ((JavaObject)instanceArg).getObject();
    640         else {
    641       instance = instanceArg.javaInstance();
    642         }
    643631        try {
    644             final Method method;
     632      final LispObject methodArg = args[0];
     633      final LispObject instanceArg = args[1];
     634      final Object instance;
     635      Class<?> intendedClass = null;
     636      if (instanceArg instanceof AbstractString) {
     637    instance = instanceArg.getStringValue();
     638      } else if (instanceArg instanceof JavaObject) {
     639    JavaObject jobj = ((JavaObject)instanceArg);
     640    instance = jobj.getObject();
     641    intendedClass = jobj.getIntendedClass();
     642      } else {
     643    instance = instanceArg.javaInstance();
     644      }
     645      if(instance == null) {
     646    throw new NullPointerException(); //Handled below
     647      }
     648            Method method;
     649      Object[] methodArgs;
    645650            if (methodArg instanceof AbstractString) {
     651    methodArgs = translateMethodArguments(args, 2);
    646652                String methodName = methodArg.getStringValue();
    647                 Class c = instance.getClass();
    648                 method = findMethod(c, methodName, args);
     653    if(intendedClass == null) {
     654        intendedClass = instance.getClass();
     655    }
     656                method = findMethod(intendedClass, methodName, methodArgs);
     657    Class actualClass = null;
     658    if(method == null) {       
     659        actualClass = instance.getClass();
     660        if(intendedClass != actualClass &&
     661           Modifier.isPublic(actualClass.getModifiers())) {
     662      method = findMethod(actualClass, methodName, methodArgs);
     663        }
     664    }
     665    if (method == null) {
     666        String classes = intendedClass.getName();
     667        if(actualClass != null && actualClass != intendedClass) {
     668      classes += " or " + actualClass.getName();
     669        }
     670        throw new NoSuchMethodException("No applicable method named " + methodName + " found in " + classes);
     671    }
     672
    649673            } else
    650674                method = (Method) JavaObject.getObject(methodArg);
    651675            Class<?>[] argTypes = (Class<?>[])method.getParameterTypes();
    652             Object[] methodArgs = new Object[args.length - 2];
     676      if(argTypes.length != args.length - 2) {
     677    return error(new WrongNumberOfArgumentsException("Wrong number of arguments for " + method + ": expected " + argTypes.length + ", got " + (args.length - 2)));
     678      }
     679            methodArgs = new Object[argTypes.length];
    653680            for (int i = 2; i < args.length; i++) {
    654681                LispObject arg = args[i];
     
    659686            }
    660687            return JavaObject.getInstance(method.invoke(instance, methodArgs),
    661                                           translate);
     688                                          translate,
     689            method.getReturnType());
    662690        }
    663691        catch (ControlTransfer t) {
     
    700728    }
    701729
    702     private static Method findMethod(Class<?> c, String methodName, LispObject[] args) throws NoSuchMethodException {
    703   int argCount = args.length - 2;
    704         Object[] javaArgs = translateMethodArguments(args, 2);
    705         Method[] methods = c.getMethods();
     730    private static Method findMethod(Method[] methods, String methodName, Object[] javaArgs) {
     731  int argCount = javaArgs.length;
    706732        Method result = null;
    707733        for (int i = methods.length; i-- > 0;) {
     
    721747            }
    722748        }
    723         if (result == null) {
    724             throw new NoSuchMethodException(methodName);
    725         }
    726749        return result;
     750    }
     751
     752    private static Method findMethod(Class<?> c, String methodName, Object[] javaArgs) {
     753        Method[] methods = c.getMethods();
     754  return findMethod(methods, methodName, javaArgs);
     755    }
     756
     757    private static Method findMethod(Class<?> c, String methodName, LispObject[] args) {
     758        Object[] javaArgs = translateMethodArguments(args, 2);
     759  return findMethod(c, methodName, javaArgs);
     760    }
     761
     762    private static Method findMethod(Method[] methods, String methodName, LispObject[] args) {
     763        Object[] javaArgs = translateMethodArguments(args, 2);
     764  return findMethod(methods, methodName, javaArgs);
    727765    }
    728766
     
    876914        {
    877915            return JavaObject.getInstance(arg.javaInstance(), true);
     916        }
     917    };
     918
     919    // ### jcoerce java-object intended-class
     920    private static final Primitive JCOERCE =
     921        new Primitive("jcoerce", PACKAGE_JAVA, true, "java-object intended-class")
     922    {
     923        @Override
     924        public LispObject execute(LispObject javaObject, LispObject intendedClass)
     925        {
     926      Object o = javaObject.javaInstance();
     927      Class<?> c = javaClass(intendedClass);
     928      try {
     929    return JavaObject.getInstance(o, c);
     930      } catch(ClassCastException e) {
     931    return error(new TypeError(javaObject, new SimpleString(c.getName())));
     932      }
    878933        }
    879934    };
  • trunk/abcl/src/org/armedbear/lisp/JavaObject.java

    r12288 r12345  
    4242import java.util.*;
    4343
    44 public final class JavaObject extends LispObject
    45 {
     44public final class JavaObject extends LispObject {
    4645    private final Object obj;
    47 
    48     public JavaObject(Object obj)
    49     {
     46    private final Class<?> intendedClass;
     47
     48    public JavaObject(Object obj) {
    5049        this.obj = obj;
     50  this.intendedClass = obj != null ? obj.getClass() : null;
     51    }
     52
     53    /**
     54     * Constructs a Java Object with the given intended class, used to access
     55     * the object reflectively.
     56     * @throws ClassCastException if the object is not an instance of the
     57     *                            intended class.
     58     */
     59    public JavaObject(Object obj, Class<?> intendedClass) {
     60  if(obj != null && intendedClass == null) {
     61      intendedClass = obj.getClass();
     62  }
     63  if(intendedClass != null && !intendedClass.isInstance(obj)) {
     64      throw new ClassCastException(obj + " can not be cast to " + intendedClass);
     65  }
     66  this.obj = obj;
     67  this.intendedClass = intendedClass;
    5168    }
    5269
     
    103120    /** Encapsulates obj, if required.
    104121     * If obj is a {@link LispObject}, it's returned as-is.
     122     * If not, a java object with the specified intended class is returned.
     123     *
     124     * @param obj Any java object
     125     * @param intendedClass the class that shall be used to access obj
     126     * @return obj or a new JavaObject encapsulating obj
     127     */
     128    public final static LispObject getInstance(Object obj, Class<?> intendedClass) {
     129        if (obj == null)
     130            return new JavaObject(null);
     131       
     132        if (obj instanceof LispObject)
     133            return (LispObject)obj;
     134
     135        return new JavaObject(obj, intendedClass);
     136    }
     137
     138    /** Encapsulates obj, if required.
     139     * If obj is a {@link LispObject}, it's returned as-is.
    105140     * If obj is of a type which can be mapped to a lisp type,
    106141     * an object of the mapped type is returned, if translated is true.
     
    110145     * @return a LispObject representing or encapsulating obj
    111146     */
    112     public final static LispObject getInstance(Object obj, boolean translated)
    113 
    114     {
     147    public final static LispObject getInstance(Object obj, boolean translated) {
     148  return getInstance(obj, translated, obj != null ? obj.getClass() : null);
     149    }
     150
     151
     152
     153    /** Encapsulates obj, if required.
     154     * If obj is a {@link LispObject}, it's returned as-is.
     155     * If obj is of a type which can be mapped to a lisp type,
     156     * an object of the mapped type is returned, if translated is true.
     157     *
     158     * @param obj
     159     * @param translated
     160     * @param intendedClass the class that shall be used to reflectively
     161     *                      access obj; it is an error for obj not to be
     162     *                      an instance of this class. This parameter is ignored
     163     *                      if translated == true and the object can be
     164     *                      converted to a Lisp object.
     165     * @return a LispObject representing or encapsulating obj
     166     */
     167    public final static LispObject getInstance(Object obj, boolean translated, Class<?> intendedClass) {
    115168        if (! translated)
    116             return getInstance(obj);
     169            return getInstance(obj, intendedClass);
    117170
    118171        if (obj == null) return NIL;
     
    168221        //  - streams
    169222        //  - others?
    170         return new JavaObject(obj);
    171     }
    172 
    173     @Override
    174     public Object javaInstance()
    175     {
     223        return new JavaObject(obj, intendedClass);
     224    }
     225
     226    @Override
     227    public Object javaInstance() {
    176228        return obj;
    177229    }
     
    179231    @Override
    180232    public Object javaInstance(Class c) {
    181   return javaInstance();
     233  if(obj == null) {
     234      return obj;
     235  } else if(c.isAssignableFrom(intendedClass)) {
     236      return obj;
     237  } else {
     238      return error(new TypeError(intendedClass.getName() + " is not assignable to " + c.getName()));
     239  }
    182240    }
    183241
     
    190248    public Object lockableInstance() {
    191249        return obj;
     250    }
     251
     252    public Class<?> getIntendedClass() {
     253  return intendedClass;
    192254    }
    193255
Note: See TracChangeset for help on using the changeset viewer.