source: branches/0.22.x/abcl/src/org/armedbear/lisp/Java.java

Last change on this file was 12849, checked in by Mark Evenson, 14 years ago

Narrow LISP-ERROR to STORAGE-CONDITION.

Now JRUN-EXCEPTION-PROTECTED behaves like INTERACTIVE-EVAL which
should be correct.

Include textual message about reason for STORAGE-CONDITION

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 47.2 KB
Line 
1/*
2 * Java.java
3 *
4 * Copyright (C) 2002-2006 Peter Graves, Andras Simon
5 * $Id: Java.java 12849 2010-08-02 06:07:45Z mevenson $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38import java.beans.BeanInfo;
39import java.beans.IntrospectionException;
40import java.beans.Introspector;
41import java.beans.PropertyDescriptor;
42import java.lang.reflect.Array;
43import java.lang.reflect.Constructor;
44import java.lang.reflect.Field;
45import java.lang.reflect.InvocationTargetException;
46import java.lang.reflect.Method;
47import java.lang.reflect.Modifier;
48import java.util.*;
49
50public final class Java
51{
52    static final Map<Class,Symbol> registeredExceptions =
53       new HashMap<Class,Symbol>();
54
55    private static final LispClass java_exception = LispClass.findClass(Symbol.JAVA_EXCEPTION);
56
57    static boolean isJavaException(LispClass lc)
58    {
59        return lc.subclassp(java_exception);
60    }
61
62    private static final Primitive ENSURE_JAVA_OBJECT = new pf_ensure_java_object();
63    @DocString(name="ensure-java-object", args="obj",
64    doc="Ensures OBJ is wrapped in a JAVA-OBJECT, wrapping it if necessary.")
65    private static final class pf_ensure_java_object extends Primitive
66    {
67        pf_ensure_java_object() 
68        {
69            super("ensure-java-object", PACKAGE_JAVA, true);
70        }
71
72        @Override
73        public LispObject execute(LispObject obj) {
74      return obj instanceof JavaObject ? obj : new JavaObject(obj);
75        }
76    };
77
78    private static final Primitive REGISTER_JAVA_EXCEPTION = new pf_register_java_exception();
79    @DocString(name="register-java-exception", // => T
80    args="exception-name condition-symbol",
81    doc="Registers the Java Throwable named by the symbol EXCEPTION-NAME as the condition " +
82        "designated by CONDITION-SYMBOL.  Returns T if successful, NIL if not.")
83    private static final class pf_register_java_exception extends Primitive
84    {
85        pf_register_java_exception() 
86        {
87            super("register-java-exception", PACKAGE_JAVA, true);
88        }
89
90        @Override
91        public LispObject execute(LispObject className, LispObject symbol)
92
93        {
94            // FIXME Verify that CONDITION-SYMBOL is a symbol that names a condition.
95            // FIXME Signal a continuable error if the exception is already registered.
96            if ((symbol instanceof Symbol) && isJavaException(LispClass.findClass((Symbol) symbol))) {
97                registeredExceptions.put(classForName(className.getStringValue()),
98                                         (Symbol)symbol);
99                return T;
100            }
101            return NIL;
102        }
103    };
104
105    private static final Primitive UNREGISTER_JAVA_EXCEPTION = new pf_unregister_java_exception();
106    @DocString(name="unregister-java-exception", args="exception-name",
107    doc="Unregisters the Java Throwable EXCEPTION-NAME previously registered" +
108        " by REGISTER-JAVA-EXCEPTION.")
109    private static final class pf_unregister_java_exception extends Primitive
110    {
111        pf_unregister_java_exception() 
112        {
113            super("unregister-java-exception", PACKAGE_JAVA, true);
114        }
115
116        @Override
117        public LispObject execute(LispObject className)
118
119        {
120            // FIXME Verify that EXCEPTION-NAME designates a subclass of Throwable.
121            return registeredExceptions.remove(classForName(className.getStringValue())) == null ? NIL : T;
122        }
123    };
124
125    static Symbol getCondition(Class cl)
126    {
127  Class o = classForName("java.lang.Object");
128      for (Class c = cl ; c != o ; c = c.getSuperclass()) {
129            Object object = registeredExceptions.get(c);
130            if (object != null && isJavaException(LispClass.findClass((Symbol) object))) {
131                return (Symbol) object;
132            }
133        }
134        return null;
135    }
136
137    private static final Primitive JCLASS = new pf_jclass();
138    @DocString(name="jclass", args="name-or-class-ref &optional class-loader",
139    doc="Returns a reference to the Java class designated by" +
140        " NAME-OR-CLASS-REF. If the CLASS-LOADER parameter is passed, the" +
141        " class is resolved with respect to the given ClassLoader.")
142    private static final class pf_jclass extends Primitive
143    {
144
145        pf_jclass() 
146        {
147            super(Symbol.JCLASS);
148        }
149
150        @Override
151        public LispObject execute(LispObject arg)
152        {
153      return JavaObject.getInstance(javaClass(arg, JavaClassLoader.getCurrentClassLoader()));
154        }
155
156        @Override
157        public LispObject execute(LispObject className, LispObject classLoader)
158        {
159      ClassLoader loader = (ClassLoader) classLoader.javaInstance(ClassLoader.class);
160      return JavaObject.getInstance(javaClass(className, loader));
161        }
162    };
163
164    static final LispObject jfield(Primitive fun, LispObject[] args, boolean translate)
165
166    {
167        if (args.length < 2 || args.length > 4)
168            error(new WrongNumberOfArgumentsException(fun));
169        String fieldName = null;
170        Class c;
171        Field f;
172        Class fieldType;
173        Object instance = null;
174        try {
175            if (args[1] instanceof AbstractString) {
176                // Cases 1-5.
177                fieldName = args[1].getStringValue();
178                c = javaClass(args[0]);
179            } else {
180                // Cases 6 and 7.
181                fieldName = args[0].getStringValue();
182                instance = JavaObject.getObject(args[1]);
183                c = instance.getClass();
184            }
185            f = c.getField(fieldName);
186            fieldType = f.getType();
187            switch (args.length) {
188                case 2:
189                    // Cases 1 and 6.
190                    break;
191                case 3:
192                    // Cases 2,3, and 7.
193                    if (instance == null) {
194                        // Cases 2 and 3.
195                        if (args[2] instanceof JavaObject) {
196                            // Case 2.
197                            instance = JavaObject.getObject(args[2]);
198                            break;
199                        } else {
200                            // Case 3.
201                            f.set(null,args[2].javaInstance(fieldType));
202                            return args[2];
203                        }
204                    } else {
205                        // Case 7.
206                        f.set(instance,args[2].javaInstance(fieldType));
207                        return args[2];
208                    }
209                case 4:
210                    // Cases 4 and 5.
211                    if (args[2] != NIL) {
212                        // Case 4.
213                        instance = JavaObject.getObject(args[2]);
214                    }
215                    f.set(instance,args[3].javaInstance(fieldType));
216                    return args[3];
217            }
218            return JavaObject.getInstance(f.get(instance), translate, f.getType());
219        }
220        catch (NoSuchFieldException e) {
221            error(new LispError("no such field"));
222        }
223        catch (SecurityException e) {
224            error(new LispError("inaccessible field"));
225        }
226        catch (IllegalAccessException e) {
227            error(new LispError("illegal access"));
228        }
229        catch (IllegalArgumentException e) {
230            error(new LispError("illegal argument"));
231        }
232        catch (Throwable t) { // no code -> no ControlTransfer
233            error(new LispError(getMessage(t)));
234        }
235        // Not reached.
236        return NIL;
237    }
238
239
240    private static final Primitive JFIELD = new pf_jfield();
241    @DocString(name="jfield",
242    args="class-ref-or-field field-or-instance &optional instance value",
243    doc="Retrieves or modifies a field in a Java class or instance.\n\n"+
244        "Supported argument patterns:\n\n"+
245        "   Case 1: class-ref  field-name:\n"+
246        "      Retrieves the value of a static field.\n\n"+
247        "   Case 2: class-ref  field-name  instance-ref:\n"+
248        "      Retrieves the value of a class field of the instance.\n\n"+
249        "   Case 3: class-ref  field-name  primitive-value:\n"+
250        "      Stores a primitive-value in a static field.\n\n"+
251        "   Case 4: class-ref  field-name  instance-ref  value:\n"+
252        "      Stores value in a class field of the instance.\n\n"+
253        "   Case 5: class-ref  field-name  nil  value:\n"+
254        "      Stores value in a static field (when value may be\n"+
255        "      confused with an instance-ref).\n\n"+
256        "   Case 6: field-name  instance:\n"+
257        "      Retrieves the value of a field of the instance. The\n"+
258        "      class is derived from the instance.\n\n"+
259        "   Case 7: field-name  instance  value:\n"+
260        "      Stores value in a field of the instance. The class is\n"+
261        "      derived from the instance.\n\n"
262        )
263    private static final class pf_jfield extends Primitive
264    {
265        pf_jfield() 
266        {
267            super("jfield", PACKAGE_JAVA, true);
268        }
269
270        @Override
271        public LispObject execute(LispObject[] args)
272        {
273            return jfield(this, args, true);
274        }
275    };
276
277    private static final Primitive JFIELD_RAW = new pf_jfield_raw();
278    @DocString(name="jfield",
279    args="class-ref-or-field field-or-instance &optional instance value",
280    doc="Retrieves or modifies a field in a Java class or instance. Does not\n"+
281        "attempt to coerce its value or the result into a Lisp object.\n\n"+
282        "Supported argument patterns:\n\n"+
283        "   Case 1: class-ref  field-name:\n"+
284        "      Retrieves the value of a static field.\n\n"+
285        "   Case 2: class-ref  field-name  instance-ref:\n"+
286        "      Retrieves the value of a class field of the instance.\n\n"+
287        "   Case 3: class-ref  field-name  primitive-value:\n"+
288        "      Stores a primitive-value in a static field.\n\n"+
289        "   Case 4: class-ref  field-name  instance-ref  value:\n"+
290        "      Stores value in a class field of the instance.\n\n"+
291        "   Case 5: class-ref  field-name  nil  value:\n"+
292        "      Stores value in a static field (when value may be\n"+
293        "      confused with an instance-ref).\n\n"+
294        "   Case 6: field-name  instance:\n"+
295        "      Retrieves the value of a field of the instance. The\n"+
296        "      class is derived from the instance.\n\n"+
297        "   Case 7: field-name  instance  value:\n"+
298        "      Stores value in a field of the instance. The class is\n"+
299        "      derived from the instance.\n\n"
300        )
301    private static final class pf_jfield_raw extends Primitive
302    {
303        pf_jfield_raw() 
304        {
305            super("jfield-raw", PACKAGE_JAVA, true);
306        }
307
308        @Override
309        public LispObject execute(LispObject[] args)
310        {
311            return jfield(this, args, false);
312        }
313    };
314
315    private static final Primitive JCONSTRUCTOR = new pf_jconstructor();
316    @DocString(name="jconstructor", args="class-ref &rest parameter-class-refs",
317    doc="Returns a reference to the Java constructor of CLASS-REF with the" +
318        " given PARAMETER-CLASS-REFS.")
319    private static final class pf_jconstructor extends Primitive
320    {
321        pf_jconstructor() 
322        {
323            super("jconstructor", PACKAGE_JAVA, true);
324        }
325
326        @Override
327        public LispObject execute(LispObject[] args)
328        {
329            if (args.length < 1)
330                error(new WrongNumberOfArgumentsException(this));
331            try {
332                final Class<?> c = javaClass(args[0]);
333                int argCount = 0;
334                if (args.length == 2 && args[1] instanceof Fixnum) {
335                    argCount = Fixnum.getValue(args[1]);
336                } else {
337                    Class<?>[] parameterTypes = new Class[args.length-1];
338                    for (int i = 1; i < args.length; i++) {
339                        parameterTypes[i-1] = javaClass(args[i]);
340                    }
341                    return JavaObject.getInstance(c.getConstructor(parameterTypes));
342                }
343                // Parameter types not explicitly specified.
344                Constructor[] constructors = c.getConstructors();
345                for (int i = 0; i < constructors.length; i++) {
346                    Constructor constructor = constructors[i];
347                    if (constructor.getParameterTypes().length == argCount)
348                        return JavaObject.getInstance(constructor);
349                }
350                throw new NoSuchMethodException();
351            }
352            catch (NoSuchMethodException e) {
353                error(new LispError("no such constructor"));
354            }
355            catch (ControlTransfer e) {
356                throw e;
357            }
358            catch (Throwable t) { // ControlTransfer addressed above
359                error(new LispError(getMessage(t)));
360            }
361            // Not reached.
362            return NIL;
363        }
364    };
365
366    private static final Primitive JMETHOD = new pf_jmethod();
367
368    @DocString(name="jmethod", args="class-ref method-name &rest parameter-class-refs",
369    doc="Returns a reference to the Java method METHOD-NAME of CLASS-REF with the" +
370        " given PARAMETER-CLASS-REFS.")
371    private static final class pf_jmethod extends Primitive
372    {
373        pf_jmethod() 
374        {
375            super("jmethod", PACKAGE_JAVA, true);
376        }
377
378        @Override
379        public LispObject execute(LispObject[] args)
380        {
381            if (args.length < 2)
382                error(new WrongNumberOfArgumentsException(this));
383            final Class<?> c = javaClass(args[0]);
384            String methodName = args[1].getStringValue();
385            try {
386                int argCount = 0;
387                if (args.length == 3 && args[2] instanceof Fixnum) {
388                    argCount = ((Fixnum)args[2]).value;
389                } else {
390                    Class<?>[] parameterTypes = new Class[args.length-2];
391                    for (int i = 2; i < args.length; i++)
392                        parameterTypes[i-2] = javaClass(args[i]);
393                    return JavaObject.getInstance(c.getMethod(methodName,
394                                                              parameterTypes));
395                }
396                // Parameter types were not explicitly specified.
397                Method[] methods = c.getMethods();
398                for (int i = 0; i < methods.length; i++) {
399                    Method method = methods[i];
400                    if (method.getName().equals(methodName) &&
401                        method.getParameterTypes().length == argCount)
402                        return JavaObject.getInstance(method);
403                }
404                throw new NoSuchMethodException();
405            }
406            catch (NoSuchMethodException e) {
407                StringBuilder sb = new StringBuilder("No such method: ");
408                sb.append(c.getName());
409                sb.append('.');
410                sb.append(methodName);
411                sb.append('(');
412                for (int i = 2; i < args.length; i++) {
413                    sb.append(args[i].writeToString());
414                    if (i < args.length - 1)
415                        sb.append(',');
416                }
417                sb.append(')');
418                error(new LispError(sb.toString()));
419            }
420            catch (ControlTransfer e) {
421                throw e;
422            }
423            catch (Throwable t) { // ControlTransfer addressed above
424                error(new LispError(getMessage(t)));
425            }
426            // Not reached.
427            return NIL;
428        }
429    };
430
431    static final LispObject jstatic(Primitive fun, LispObject[] args, boolean translate)
432
433    {
434        if (args.length < 2)
435            error(new WrongNumberOfArgumentsException(fun));
436        try {
437            Method m = null;
438            LispObject methodRef = args[0];
439            if (methodRef instanceof JavaObject) {
440                Object obj = ((JavaObject)methodRef).getObject();
441                if (obj instanceof Method)
442                    m = (Method) obj;
443            } else if (methodRef instanceof AbstractString) {
444                Class c = javaClass(args[1]);
445                if (c != null) {
446                    String methodName = methodRef.getStringValue();
447                    Method[] methods = c.getMethods();
448        List<Method> staticMethods = new ArrayList<Method>();
449                    int argCount = args.length - 2;
450        for(Method m1 : methods) {
451      if(Modifier.isStatic(m1.getModifiers())) {
452          staticMethods.add(m1);
453      }
454        }
455        if(staticMethods.size() > 0) {
456      m = findMethod(staticMethods.toArray(new Method[staticMethods.size()]), methodName, args);
457        }
458                    if (m == null)
459                        error(new LispError("no such method"));
460                }
461            } else
462                error(new TypeError("wrong type: " + methodRef));
463            Object[] methodArgs = new Object[args.length-2];
464            Class[] argTypes = m.getParameterTypes();
465            for (int i = 2; i < args.length; i++) {
466                LispObject arg = args[i];
467                if (arg == NIL)
468                    methodArgs[i-2] = null;
469                else
470                    methodArgs[i-2] = arg.javaInstance(argTypes[i-2]);
471            }
472            Object result = m.invoke(null, methodArgs);
473      return JavaObject.getInstance(result, translate, m.getReturnType());
474        }
475        catch (ControlTransfer c) {
476            throw c;
477        }
478        catch (Throwable t) { // ControlTransfer handled above
479            if (t instanceof InvocationTargetException)
480                t = t.getCause();
481            Symbol condition = getCondition(t.getClass());
482            if (condition == null)
483                error(new JavaException(t));
484            else
485                Symbol.SIGNAL.execute(
486                    condition,
487                    Keyword.CAUSE,
488                    JavaObject.getInstance(t),
489                    Keyword.FORMAT_CONTROL,
490                    new SimpleString(getMessage(t)));
491        }
492        // Not reached.
493        return NIL;
494    }
495
496    private static final Primitive JSTATIC = new pf_jstatic();
497    @DocString(name="jstatic", args="method class &rest args",
498    doc="Invokes the static method METHOD on class CLASS with ARGS.")
499    private static final class pf_jstatic extends Primitive
500    {
501        pf_jstatic() 
502        {
503            super("jstatic", PACKAGE_JAVA, true);
504        }
505
506        @Override
507        public LispObject execute(LispObject[] args)
508        {
509            return jstatic(this, args, true);
510        }
511    };
512
513    private static final Primitive JSTATIC_RAW = new pf_jstatic_raw();
514    @DocString(name="jstatic-raw", args="method class &rest args",
515    doc="Invokes the static method METHOD on class CLASS with ARGS. Does not "+
516        "attempt to coerce the arguments or result into a Lisp object.")
517    private static final class pf_jstatic_raw extends Primitive
518    {
519        pf_jstatic_raw() 
520        {
521            super("jstatic-raw", PACKAGE_JAVA, true);
522        }
523
524        @Override
525        public LispObject execute(LispObject[] args)
526        {
527            return jstatic(this, args, false);
528        }
529    };
530
531    private static final Primitive JNEW = new pf_jnew();
532    @DocString(name="jnew", args="constructor &rest args",
533    doc="Invokes the Java constructor CONSTRUCTOR with the arguments ARGS.")
534    private static final class pf_jnew extends Primitive
535    {
536        pf_jnew()
537        {
538            super("jnew", PACKAGE_JAVA, true);
539        }
540
541        @Override
542        public LispObject execute(LispObject[] args)
543        {
544            if (args.length < 1)
545                error(new WrongNumberOfArgumentsException(this));
546            LispObject classRef = args[0];
547            try {
548                Constructor constructor;
549    if(classRef instanceof AbstractString) {
550        constructor = findConstructor(javaClass(classRef), args);
551    } else {
552        Object object = JavaObject.getObject(classRef);
553        if(object instanceof Constructor) {
554      constructor = (Constructor) object;
555        } else if(object instanceof Class<?>) {
556      constructor = findConstructor((Class<?>) object, args);
557        } else {
558      return error(new LispError(classRef.writeToString() + " is neither a Constructor nor a Class"));
559        }
560    }
561                Class[] argTypes = constructor.getParameterTypes();
562                Object[] initargs = new Object[args.length-1];
563                for (int i = 1; i < args.length; i++) {
564                    LispObject arg = args[i];
565                    if (arg == NIL)
566                        initargs[i-1] = null;
567                    else {
568                        initargs[i-1] = arg.javaInstance(argTypes[i-1]);
569                    }
570                }
571                return JavaObject.getInstance(constructor.newInstance(initargs));
572            }
573            catch (ControlTransfer c) {
574                throw c;
575            }
576            catch (Throwable t) { // ControlTransfer handled above
577                if (t instanceof InvocationTargetException)
578                    t = t.getCause();
579                Symbol condition = getCondition(t.getClass());
580                if (condition == null)
581                    error(new JavaException(t));
582                else
583                    Symbol.SIGNAL.execute(
584                        condition,
585                        Keyword.CAUSE,
586                        JavaObject.getInstance(t),
587                        Keyword.FORMAT_CONTROL,
588                        new SimpleString(getMessage(t)));
589            }
590            // Not reached.
591            return NIL;
592        }
593    };
594
595    private static final Primitive JNEW_ARRAY = new pf_jnew_array();
596    @DocString(name="jnew-array", args="element-type &rest dimensions",
597    doc="Creates a new Java array of type ELEMENT-TYPE, with the given" +
598        " DIMENSIONS.")
599    private static final class pf_jnew_array extends Primitive
600    {
601        pf_jnew_array()
602        {
603            super("jnew-array", PACKAGE_JAVA, true);
604        }
605
606        @Override
607        public LispObject execute(LispObject[] args)
608        {
609            if (args.length < 2)
610                error(new WrongNumberOfArgumentsException(this));
611            try {
612                Class c = javaClass(args[0]);
613                int[] dimensions = new int[args.length - 1];
614                for (int i = 1; i < args.length; i++)
615                    dimensions[i-1] = ((Integer)args[i].javaInstance()).intValue();
616                return JavaObject.getInstance(Array.newInstance(c, dimensions));
617            }
618            catch (Throwable t) { // no code -> no ControlTransfer
619                error(new JavaException(t));
620            }
621            // Not reached.
622            return NIL;
623        }
624    };
625
626    static final LispObject jarray_ref(Primitive fun, LispObject[] args, boolean translate)
627
628    {
629        if (args.length < 2)
630            error(new WrongNumberOfArgumentsException(fun));
631        try {
632            Object a = args[0].javaInstance();
633            for (int i = 1; i<args.length - 1; i++)
634                a = Array.get(a, ((Integer)args[i].javaInstance()).intValue());
635            return JavaObject.getInstance(Array.get(a,
636                    ((Integer)args[args.length - 1].javaInstance()).intValue()), translate);
637        }
638        catch (Throwable t) { // no code -> no ControlTransfer
639            Symbol condition = getCondition(t.getClass());
640            if (condition == null)
641                error(new JavaException(t));
642            else
643                Symbol.SIGNAL.execute(
644                    condition,
645                    Keyword.CAUSE,
646                    JavaObject.getInstance(t),
647                    Keyword.FORMAT_CONTROL,
648                    new SimpleString(getMessage(t)));
649        }
650        // Not reached.
651        return NIL;
652    }
653
654    private static final Primitive JARRAY_REF = new pf_jarray_ref();
655    @DocString(name="jarray-ref", args="java-array &rest indices",
656    doc="Dereferences the Java array JAVA-ARRAY using the given INDICIES, " +
657        "coercing the result into a Lisp object, if possible.")
658    private static final class pf_jarray_ref extends Primitive
659    {
660        pf_jarray_ref()
661        {
662            super("jarray-ref", PACKAGE_JAVA, true);
663        }
664
665        @Override
666        public LispObject execute(LispObject[] args)
667        {
668            return jarray_ref(this, args, true);
669        }
670    };
671
672    private static final Primitive JARRAY_REF_RAW = new pf_jarray_ref_raw();
673    @DocString(name="jarray-ref-raw", args="java-array &rest indices",
674    doc="Dereference the Java array JAVA-ARRAY using the given INDICIES. " +
675        "Does not attempt to coerce the result into a Lisp object.")
676    private static final class pf_jarray_ref_raw extends Primitive
677    {
678        pf_jarray_ref_raw() 
679        {
680            super("jarray-ref-raw", PACKAGE_JAVA, true);
681        }
682
683        @Override
684        public LispObject execute(LispObject[] args)
685        {
686            return jarray_ref(this, args, false);
687        }
688    };
689
690    private static final Primitive JARRAY_SET = new pf_jarray_set();
691    @DocString(name="jarray-set", args="java-array new-value &rest indices",
692    doc="Stores NEW-VALUE at the given index in JAVA-ARRAY.")
693    private static final class pf_jarray_set extends Primitive
694    {
695        pf_jarray_set()
696        {
697            super("jarray-set", PACKAGE_JAVA, true);
698        }
699
700        @Override
701        public LispObject execute(LispObject[] args)
702        {
703            if (args.length < 3)
704                error(new WrongNumberOfArgumentsException(this));
705            try {
706                Object a = args[0].javaInstance();
707                LispObject v = args[1];
708                for (int i = 2; i<args.length - 1; i++)
709                    a = Array.get(a, ((Integer)args[i].javaInstance()).intValue());
710                Array.set(a, ((Integer)args[args.length - 1].javaInstance()).intValue(), v.javaInstance());
711                return v;
712            }
713            catch (Throwable t) { // no code -> no ControlTransfer
714                Symbol condition = getCondition(t.getClass());
715                if (condition == null)
716                    error(new JavaException(t));
717                else
718                    Symbol.SIGNAL.execute(
719                        condition,
720                        Keyword.CAUSE,
721                        JavaObject.getInstance(t),
722                        Keyword.FORMAT_CONTROL,
723                        new SimpleString(getMessage(t)));
724            }
725            // Not reached.
726            return NIL;
727        }
728    };
729
730    /**  Calls makeLispObject() to convert the result to an appropriate Lisp type. */
731    private static final Primitive JCALL = new pf_jcall();
732    @DocString(name="jcall", args="method-ref instance &rest args",
733    doc="Invokes the Java method METHOD-REF on INSTANCE with arguments ARGS," +
734        " coercing the result into a Lisp object, if possible.")
735    private static final class pf_jcall extends Primitive
736    {
737        pf_jcall()
738        {
739            super(Symbol.JCALL);
740        }
741
742        @Override
743        public LispObject execute(LispObject[] args)
744        {
745            return jcall(this, args, true);
746        }
747    };
748
749    /**
750     * Does no type conversion. The result of the call is simply wrapped in a
751     *   JavaObject.
752     */
753    private static final Primitive JCALL_RAW = new pf_jcall_raw();
754    @DocString(name="jcall-raw", args="method-ref instance &rest args",
755    doc="Invokes the Java method METHOD-REF on INSTANCE with arguments ARGS." +
756        " Does not attempt to coerce the result into a Lisp object.")
757    private static final class pf_jcall_raw extends Primitive
758    {
759        pf_jcall_raw()
760        {
761            super(Symbol.JCALL_RAW);
762        }
763
764        @Override
765        public LispObject execute(LispObject[] args)
766        {
767            return jcall(this, args, false);
768        }
769    };
770
771    static LispObject jcall(Primitive fun, LispObject[] args, boolean translate)
772
773    {
774        if (args.length < 2)
775            error(new WrongNumberOfArgumentsException(fun));
776        try {
777      final LispObject methodArg = args[0];
778      final LispObject instanceArg = args[1];
779      final Object instance;
780      Class<?> intendedClass = null;
781      if (instanceArg instanceof AbstractString) {
782    instance = instanceArg.getStringValue();
783      } else if (instanceArg instanceof JavaObject) {
784    JavaObject jobj = ((JavaObject)instanceArg);
785    instance = jobj.getObject();
786    intendedClass = jobj.getIntendedClass();
787      } else {
788    instance = instanceArg.javaInstance();
789      }
790      if(instance == null) {
791    throw new NullPointerException(); //Handled below
792      }
793            Method method;
794      Object[] methodArgs;
795            if (methodArg instanceof AbstractString) {
796    methodArgs = translateMethodArguments(args, 2);
797                String methodName = methodArg.getStringValue();
798    if(intendedClass == null) {
799        intendedClass = instance.getClass();
800    }
801                method = findMethod(intendedClass, methodName, methodArgs);
802    Class actualClass = null;
803    if(method == null) {       
804        actualClass = instance.getClass();
805        if(intendedClass != actualClass &&
806           Modifier.isPublic(actualClass.getModifiers())) {
807      method = findMethod(actualClass, methodName, methodArgs);
808        }
809    }
810    if (method == null) {
811        String classes = intendedClass.getName();
812        if(actualClass != null && actualClass != intendedClass) {
813      classes += " or " + actualClass.getName();
814        }
815        throw new NoSuchMethodException("No applicable method named " + methodName + " found in " + classes);
816    }
817
818            } else
819                method = (Method) JavaObject.getObject(methodArg);
820            Class<?>[] argTypes = (Class<?>[])method.getParameterTypes();
821      if(argTypes.length != args.length - 2) {
822    return error(new WrongNumberOfArgumentsException("Wrong number of arguments for " + method + ": expected " + argTypes.length + ", got " + (args.length - 2)));
823      }
824            methodArgs = new Object[argTypes.length];
825            for (int i = 2; i < args.length; i++) {
826                LispObject arg = args[i];
827                if (arg == NIL)
828                    methodArgs[i-2] = null;
829                else
830                    methodArgs[i-2] = arg.javaInstance(argTypes[i-2]);
831            }
832            return JavaObject.getInstance(method.invoke(instance, methodArgs),
833                                          translate,
834            method.getReturnType());
835        }
836        catch (ControlTransfer t) {
837            throw t;
838        }
839        catch (Throwable t) { // ControlTransfer handled above
840            if (t instanceof InvocationTargetException)
841                t = t.getCause();
842            Symbol condition = getCondition(t.getClass());
843            if (condition == null)
844                error(new JavaException(t));
845            else
846                Symbol.SIGNAL.execute(
847                    condition,
848                    Keyword.CAUSE,
849                    JavaObject.getInstance(t),
850                    Keyword.FORMAT_CONTROL,
851                    new SimpleString(getMessage(t)));
852        }
853        // Not reached.
854        return null;
855    }
856
857    private static Object[] translateMethodArguments(LispObject[] args) {
858  return translateMethodArguments(args, 0);
859    }
860
861    private static Object[] translateMethodArguments(LispObject[] args, int offs) {
862  int argCount = args.length - offs;
863        Object[] javaArgs = new Object[argCount];
864        for (int i = 0; i < argCount; ++i) {
865            Object x = args[i + offs];
866            if (x == NIL) {
867                javaArgs[i] = null;
868            } else {
869                javaArgs[i] = ((LispObject) x).javaInstance();
870            }
871        }
872  return javaArgs;
873    }
874
875    private static Method findMethod(Method[] methods, String methodName, Object[] javaArgs) {
876  int argCount = javaArgs.length;
877        Method result = null;
878        for (int i = methods.length; i-- > 0;) {
879            Method method = methods[i];
880            if (!method.getName().equals(methodName)) {
881                continue;
882            }
883            if (method.getParameterTypes().length != argCount) {
884                continue;
885            }
886            Class<?>[] methodTypes = (Class<?>[]) method.getParameterTypes();
887            if (!isApplicableMethod(methodTypes, javaArgs)) {
888                continue;
889            }
890            if (result == null || isMoreSpecialized(methodTypes, result.getParameterTypes())) {
891                result = method;
892            }
893        }
894        return result;
895    }
896
897    private static Method findMethod(Class<?> c, String methodName, Object[] javaArgs) {
898        Method[] methods = c.getMethods();
899  return findMethod(methods, methodName, javaArgs);
900    }
901
902    private static Method findMethod(Class<?> c, String methodName, LispObject[] args) {
903        Object[] javaArgs = translateMethodArguments(args, 2);
904  return findMethod(c, methodName, javaArgs);
905    }
906
907    private static Method findMethod(Method[] methods, String methodName, LispObject[] args) {
908        Object[] javaArgs = translateMethodArguments(args, 2);
909  return findMethod(methods, methodName, javaArgs);
910    }
911
912    static Constructor findConstructor(Class<?> c, LispObject[] args) throws NoSuchMethodException {
913  int argCount = args.length - 1;
914        Object[] javaArgs = translateMethodArguments(args, 1);
915        Constructor[] ctors = c.getConstructors();
916        Constructor result = null;
917        for (int i = ctors.length; i-- > 0;) {
918            Constructor ctor = ctors[i];
919            if (ctor.getParameterTypes().length != argCount) {
920                continue;
921            }
922            Class<?>[] methodTypes = (Class<?>[]) ctor.getParameterTypes();
923            if (!isApplicableMethod(methodTypes, javaArgs)) {
924                continue;
925            }
926            if (result == null || isMoreSpecialized(methodTypes, result.getParameterTypes())) {
927                result = ctor;
928            }
929        }
930        if (result == null) {
931      StringBuilder sb = new StringBuilder(c.getSimpleName());
932      sb.append('(');
933      boolean first = true;
934      for(Object o : javaArgs) {
935    if(first) {
936        first = false;
937    } else {
938        sb.append(", ");
939    }
940    if(o != null) {
941        sb.append(o.getClass().getName());
942    } else {
943        sb.append("<null>");
944    }
945      }
946      sb.append(')');
947            throw new NoSuchMethodException(sb.toString());
948        }
949        return result;
950    }
951
952    private static boolean isApplicableMethod(Class<?>[] methodTypes,
953            Object[] args) {
954        for (int i = 0; i < methodTypes.length; ++i) {
955            Class<?> methodType = methodTypes[i];
956            Object arg = args[i];
957            if (methodType.isPrimitive()) {
958                Class<?> x = getBoxedClass(methodType);
959                if (!x.isInstance(arg)) {
960                    return false;
961                }
962            } else if (arg != null && !methodType.isInstance(arg)) {
963                return false;
964            }
965        }
966        return true;
967    }
968
969    private static boolean isMoreSpecialized(Class<?>[] xtypes, Class<?>[] ytypes) {
970        for (int i = 0; i < xtypes.length; ++i) {
971            Class<?> xtype = xtypes[i];
972            if (xtype.isPrimitive()) {
973                xtype = getBoxedClass(xtype);
974            }
975            Class<?> ytype = ytypes[i];
976            if (ytype.isPrimitive()) {
977                ytype = getBoxedClass(ytype);
978            }
979            if (xtype.equals(ytype)) {
980                continue;
981            }
982            if (ytype.isAssignableFrom(xtype)) {
983                return true;
984            }
985        }
986        return false;
987    }
988
989    public static Class<?> maybeBoxClass(Class<?> clazz) {
990  if(clazz.isPrimitive()) {
991      return getBoxedClass(clazz);
992  } else {
993      return clazz;
994  }
995    }
996   
997    private static Class<?> getBoxedClass(Class<?> clazz) {
998        if (clazz.equals(int.class)) {
999            return Integer.class;
1000        } else if (clazz.equals(boolean.class)) {
1001            return Boolean.class;
1002        } else if (clazz.equals(byte.class)) {
1003            return Byte.class;
1004        } else if (clazz.equals(char.class)) {
1005            return Character.class;
1006        } else if (clazz.equals(long.class)) {
1007            return Long.class;
1008        } else if (clazz.equals(float.class)) {
1009            return Float.class;
1010        } else if (clazz.equals(double.class)) {
1011            return Double.class;
1012        } else if (clazz.equals(short.class)) {
1013            return Short.class;
1014        } else { // if (methodType.equals(void.class))
1015            return Void.class;
1016        }
1017    }
1018
1019    private static final Primitive MAKE_IMMEDIATE_OBJECT = new pf_make_immediate_object();
1020    @DocString(name="make-immediate-object", args="object &optional type",
1021    doc="Attempts to coerce a given Lisp object into a java-object of the\n"+
1022        "given type.  If type is not provided, works as jobject-lisp-value.\n"+
1023        "Currently, type may be :BOOLEAN, treating the object as a truth value,\n"+
1024        "or :REF, which returns Java null if NIL is provided.")
1025    private static final class pf_make_immediate_object extends Primitive
1026    {
1027        pf_make_immediate_object()
1028        {
1029            super("make-immediate-object", PACKAGE_JAVA, true);
1030        }
1031
1032        @Override
1033        public LispObject execute(LispObject[] args)
1034        {
1035            if (args.length < 1)
1036                error(new WrongNumberOfArgumentsException(this));
1037            LispObject object = args[0];
1038            if (args.length > 1) {
1039                LispObject type = args[1];
1040                if (type == Keyword.BOOLEAN) {
1041                    if (object == NIL)
1042                        return JavaObject.getInstance(Boolean.FALSE);
1043                    else
1044                        return JavaObject.getInstance(Boolean.TRUE);
1045                }
1046                if (type == Keyword.REF) {
1047                    if (object == NIL)
1048                        return JavaObject.getInstance(null);
1049                    else
1050                        error(new LispError("MAKE-IMMEDIATE-OBJECT: not implemented"));
1051                }
1052                // other special cases come here
1053            }
1054            return JavaObject.getInstance(object.javaInstance());
1055        }
1056    };
1057
1058    private static final Primitive JAVA_OBJECT_P = new pf_java_object_p();
1059    @DocString(name="java-object-p", args="object",
1060    doc="Returns T if OBJECT is a JAVA-OBJECT.")
1061    private static final class pf_java_object_p extends Primitive
1062    {
1063        pf_java_object_p() 
1064        {
1065            super("java-object-p", PACKAGE_JAVA, true);
1066        }
1067
1068        @Override
1069        public LispObject execute(LispObject arg)
1070        {
1071            return (arg instanceof JavaObject) ? T : NIL;
1072        }
1073    };
1074
1075    private static final Primitive JOBJECT_LISP_VALUE = new pf_jobject_lisp_value();
1076    @DocString(name="jobject-lisp-value", args="java-object",
1077    doc="Attempts to coerce JAVA-OBJECT into a Lisp object.")
1078    private static final class pf_jobject_lisp_value extends Primitive
1079    {
1080        pf_jobject_lisp_value()
1081        {
1082            super("jobject-lisp-value", PACKAGE_JAVA, true, "java-object");
1083        }
1084
1085        @Override
1086        public LispObject execute(LispObject arg)
1087        {
1088            return JavaObject.getInstance(arg.javaInstance(), true);
1089        }
1090    };
1091
1092    private static final Primitive JCOERCE = new pf_jcoerce();
1093    @DocString(name="jcoerce", args="object intended-class",
1094    doc="Attempts to coerce OBJECT into a JavaObject of class INTENDED-CLASS." +
1095        "  Raises a TYPE-ERROR if no conversion is possible.")
1096    private static final class pf_jcoerce extends Primitive
1097    {
1098        pf_jcoerce()
1099        {
1100            super("jcoerce", PACKAGE_JAVA, true);
1101        }
1102
1103        @Override
1104        public LispObject execute(LispObject javaObject, LispObject intendedClass)
1105        {
1106      Object o = javaObject.javaInstance();
1107      Class<?> c = javaClass(intendedClass);
1108      try {
1109    return JavaObject.getInstance(o, c);
1110      } catch(ClassCastException e) {
1111    return error(new TypeError(javaObject, new SimpleString(c.getName())));
1112      }
1113        }
1114    };
1115
1116    private static final Primitive JGET_PROPERTY_VALUE = new pf__jget_property_value();
1117    @DocString(name="%jget-propety-value", args="java-object property-name",
1118    doc="Gets a JavaBeans property on JAVA-OBJECT.\n" +
1119        "SYSTEM-INTERNAL: Use jproperty-value instead.")
1120    private static final class pf__jget_property_value extends Primitive
1121    {
1122        pf__jget_property_value() 
1123        {
1124      super("%jget-property-value", PACKAGE_JAVA, true,
1125                  "java-object property-name");
1126        }
1127     
1128        @Override
1129        public LispObject execute(LispObject javaObject, LispObject propertyName) {
1130      try {
1131        Object obj = javaObject.javaInstance();
1132        PropertyDescriptor pd = getPropertyDescriptor(obj, propertyName);
1133        Object value = pd.getReadMethod().invoke(obj);
1134        if(value instanceof LispObject) {
1135            return (LispObject) value;
1136        } else if(value != null) {
1137            return JavaObject.getInstance(value, true);
1138        } else {
1139            return NIL;
1140        }
1141      } catch (Exception e) {
1142                return error(new JavaException(e));
1143      }
1144        }
1145    };
1146   
1147    private static final Primitive JSET_PROPERTY_VALUE = new pf__jset_property_value();
1148    @DocString(name="%jset-propety-value", args="java-object property-name value",
1149    doc="Sets a JavaBean property on JAVA-OBJECT.\n" +
1150        "SYSTEM-INTERNAL: Use (setf jproperty-value) instead.")
1151    private static final class pf__jset_property_value extends Primitive
1152    {
1153        pf__jset_property_value()
1154        {
1155      super("%jset-property-value", PACKAGE_JAVA, true,
1156                  "java-object property-name value");
1157        }
1158     
1159        @Override
1160        public LispObject execute(LispObject javaObject, LispObject propertyName, LispObject value) {
1161      Object obj = null;
1162      try {
1163    obj = javaObject.javaInstance();
1164    PropertyDescriptor pd = getPropertyDescriptor(obj, propertyName);
1165    Object jValue;
1166    //TODO maybe we should do this in javaInstance(Class)
1167    if(value instanceof JavaObject) {
1168        jValue = value.javaInstance();
1169    } else {
1170        if(Boolean.TYPE.equals(pd.getPropertyType()) ||
1171           Boolean.class.equals(pd.getPropertyType())) {
1172      jValue = value != NIL;
1173        } else {
1174      jValue = value != NIL ? value.javaInstance() : null;
1175        }
1176    }
1177    pd.getWriteMethod().invoke(obj, jValue);
1178    return value;
1179      } catch (Exception e) {
1180            return error(new JavaException(e));
1181      }
1182        }
1183    };
1184
1185    private static final Primitive JRUN_EXCEPTION_PROTECTED = new pf_jrun_exception_protection();
1186    @DocString(name="jrun-exception-protected", args="closure",
1187    doc="Invokes the function CLOSURE and returns the result.  "+
1188        "Signals an error if stack or heap exhaustion occurs.")
1189    private static final class pf_jrun_exception_protection extends Primitive
1190    {
1191        pf_jrun_exception_protection()
1192        {
1193            super("jrun-exception-protected", PACKAGE_JAVA, true);
1194        }
1195
1196        @Override
1197        public LispObject execute(LispObject closure) {
1198            Function fun = checkFunction(closure);
1199
1200            try {
1201                return LispThread.currentThread().execute(closure);
1202            }
1203            catch (OutOfMemoryError oom) {
1204                return error(new StorageCondition("Out of memory " + oom.getMessage()));
1205            }
1206            catch (StackOverflowError oos) {
1207                return error(new StorageCondition("Stack overflow."));
1208            }
1209        }
1210    };
1211
1212    static PropertyDescriptor getPropertyDescriptor(Object obj, LispObject propertyName) throws IntrospectionException {
1213        String prop = ((AbstractString) propertyName).getStringValue();
1214        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
1215        for(PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
1216          if(pd.getName().equals(prop)) {
1217            return pd;
1218          }
1219        }
1220        error(new LispError("Property " + prop + " not found in " + obj));
1221
1222        return null; // not reached
1223    }
1224   
1225    private static Class classForName(String className) {
1226  return classForName(className, JavaClassLoader.getPersistentInstance());
1227    }
1228
1229    private static Class classForName(String className, ClassLoader classLoader) {
1230        try {
1231            return Class.forName(className, true, classLoader);
1232        }
1233        catch (ClassNotFoundException e) {
1234      error(new LispError("Class not found: " + className));
1235      // Not reached.
1236      return null;
1237        }
1238    }
1239
1240    private static Class javaClass(LispObject obj) {
1241  return javaClass(obj, JavaClassLoader.getCurrentClassLoader());
1242    }
1243
1244    // Supports Java primitive types too.
1245    static Class javaClass(LispObject obj, ClassLoader classLoader)
1246    {
1247        if (obj instanceof AbstractString || obj instanceof Symbol) {
1248            String s = javaString(obj);
1249            if (s.equals("boolean"))
1250                return Boolean.TYPE;
1251            if (s.equals("byte"))
1252                return Byte.TYPE;
1253            if (s.equals("char"))
1254                return Character.TYPE;
1255            if (s.equals("short"))
1256                return Short.TYPE;
1257            if (s.equals("int"))
1258                return Integer.TYPE;
1259            if (s.equals("long"))
1260                return Long.TYPE;
1261            if (s.equals("float"))
1262                return Float.TYPE;
1263            if (s.equals("double"))
1264                return Double.TYPE;
1265            // Not a primitive Java type.
1266            Class c;
1267      c = classForName(s, classLoader);
1268            if (c == null)
1269                error(new LispError(s + " does not designate a Java class."));
1270
1271            return c;
1272        }
1273        // It's not a string, so it must be a JavaObject.
1274        final JavaObject javaObject;
1275        if (obj instanceof JavaObject) {
1276            javaObject = (JavaObject) obj;
1277        }
1278        else {
1279            type_error(obj, list(Symbol.OR, Symbol.STRING,
1280                                       Symbol.JAVA_OBJECT));
1281            // Not reached.
1282            return null;
1283        }
1284        final Object javaObjectgetObject = javaObject.getObject();
1285        if (javaObjectgetObject instanceof Class) {
1286            return (Class) javaObjectgetObject;
1287        }
1288            error(new LispError(obj.writeToString() + " does not designate a Java class."));
1289            return null;
1290    }
1291
1292    static final String getMessage(Throwable t)
1293    {
1294        String message = t.getMessage();
1295        if (message == null || message.length() == 0)
1296            message = t.getClass().getName();
1297        return message;
1298    }
1299}
Note: See TracBrowser for help on using the repository browser.