Skip to content

Commit ef0e110

Browse files
authored
Merge pull request #24 from /issues/20
Issues/20
2 parents b92cc96 + 6b48620 commit ef0e110

File tree

8 files changed

+128
-124
lines changed

8 files changed

+128
-124
lines changed

rococoa/pom.xml

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@
145145
<dependency>
146146
<groupId>net.java.dev.jna</groupId>
147147
<artifactId>jna</artifactId>
148-
<version>5.9.0</version>
148+
<version>5.10.0</version>
149149
</dependency>
150150
<dependency>
151151
<groupId>cglib</groupId>
@@ -167,7 +167,7 @@
167167
<plugin>
168168
<groupId>org.apache.maven.plugins</groupId>
169169
<artifactId>maven-source-plugin</artifactId>
170-
<version>2.2.1</version>
170+
<version>3.2.1</version>
171171
<executions>
172172
<execution>
173173
<id>attach-sources</id>
@@ -177,25 +177,9 @@
177177
</execution>
178178
</executions>
179179
</plugin>
180-
<plugin>
181-
<groupId>org.apache.maven.plugins</groupId>
182-
<artifactId>maven-javadoc-plugin</artifactId>
183-
<version>2.9.1</version>
184-
<executions>
185-
<execution>
186-
<id>attach-javadocs</id>
187-
<goals>
188-
<goal>jar</goal>
189-
</goals>
190-
<configuration>
191-
<additionalparam>-Xdoclint:none</additionalparam>
192-
</configuration>
193-
</execution>
194-
</executions>
195-
</plugin>
196180
<plugin>
197181
<artifactId>maven-resources-plugin</artifactId>
198-
<version>2.6</version>
182+
<version>3.2.0</version>
199183
<inherited>true</inherited>
200184
<configuration>
201185
<encoding>UTF-8</encoding>
@@ -305,7 +289,7 @@
305289
</plugin>
306290
<plugin>
307291
<artifactId>maven-dependency-plugin</artifactId>
308-
<version>3.1.2</version>
292+
<version>3.2.0</version>
309293
</plugin>
310294
<plugin>
311295
<groupId>org.codehaus.mojo</groupId>

rococoa/rococoa-core/src/main/java/org/rococoa/Foundation.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.rococoa.cocoa.CFIndex;
2525
import org.rococoa.internal.*;
2626

27+
import java.lang.reflect.Method;
2728
import java.nio.charset.StandardCharsets;
2829
import java.util.HashMap;
2930
import java.util.Map;
@@ -171,26 +172,39 @@ public static Selector selector(String selectorName) {
171172
return result;
172173
}
173174

174-
/**
175-
* Send message with selectorName to receiver, passing args, expecting returnType.
176-
* <p>
177-
* Note that you are responsible for memory management if returnType is ID.
178-
*/
175+
@SuppressWarnings("unchecked")
179176
public static <T> T send(ID receiver, String selectorName, Class<T> returnType, Object... args) {
180-
return send(receiver, selector(selectorName), returnType, args);
177+
return send(receiver, selectorName, returnType, null, args);
178+
}
179+
180+
@SuppressWarnings("unchecked")
181+
public static <T> T send(ID receiver, String selectorName, Class<T> returnType, Method method, Object... args) {
182+
return send(receiver, selector(selectorName), returnType, method, args);
183+
}
184+
185+
@SuppressWarnings("unchecked")
186+
public static <T> T send(ID receiver, Selector selector, Class<T> returnType, Object... args) {
187+
return send(receiver, selector, returnType, null, args);
181188
}
182189

183190
/**
184191
* Send message with selector to receiver, passing args, expecting returnType.
185192
* <p>
186193
* Note that you are responsible for memory management if returnType is ID.
194+
*
195+
* @param returnType Expected return type mapping
196+
* @param method Used to determine if variadic function call is required
197+
* @param args Arguments including ID and selector
187198
*/
188199
@SuppressWarnings("unchecked")
189-
public static <T> T send(ID receiver, Selector selector, Class<T> returnType, Object... args) {
200+
public static <T> T send(ID receiver, Selector selector, Class<T> returnType, Method method, Object... args) {
190201
if (logging.isLoggable(Level.FINEST)) {
191202
logging.finest(String.format("sending (%s) %s.%s(%s)",
192203
returnType.getSimpleName(), receiver, selector.getName(), new VarArgsUnpacker(args)));
193204
}
205+
if (method != null && method.isVarArgs()) {
206+
return (T) messageSendLibrary.syntheticSendVarArgsMessage(returnType, receiver, selector, args);
207+
}
194208
return (T) messageSendLibrary.syntheticSendMessage(returnType, receiver, selector, args);
195209
}
196210

rococoa/rococoa-core/src/main/java/org/rococoa/internal/Pair.java renamed to rococoa/rococoa-core/src/main/java/org/rococoa/internal/MethodFunctionPair.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919

2020
package org.rococoa.internal;
2121

22-
public class Pair<T1, T2> {
22+
import com.sun.jna.Function;
23+
24+
import java.lang.reflect.Method;
25+
26+
public class MethodFunctionPair {
2327

24-
public final T1 a;
25-
public final T2 b;
28+
public final Method method;
29+
public final Function function;
2630

27-
public Pair(T1 a, T2 b) {
28-
this.a = a;
29-
this.b = b;
31+
public MethodFunctionPair(Method method, Function function) {
32+
this.method = method;
33+
this.function = function;
3034
}
3135
}

rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendHandler.java

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
/*
22
* Copyright 2007, 2008 Duncan McGregor
3-
*
3+
*
44
* This file is part of Rococoa, a library to allow Java to talk to Cocoa.
5-
*
5+
*
66
* Rococoa is free software: you can redistribute it and/or modify
77
* it under the terms of the GNU Lesser General Public License as published by
88
* the Free Software Foundation, either version 3 of the License, or
99
* (at your option) any later version.
10-
*
10+
*
1111
* Rococoa is distributed in the hope that it will be useful,
1212
* but WITHOUT ANY WARRANTY; without even the implied warranty of
1313
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -16,125 +16,125 @@
1616
* You should have received a copy of the GNU Lesser General Public License
1717
* along with Rococoa. If not, see <http://www.gnu.org/licenses/>.
1818
*/
19-
20-
package org.rococoa.internal;
2119

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;
2621

22+
import com.sun.jna.Library;
23+
import com.sun.jna.NativeLibrary;
24+
import com.sun.jna.NativeLong;
25+
import com.sun.jna.Structure;
2726
import org.rococoa.ID;
2827
import org.rococoa.RococoaException;
2928
import org.rococoa.Selector;
3029

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;
3537

3638
/**
3739
* Very special case InvocationHandler that invokes the correct message dispatch
3840
* function for different return types.
39-
*
41+
* <p>
4042
* Either objc_msgSend or objc_msgSend_stret should be called, depending on the
4143
* return type. The latter is usually for struct by value, but the former is
4244
* used for small structs on Intel! Oh and the call has to be mangled in all
4345
* cases as the result is returned on the stack, but is different sizes
4446
* depending on its type. Luckily jna and libffi take care of the details -
4547
* provided they know what the return type is.
46-
*
48+
* <p>
4749
* This InvocationHandler is passed the return type as the first arg to the method call that it
4850
* intercepts, it uses it to determine which function to call, and removes it before
4951
* calling invoking.
50-
*
52+
*
53+
* @author duncan
5154
* @see "http://www.cocoabuilder.com/archive/message/cocoa/2006/6/25/166236"
5255
* @see "http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/LowLevelABI/Mac_OS_X_ABI_Function_Calls.pdf"
5356
* @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
5659
* floating point return types, but that I haven't (yet) had to use.
57-
*
5860
* @see "http://www.sealiesoftware.com/blog/archive/2008/11/16/objc_explain_objc_msgSend_fpret.html"
59-
*
60-
* @author duncan
61-
*
6261
*/
6362
class MsgSendHandler implements InvocationHandler {
6463

6564
/**
6665
* @see com.sun.jna.Function#OPTION_INVOKING_METHOD
6766
*/
6867
private final String OPTION_INVOKING_METHOD = "invoking-method";
69-
// TODO - use JNA string when made public
70-
68+
7169
private final static int I386_STRET_CUTOFF = 9;
7270
private final static int IA64_STRET_CUTOFF = 17;
7371

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;
7573

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");
7776

7877
private final static Method OBJC_MSGSEND;
78+
private final static Method OBJC_MSGSEND_VAR_ARGS;
7979
private final static Method OBJC_MSGSEND_STRET;
80+
8081
static {
8182
try {
8283
OBJC_MSGSEND = MsgSendLibrary.class.getDeclaredMethod("objc_msgSend",
8384
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);
8487
OBJC_MSGSEND_STRET = MsgSendLibrary.class.getDeclaredMethod("objc_msgSend_stret",
8588
ID.class, Selector.class, Object[].class);
86-
}
87-
catch (NoSuchMethodException x) {
89+
} catch (NoSuchMethodException x) {
8890
throw new RococoaException(x);
8991
}
9092
}
9193

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;
9497

95-
private RococoaTypeMapper rococoaTypeMapper = new RococoaTypeMapper();
98+
private final RococoaTypeMapper rococoaTypeMapper = new RococoaTypeMapper();
9699

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"));
100107
}
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) {
103110
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);
112115
}
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+
}
121124
boolean isStruct = Structure.class.isAssignableFrom(returnTypeForThisCall);
122125
boolean isStructByValue = isStruct && Structure.ByValue.class.isAssignableFrom(returnTypeForThisCall);
123-
if (!isStructByValue)
126+
if (!isStructByValue) {
124127
return objc_msgSend_Pair;
128+
}
125129
try {
126-
if(ppc) {
130+
if (PPC) {
127131
// on ppc32 structs never return in registers
128132
return objc_msgSend_stret_Pair;
129133
}
130134
// 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) {
138138
throw new RococoaException(e);
139139
}
140140
}

0 commit comments

Comments
 (0)