| 1 | /* |
|---|
| 2 | * JProxy.java |
|---|
| 3 | * |
|---|
| 4 | * Copyright (C) 2002-2005 Peter Graves, Andras Simon |
|---|
| 5 | * $Id: JProxy.java 14466 2013-04-24 12:50:40Z rschlatte $ |
|---|
| 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 | |
|---|
| 34 | package org.armedbear.lisp; |
|---|
| 35 | |
|---|
| 36 | import static org.armedbear.lisp.Lisp.*; |
|---|
| 37 | |
|---|
| 38 | import java.lang.reflect.InvocationHandler; |
|---|
| 39 | import java.lang.reflect.Method; |
|---|
| 40 | import java.lang.reflect.Proxy; |
|---|
| 41 | import java.util.HashMap; |
|---|
| 42 | import java.util.Map; |
|---|
| 43 | import java.util.WeakHashMap; |
|---|
| 44 | |
|---|
| 45 | public 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 type_error(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 type_error(args[0], Symbol.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 | } |
|---|