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

Last change on this file was 12255, checked in by ehuelsmann, 16 years ago

Rename ConditionThrowable? to ControlTransfer? and remove

try/catch blocks which don't have anything to do with
non-local transfer of control.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.7 KB
Line 
1/*
2 * JProxy.java
3 *
4 * Copyright (C) 2002-2005 Peter Graves, Andras Simon
5 * $Id: JProxy.java 12255 2009-11-06 22:36:32Z 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 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)
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              LispObject lispArgs = NIL;
98              if (args != null)
99                {
100                  for (int i = args.length - 1 ; 0 <= i  ; i--)
101                    lispArgs = lispArgs.push(new JavaObject(args[i]));
102                }
103              LispObject result = evalCall(f, lispArgs, new Environment(),
104                                           LispThread.currentThread());
105              return (method.getReturnType() == void.class ? null : result.javaInstance());
106            }
107        }
108      return null;
109    }
110  }
111
112  private static class Entry
113  {
114    Class iface;
115    Map lispDefinedMethods;
116
117    public Entry (Class iface, Map lispDefinedMethods)
118    {
119      this.iface = iface;
120      this.lispDefinedMethods = lispDefinedMethods;
121    }
122
123    public Function getLispMethod(String methodName)
124    {
125      if (lispDefinedMethods.containsKey(methodName))
126        return (Function)lispDefinedMethods.get(methodName);
127      return null;
128    }
129  }
130 
131    //NEW IMPLEMENTATION by Alessio Stalla
132 
133    /**
134     * A weak map associating each proxy instance with a "Lisp-this" object.
135     */
136    private static final Map<Object, LispObject> proxyMap = new WeakHashMap<Object, LispObject>();
137 
138    public static class LispInvocationHandler implements InvocationHandler {
139 
140  private Function function;
141  private static Method hashCodeMethod;
142  private static Method equalsMethod;
143  private static Method toStringMethod;
144   
145  static {
146      try {
147    hashCodeMethod = Object.class.getMethod("hashCode", new Class[] {});
148    equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class });
149    toStringMethod = Object.class.getMethod("toString", new Class[] {});
150      } catch (Exception e) {
151    throw new Error("Something got horribly wrong - can't get a method from Object.class", e);
152      }
153  }
154 
155  public LispInvocationHandler(Function function) {
156      this.function = function;
157  }
158     
159  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
160      if(hashCodeMethod.equals(method)) {
161    return System.identityHashCode(proxy);
162      }
163      if(equalsMethod.equals(method)) {
164    return proxy == args[0];
165      }
166      if(toStringMethod.equals(method)) {
167    return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
168      }
169       
170      if(args == null) {
171    args = new Object[0];
172      }
173      LispObject lispArgs = NIL;
174      synchronized(proxyMap) {
175    lispArgs = lispArgs.push(toLispObject(proxyMap.get(proxy)));
176      }
177      lispArgs = lispArgs.push(new SimpleString(method.getName()));
178      for(int i = 0; i < args.length; i++) {
179    lispArgs = lispArgs.push(toLispObject(args[i]));
180      }
181      Object retVal =
182    LispThread.currentThread().execute
183    (Symbol.APPLY, function, lispArgs.reverse()).javaInstance();
184      //(function.execute(lispArgs)).javaInstance();
185      /* DOES NOT WORK due to autoboxing!
186         if(retVal != null && !method.getReturnType().isAssignableFrom(retVal.getClass())) {
187         return error(new TypeError(new JavaObject(retVal), new JavaObject(method.getReturnType())));
188         }*/
189      return retVal;
190  }
191    }
192 
193    private static final Primitive _JMAKE_INVOCATION_HANDLER =
194      new Primitive("%jmake-invocation-handler", PACKAGE_JAVA, false,
195                    "function") {
196   
197          public LispObject execute(LispObject[] args) {
198            int length = args.length;
199            if (length != 1) {
200              return error(new WrongNumberOfArgumentsException(this));
201            }
202            if(!(args[0] instanceof Function)) {
203              return error(new TypeError(args[0], Symbol.FUNCTION));
204            }
205            return new JavaObject(new LispInvocationHandler((Function) args[0]));
206          }
207      };
208
209    private static final Primitive _JMAKE_PROXY =
210      new Primitive("%jmake-proxy", PACKAGE_JAVA, false,
211                    "interface invocation-handler") {
212   
213          public LispObject execute(final LispObject[] args) {
214            int length = args.length;
215            if (length != 3) {
216              return error(new WrongNumberOfArgumentsException(this));
217            }
218            if(!(args[0] instanceof JavaObject) ||
219               !(((JavaObject) args[0]).javaInstance() instanceof Class)) {
220              return error(new TypeError(args[0], new SimpleString(Class.class.getName())));
221            }
222            if(!(args[1] instanceof JavaObject) ||
223               !(((JavaObject) args[1]).javaInstance() instanceof InvocationHandler)) {
224              return error(new TypeError(args[1], new SimpleString(InvocationHandler.class.getName())));
225            }
226            Class<?> iface = (Class<?>) ((JavaObject) args[0]).javaInstance();
227            InvocationHandler invocationHandler = (InvocationHandler) ((JavaObject) args[1]).javaInstance();
228            Object proxy = Proxy.newProxyInstance(
229                iface.getClassLoader(),
230                new Class[] { iface },
231                invocationHandler);
232            synchronized(proxyMap) {
233              proxyMap.put(proxy, args[2]);
234            }
235            return new JavaObject(proxy);
236          }
237      };   
238     
239  private static LispObject toLispObject(Object obj) {
240    return (obj instanceof LispObject) ? (LispObject) obj : new JavaObject(obj);
241  }
242     
243}
Note: See TracBrowser for help on using the repository browser.