001 package org.bridj.objc;
002
003 import java.lang.reflect.Method;
004 import java.lang.reflect.Type;
005 import org.bridj.*;
006 import static org.bridj.Pointer.*;
007 import static org.bridj.objc.ObjCJNI.*;
008 import static org.bridj.Platform.*;
009 import static org.bridj.objc.ObjectiveCRuntime.*;
010 import java.util.*;
011 import org.bridj.util.Pair;
012 import org.bridj.util.Utils;
013
014 public class ObjCProxy extends ObjCObject {
015 final Map<SEL, Pair<NSMethodSignature, Method>> signatures = new HashMap<SEL, Pair<NSMethodSignature, Method>>();
016 final Object invocationTarget;
017 final static String PROXY_OBJC_CLASS_NAME = "ObjCProxy";
018
019 protected ObjCProxy() {
020 super(null);
021 peer = createObjCProxyPeer(this);
022 assert getClass() != ObjCProxy.class;
023 this.invocationTarget = this;
024 }
025 public ObjCProxy(Object invocationTarget) {
026 super(null);
027 peer = createObjCProxyPeer(this);
028 assert invocationTarget != null;
029 this.invocationTarget = invocationTarget;
030 }
031
032 public void addProtocol(String name) throws ClassNotFoundException {
033 Pointer<? extends ObjCObject> protocol = objc_getProtocol(pointerToCString(name));
034 if (protocol == null)
035 throw new ClassNotFoundException("Protocol " + name + " not found !");
036 Pointer<? extends ObjCObject> cls = getObjCClass(PROXY_OBJC_CLASS_NAME);
037 if (!class_addProtocol(cls, protocol))
038 throw new RuntimeException("Failed to add protocol " + name + " to class " + PROXY_OBJC_CLASS_NAME);
039 }
040 public Object getInvocationTarget() {
041 return invocationTarget;
042 }
043 public Pointer<NSMethodSignature> methodSignatureForSelector(SEL sel) {
044 Pair<NSMethodSignature, Method> sig = getMethodAndSignature(sel);
045 return sig == null ? null : pointerTo(sig.getFirst());
046 }
047 public synchronized Pair<NSMethodSignature, Method> getMethodAndSignature(SEL sel) {
048 Pair<NSMethodSignature, Method> sig = signatures.get(sel);
049 if (sig == null) {
050 try {
051 sig = computeMethodAndSignature(sel);
052 if (sig != null)
053 signatures.put(sel, sig);
054 } catch (Throwable th) {
055 BridJ.error("Failed to compute Objective-C signature for selector " + sel + ": " + th, th);
056 }
057 }
058 return sig;
059 }
060 Pair<NSMethodSignature, Method> computeMethodAndSignature(SEL sel) {
061 String name = sel.getName();
062 ObjectiveCRuntime rt = ObjectiveCRuntime.getInstance();
063 for (Method method : invocationTarget.getClass().getMethods()) {
064 String msel = rt.getSelector(method);
065 //System.out.println("Selector for method " + method.getName() + " is '" + msel + "' (vs. sel = '" + sel + "')");
066 if (msel.equals(name)) {
067 String sig = rt.getMethodSignature(method);
068 if (BridJ.debug)
069 BridJ.info("Objective-C signature for method " + method + " = '" + sig + "'");
070 NSMethodSignature ms = NSMethodSignature.signatureWithObjCTypes(pointerToCString(sig)).get();
071 long nArgs = ms.numberOfArguments() - 2;
072 if (nArgs != method.getParameterTypes().length)
073 throw new RuntimeException("Bad method signature (mismatching arg types) : '" + sig + "' for " + method);
074 return new Pair<NSMethodSignature, Method>(ms, method);
075 }
076 }
077 //if (BridJ.debug)
078 BridJ.error("Missing method for " + sel + " in class " + classHierarchyToString(getInvocationTarget().getClass()));
079
080 return null;
081 }
082 static String classHierarchyToString(Class c) {
083 String s = Utils.toString(c);
084 Type p = c.getGenericSuperclass();
085 while (p != null && p != Object.class && p != ObjCProxy.class) {
086 s += " extends " + Utils.toString(p);
087 p = Utils.getClass(p).getGenericSuperclass();
088 }
089 return s;
090 }
091 /*
092 static Type promote(Type type) {
093 if (type == byte.class || type == short.class || type == char.class || type == boolean.class)
094 return int.class;
095 if (type == float.class)
096 return double.class;
097 return type;
098 }
099 */
100 /*
101 static final Map<Class, Class> wrapperClasses = new HashMap<Class, Class>();
102 static {
103 wrapperClasses.put(int.class, Integer.class);
104 wrapperClasses.put(short.class, Short.class);
105 wrapperClasses.put(long.class, Long.class);
106 wrapperClasses.put(char.class, Character.class);
107 wrapperClasses.put(byte.class, Byte.class);
108 wrapperClasses.put(boolean.class, Boolean.class);
109 wrapperClasses.put(double.class, Double.class);
110 wrapperClasses.put(float.class, Float.class);
111 }
112 */
113 /*
114 static Object constrain(Object value, Type type) {
115 Class c = Utils.getClass(type);
116 if (c.isPrimitive())
117 c = wrapperClasses.get(c);
118 if (c.isInstance(value))
119 return value;
120
121 // Assume value is an Integer or a Double
122 if (type == byte.class)
123 return ((Integer)value).byteValue();
124 if (type == short.class)
125 return ((Integer)value).shortValue();
126 if (type == char.class)
127 return (char)((Integer)value).shortValue();
128 if (type == boolean.class)
129 return ((Integer)value).byteValue() != 0;
130 if (type == float.class)
131 return ((Double)value).floatValue();
132
133 throw new UnsupportedOperationException("Don't know how to constrain value " + value + " to type " + Utils.toString(type));
134 }
135 */
136 public synchronized void forwardInvocation(Pointer<NSInvocation> pInvocation) {
137 NSInvocation invocation = pInvocation.get();
138 SEL sel = invocation.selector();
139 Pair<NSMethodSignature, Method> sigMet = getMethodAndSignature(sel);
140 NSMethodSignature sig = sigMet.getFirst();
141 Method method = sigMet.getSecond();
142
143 //System.out.println("forwardInvocation(" + invocation + ") : sel = " + sel);
144 Type[] paramTypes = method.getGenericParameterTypes();
145 int nArgs = paramTypes.length;//(int)sig.numberOfArguments();
146 Object[] args = new Object[nArgs];
147 for (int i = 0; i < nArgs; i++) {
148 Type paramType = paramTypes[i];
149 PointerIO<?> paramIO = PointerIO.getInstance(paramType);//promote(paramType));
150 Pointer<?> pArg = allocate(paramIO);
151 invocation.getArgument_atIndex(pArg, i + 2);
152 Object arg = pArg.get();
153 args[i] = arg;//constrain(arg, paramType);
154 }
155 try {
156 method.setAccessible(true);
157 Object ret = method.invoke(getInvocationTarget(), args);
158 //System.out.println("Invoked " + method + " : " + ret);
159 Type returnType = method.getGenericReturnType();
160 if (returnType == void.class) {
161 assert ret == null;
162 } else {
163 PointerIO<?> returnIO = PointerIO.getInstance(returnType);
164 Pointer<Object> pRet = (Pointer)allocate(returnIO);
165 pRet.set(ret);
166 invocation.setReturnValue(pRet);
167 }
168 } catch (Throwable ex) {
169 throw new RuntimeException("Failed to forward invocation from Objective-C to Java invocation target " + getInvocationTarget() + " for method " + method + " : " + ex, ex);
170 }
171 }
172
173
174 }
175