|
1 | 1 | /*
|
2 | 2 | * Copyright 2007, 2008 Duncan McGregor
|
3 |
| - * |
| 3 | + * |
4 | 4 | * This file is part of Rococoa, a library to allow Java to talk to Cocoa.
|
5 |
| - * |
| 5 | + * |
6 | 6 | * Rococoa is free software: you can redistribute it and/or modify
|
7 | 7 | * it under the terms of the GNU Lesser General Public License as published by
|
8 | 8 | * the Free Software Foundation, either version 3 of the License, or
|
9 | 9 | * (at your option) any later version.
|
10 |
| - * |
| 10 | + * |
11 | 11 | * Rococoa is distributed in the hope that it will be useful,
|
12 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16 | 16 | * You should have received a copy of the GNU Lesser General Public License
|
17 | 17 | * along with Rococoa. If not, see <http://www.gnu.org/licenses/>.
|
18 | 18 | */
|
19 |
| - |
20 |
| -package org.rococoa.internal; |
21 | 19 |
|
22 |
| -import java.lang.reflect.InvocationHandler; |
23 |
| -import java.lang.reflect.Method; |
24 |
| -import java.util.HashMap; |
25 |
| -import java.util.Map; |
| 20 | +package org.rococoa.internal; |
26 | 21 |
|
| 22 | +import com.sun.jna.Library; |
| 23 | +import com.sun.jna.NativeLibrary; |
| 24 | +import com.sun.jna.NativeLong; |
| 25 | +import com.sun.jna.Structure; |
27 | 26 | import org.rococoa.ID;
|
28 | 27 | import org.rococoa.RococoaException;
|
29 | 28 | import org.rococoa.Selector;
|
30 | 29 |
|
31 |
| -import com.sun.jna.Function; |
32 |
| -import com.sun.jna.Library; |
33 |
| -import com.sun.jna.NativeLong; |
34 |
| -import com.sun.jna.Structure; |
| 30 | +import java.lang.reflect.InvocationHandler; |
| 31 | +import java.lang.reflect.InvocationTargetException; |
| 32 | +import java.lang.reflect.Method; |
| 33 | +import java.util.Arrays; |
| 34 | +import java.util.Collections; |
| 35 | +import java.util.HashMap; |
| 36 | +import java.util.Map; |
35 | 37 |
|
36 | 38 | /**
|
37 | 39 | * Very special case InvocationHandler that invokes the correct message dispatch
|
38 | 40 | * function for different return types.
|
39 |
| - * |
| 41 | + * <p> |
40 | 42 | * Either objc_msgSend or objc_msgSend_stret should be called, depending on the
|
41 | 43 | * return type. The latter is usually for struct by value, but the former is
|
42 | 44 | * used for small structs on Intel! Oh and the call has to be mangled in all
|
43 | 45 | * cases as the result is returned on the stack, but is different sizes
|
44 | 46 | * depending on its type. Luckily jna and libffi take care of the details -
|
45 | 47 | * provided they know what the return type is.
|
46 |
| - * |
| 48 | + * <p> |
47 | 49 | * This InvocationHandler is passed the return type as the first arg to the method call that it
|
48 | 50 | * intercepts, it uses it to determine which function to call, and removes it before
|
49 | 51 | * calling invoking.
|
50 |
| - * |
| 52 | + * |
| 53 | + * @author duncan |
51 | 54 | * @see "http://www.cocoabuilder.com/archive/message/cocoa/2006/6/25/166236"
|
52 | 55 | * @see "http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/LowLevelABI/Mac_OS_X_ABI_Function_Calls.pdf"
|
53 | 56 | * @see "http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html"
|
54 |
| - * |
55 |
| - * Note also that there is a objc_msgSend_fret that is used supposed to be for |
| 57 | + * <p> |
| 58 | + * Note also that there is a objc_msgSend_fret that is used supposed to be for |
56 | 59 | * floating point return types, but that I haven't (yet) had to use.
|
57 |
| - * |
58 | 60 | * @see "http://www.sealiesoftware.com/blog/archive/2008/11/16/objc_explain_objc_msgSend_fpret.html"
|
59 |
| - * |
60 |
| - * @author duncan |
61 |
| - * |
62 | 61 | */
|
63 | 62 | class MsgSendHandler implements InvocationHandler {
|
64 | 63 |
|
65 | 64 | /**
|
66 | 65 | * @see com.sun.jna.Function#OPTION_INVOKING_METHOD
|
67 | 66 | */
|
68 | 67 | private final String OPTION_INVOKING_METHOD = "invoking-method";
|
69 |
| - // TODO - use JNA string when made public |
70 |
| - |
| 68 | + |
71 | 69 | private final static int I386_STRET_CUTOFF = 9;
|
72 | 70 | private final static int IA64_STRET_CUTOFF = 17;
|
73 | 71 |
|
74 |
| - private final static int stretCutoff = NativeLong.SIZE == 8 ? IA64_STRET_CUTOFF : I386_STRET_CUTOFF; |
| 72 | + private final static int STRET_CUTOFF = NativeLong.SIZE == 8 ? IA64_STRET_CUTOFF : I386_STRET_CUTOFF; |
75 | 73 |
|
76 |
| - private final static boolean ppc = System.getProperty("os.arch").trim().equalsIgnoreCase("ppc"); |
| 74 | + public final static boolean AARCH64 = System.getProperty("os.arch").trim().equalsIgnoreCase("aarch64"); |
| 75 | + public final static boolean PPC = System.getProperty("os.arch").trim().equalsIgnoreCase("ppc"); |
77 | 76 |
|
78 | 77 | private final static Method OBJC_MSGSEND;
|
| 78 | + private final static Method OBJC_MSGSEND_VAR_ARGS; |
79 | 79 | private final static Method OBJC_MSGSEND_STRET;
|
| 80 | + |
80 | 81 | static {
|
81 | 82 | try {
|
82 | 83 | OBJC_MSGSEND = MsgSendLibrary.class.getDeclaredMethod("objc_msgSend",
|
83 | 84 | ID.class, Selector.class, Object[].class);
|
| 85 | + OBJC_MSGSEND_VAR_ARGS = MsgSendLibrary.class.getDeclaredMethod("objc_msgSend", |
| 86 | + ID.class, Selector.class, Object.class, Object[].class); |
84 | 87 | OBJC_MSGSEND_STRET = MsgSendLibrary.class.getDeclaredMethod("objc_msgSend_stret",
|
85 | 88 | ID.class, Selector.class, Object[].class);
|
86 |
| - } |
87 |
| - catch (NoSuchMethodException x) { |
| 89 | + } catch (NoSuchMethodException x) { |
88 | 90 | throw new RococoaException(x);
|
89 | 91 | }
|
90 | 92 | }
|
91 | 93 |
|
92 |
| - private final Pair<Method, Function> objc_msgSend_stret_Pair; |
93 |
| - private final Pair<Method, Function> objc_msgSend_Pair; |
| 94 | + private final MethodFunctionPair objc_msgSend_stret_Pair; |
| 95 | + private final MethodFunctionPair objc_msgSend_varArgs_Pair; |
| 96 | + private final MethodFunctionPair objc_msgSend_Pair; |
94 | 97 |
|
95 |
| - private RococoaTypeMapper rococoaTypeMapper = new RococoaTypeMapper(); |
| 98 | + private final RococoaTypeMapper rococoaTypeMapper = new RococoaTypeMapper(); |
96 | 99 |
|
97 |
| - public MsgSendHandler(Function objc_msgSend_Function, Function objc_msgSend_stret_Function) { |
98 |
| - this.objc_msgSend_Pair = new Pair<Method, Function>(OBJC_MSGSEND, objc_msgSend_Function); |
99 |
| - this.objc_msgSend_stret_Pair = new Pair<Method, Function>(OBJC_MSGSEND_STRET, objc_msgSend_stret_Function); |
| 100 | + public MsgSendHandler(final NativeLibrary lib) { |
| 101 | + this.objc_msgSend_Pair = new MethodFunctionPair(AARCH64 ? null : OBJC_MSGSEND, |
| 102 | + lib.getFunction("objc_msgSend")); |
| 103 | + this.objc_msgSend_varArgs_Pair = new MethodFunctionPair(OBJC_MSGSEND_VAR_ARGS, |
| 104 | + lib.getFunction("objc_msgSend")); |
| 105 | + this.objc_msgSend_stret_Pair = new MethodFunctionPair(OBJC_MSGSEND_STRET, |
| 106 | + AARCH64 ? null : lib.getFunction("objc_msgSend_stret")); |
100 | 107 | }
|
101 |
| - |
102 |
| - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| 108 | + |
| 109 | + public Object invoke(final Object proxy, final Method method, final Object[] args) { |
103 | 110 | Class<?> returnTypeForThisCall = (Class<?>) args[0];
|
104 |
| - Object[] argsWithoutReturnType = this.removeReturnTypeFrom(args); |
105 |
| - |
106 |
| - Map<String, Object> options = new HashMap<String, Object>(1); |
107 |
| - options.put(Library.OPTION_TYPE_MAPPER, rococoaTypeMapper); |
108 |
| - |
109 |
| - Pair<Method, Function> invocation = this.invocationFor(returnTypeForThisCall); |
110 |
| - options.put(OPTION_INVOKING_METHOD, invocation.a); |
111 |
| - return invocation.b.invoke(returnTypeForThisCall, argsWithoutReturnType, options); |
| 111 | + MethodFunctionPair invocation = this.invocationFor(returnTypeForThisCall, MsgSendInvocationMapper.SYNTHETIC_SEND_VARARGS_MSG.equals(method)); |
| 112 | + Map<String, Object> options = new HashMap<>(Collections.singletonMap(Library.OPTION_TYPE_MAPPER, rococoaTypeMapper)); |
| 113 | + options.put(OPTION_INVOKING_METHOD, invocation.method); |
| 114 | + return invocation.function.invoke(returnTypeForThisCall, Arrays.copyOfRange(args, 1, args.length), options); |
112 | 115 | }
|
113 |
| - |
114 |
| - private Object[] removeReturnTypeFrom(Object[] args) { |
115 |
| - Object[] result = new Object[args.length - 1]; |
116 |
| - System.arraycopy(args, 1, result, 0, args.length - 2); |
117 |
| - return result; |
118 |
| - } |
119 |
| - |
120 |
| - private Pair<Method, Function> invocationFor(Class<?> returnTypeForThisCall) { |
| 116 | + |
| 117 | + private MethodFunctionPair invocationFor(Class<?> returnTypeForThisCall, boolean varArgs) { |
| 118 | + if (AARCH64) { |
| 119 | + if (varArgs) { |
| 120 | + return objc_msgSend_varArgs_Pair; |
| 121 | + } |
| 122 | + return objc_msgSend_Pair; |
| 123 | + } |
121 | 124 | boolean isStruct = Structure.class.isAssignableFrom(returnTypeForThisCall);
|
122 | 125 | boolean isStructByValue = isStruct && Structure.ByValue.class.isAssignableFrom(returnTypeForThisCall);
|
123 |
| - if (!isStructByValue) |
| 126 | + if (!isStructByValue) { |
124 | 127 | return objc_msgSend_Pair;
|
| 128 | + } |
125 | 129 | try {
|
126 |
| - if(ppc) { |
| 130 | + if (PPC) { |
127 | 131 | // on ppc32 structs never return in registers
|
128 | 132 | return objc_msgSend_stret_Pair;
|
129 | 133 | }
|
130 | 134 | // on i386 structs with sizeof exactly equal to 1, 2, 4, or 8 return in registers
|
131 |
| - Structure prototype = (Structure) returnTypeForThisCall.newInstance(); |
132 |
| - return prototype.size() < stretCutoff ? objc_msgSend_Pair : objc_msgSend_stret_Pair; |
133 |
| - } |
134 |
| - catch(InstantiationException e) { |
135 |
| - throw new RococoaException(e); |
136 |
| - } |
137 |
| - catch(IllegalAccessException e) { |
| 135 | + Structure prototype = (Structure) returnTypeForThisCall.getDeclaredConstructor().newInstance(); |
| 136 | + return prototype.size() < STRET_CUTOFF ? objc_msgSend_Pair : objc_msgSend_stret_Pair; |
| 137 | + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { |
138 | 138 | throw new RococoaException(e);
|
139 | 139 | }
|
140 | 140 | }
|
|
0 commit comments