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

Last change on this file was 11859, checked in by astalla, 16 years ago

Corrected previous commit: JProxy uses APPLY and not FUNCALL.

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