source: branches/1.1.x/src/org/armedbear/lisp/JProxy.java

Last change on this file was 13461, checked in by ehuelsmann, 13 years ago

Print expected minimum and maximum argument list lengths in
WrongNumberOfArguments? program errors.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.6 KB
Line 
1/*
2 * JProxy.java
3 *
4 * Copyright (C) 2002-2005 Peter Graves, Andras Simon
5 * $Id: JProxy.java 13461 2011-08-11 17:01:41Z ehuelsmann $
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.lang.reflect.InvocationHandler;
39import java.lang.reflect.Method;
40import java.lang.reflect.Proxy;
41import java.util.HashMap;
42import java.util.Map;
43import java.util.WeakHashMap;
44
45public final class JProxy
46{
47  static final Map<Object,Entry> table = new WeakHashMap<Object,Entry>();
48
49  // ### %jnew-proxy interface &rest method-names-and-defs
50  private static final Primitive _JNEW_PROXY =
51    new Primitive("%jnew-proxy", PACKAGE_JAVA, false,
52                  "interface &rest method-names-and-defs")
53    {
54      @Override
55      public LispObject execute(LispObject[] args)
56      {
57        int length = args.length;
58        if (length < 3 || length % 2 != 1)
59          return error(new WrongNumberOfArgumentsException(this));
60        Map<String,Function> lispDefinedMethods = new HashMap<String,Function>();
61        for (int i = 1; i < length; i += 2)
62          lispDefinedMethods.put(args[i].getStringValue(),
63                                 (Function) args[i + 1]);
64        Class iface = (Class) args[0].javaInstance();
65        Object proxy = Proxy.newProxyInstance(iface.getClassLoader(),
66                                              new Class[] { iface },
67                                              new LispHandler(table));
68        table.put(proxy, new Entry(iface, lispDefinedMethods));
69        return new JavaObject(proxy);
70      }
71    };
72
73  private static class LispHandler implements InvocationHandler
74  {
75    Map table;
76
77    LispHandler (Map table)
78    {
79      this.table = table;
80    }
81
82    public Object invoke(Object proxy, Method method, Object[] args)
83    {
84      String methodName = method.getName();
85
86      if (methodName.equals("hashCode"))
87          return new Integer(System.identityHashCode(proxy));
88      if (methodName.equals("equals"))
89        return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
90      if (methodName.equals("toString"))
91        return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
92
93      if (table.containsKey(proxy))
94        {
95          Entry entry = (Entry) table.get(proxy);
96          Function f = entry.getLispMethod(methodName);
97          if (f != null)
98            {
99              LispObject lispArgs = NIL;
100              if (args != null)
101                {
102                  for (int i = args.length - 1 ; 0 <= i  ; i--)
103                    lispArgs = lispArgs.push(new JavaObject(args[i]));
104                }
105              LispObject result = evalCall(f, lispArgs, new Environment(),
106                                           LispThread.currentThread());
107              return (method.getReturnType() == void.class ? null : result.javaInstance());
108            }
109        }
110      return null;
111    }
112  }
113
114  private static class Entry
115  {
116    Class iface;
117    Map lispDefinedMethods;
118
119    public Entry (Class iface, Map lispDefinedMethods)
120    {
121      this.iface = iface;
122      this.lispDefinedMethods = lispDefinedMethods;
123    }
124
125    public Function getLispMethod(String methodName)
126    {
127      if (lispDefinedMethods.containsKey(methodName))
128        return (Function)lispDefinedMethods.get(methodName);
129      return null;
130    }
131  }
132 
133    //NEW IMPLEMENTATION by Alessio Stalla
134 
135    /**
136     * A weak map associating each proxy instance with a "Lisp-this" object.
137     */
138    static final Map<Object, LispObject> proxyMap = new WeakHashMap<Object, LispObject>();
139 
140    public static class LispInvocationHandler implements InvocationHandler {
141 
142  private Function function;
143  private static Method hashCodeMethod;
144  private static Method equalsMethod;
145  private static Method toStringMethod;
146   
147  static {
148      try {
149    hashCodeMethod = Object.class.getMethod("hashCode", new Class[] {});
150    equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class });
151    toStringMethod = Object.class.getMethod("toString", new Class[] {});
152      } catch (Exception e) {
153    throw new Error("Something got horribly wrong - can't get a method from Object.class", e);
154      }
155  }
156 
157  public LispInvocationHandler(Function function) {
158      this.function = function;
159  }
160     
161  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
162      if(hashCodeMethod.equals(method)) {
163    return System.identityHashCode(proxy);
164      }
165      if(equalsMethod.equals(method)) {
166    return proxy == args[0];
167      }
168      if(toStringMethod.equals(method)) {
169    return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
170      }
171       
172      if(args == null) {
173    args = new Object[0];
174      }
175      LispObject lispArgs = NIL;
176      synchronized(proxyMap) {
177    lispArgs = lispArgs.push(toLispObject(proxyMap.get(proxy)));
178      }
179      lispArgs = lispArgs.push(new SimpleString(method.getName()));
180      for(int i = 0; i < args.length; i++) {
181    lispArgs = lispArgs.push(toLispObject(args[i]));
182      }
183      Object retVal =
184    LispThread.currentThread().execute
185    (Symbol.APPLY, function, lispArgs.reverse()).javaInstance();
186      //(function.execute(lispArgs)).javaInstance();
187      /* DOES NOT WORK due to autoboxing!
188         if(retVal != null && !method.getReturnType().isAssignableFrom(retVal.getClass())) {
189         return error(new TypeError(new JavaObject(retVal), new JavaObject(method.getReturnType())));
190         }*/
191      return retVal;
192  }
193    }
194 
195    private static final Primitive _JMAKE_INVOCATION_HANDLER =
196      new Primitive("%jmake-invocation-handler", PACKAGE_JAVA, false,
197                    "function") {
198   
199          public LispObject execute(LispObject[] args) {
200            int length = args.length;
201            if (length != 1) {
202              return error(new WrongNumberOfArgumentsException(this, 1));
203            }
204            if(!(args[0] instanceof Function)) {
205              return error(new TypeError(args[0], Symbol.FUNCTION));
206            }
207            return new JavaObject(new LispInvocationHandler((Function) args[0]));
208          }
209      };
210
211    private static final Primitive _JMAKE_PROXY =
212      new Primitive("%jmake-proxy", PACKAGE_JAVA, false,
213                    "interfaces invocation-handler") {
214   
215          public LispObject execute(final LispObject[] args) {
216            int length = args.length;
217            if (length != 3) {
218              return error(new WrongNumberOfArgumentsException(this, 3));
219            }
220            if(!(args[0] instanceof Cons)) {
221          return error(new TypeError(args[0], new SimpleString("CONS")));
222            }
223      Class[] ifaces = new Class[args[0].length()];
224      LispObject ifList = args[0];
225      for(int i = 0; i < ifaces.length; i++) {
226                          ifaces[i] = (Class) ifList.car().javaInstance(Class.class);
227          ifList = ifList.cdr();
228      }
229            InvocationHandler invocationHandler = (InvocationHandler) ((JavaObject) args[1]).javaInstance(InvocationHandler.class);
230            Object proxy = Proxy.newProxyInstance(
231                JavaClassLoader.getCurrentClassLoader(),
232                ifaces,
233                invocationHandler);
234            synchronized(proxyMap) {
235              proxyMap.put(proxy, args[2]);
236            }
237            return new JavaObject(proxy);
238          }
239      };   
240     
241  static LispObject toLispObject(Object obj) {
242    return (obj instanceof LispObject) ? (LispObject) obj : new JavaObject(obj);
243  }
244     
245}
Note: See TracBrowser for help on using the repository browser.