diff --git a/rococoa/rococoa-core/src/main/java/org/rococoa/Foundation.java b/rococoa/rococoa-core/src/main/java/org/rococoa/Foundation.java
index c5d6f278..b8f04e20 100644
--- a/rococoa/rococoa-core/src/main/java/org/rococoa/Foundation.java
+++ b/rococoa/rococoa-core/src/main/java/org/rococoa/Foundation.java
@@ -24,6 +24,7 @@
import org.rococoa.cocoa.CFIndex;
import org.rococoa.internal.*;
+import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@@ -171,26 +172,39 @@ public static Selector selector(String selectorName) {
return result;
}
- /**
- * Send message with selectorName to receiver, passing args, expecting returnType.
- *
- * Note that you are responsible for memory management if returnType is ID.
- */
+ @SuppressWarnings("unchecked")
public static T send(ID receiver, String selectorName, Class returnType, Object... args) {
- return send(receiver, selector(selectorName), returnType, args);
+ return send(receiver, selectorName, returnType, null, args);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T send(ID receiver, String selectorName, Class returnType, Method method, Object... args) {
+ return send(receiver, selector(selectorName), returnType, method, args);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T send(ID receiver, Selector selector, Class returnType, Object... args) {
+ return send(receiver, selector, returnType, null, args);
}
/**
* Send message with selector to receiver, passing args, expecting returnType.
*
* Note that you are responsible for memory management if returnType is ID.
+ *
+ * @param returnType Expected return type mapping
+ * @param method Used to determine if variadic function call is required
+ * @param args Arguments including ID and selector
*/
@SuppressWarnings("unchecked")
- public static T send(ID receiver, Selector selector, Class returnType, Object... args) {
+ public static T send(ID receiver, Selector selector, Class returnType, Method method, Object... args) {
if (logging.isLoggable(Level.FINEST)) {
logging.finest(String.format("sending (%s) %s.%s(%s)",
returnType.getSimpleName(), receiver, selector.getName(), new VarArgsUnpacker(args)));
}
+ if (method != null && method.isVarArgs()) {
+ return (T) messageSendLibrary.syntheticSendVarArgsMessage(returnType, receiver, selector, args);
+ }
return (T) messageSendLibrary.syntheticSendMessage(returnType, receiver, selector, args);
}
diff --git a/rococoa/rococoa-core/src/main/java/org/rococoa/internal/Pair.java b/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MethodFunctionPair.java
similarity index 73%
rename from rococoa/rococoa-core/src/main/java/org/rococoa/internal/Pair.java
rename to rococoa/rococoa-core/src/main/java/org/rococoa/internal/MethodFunctionPair.java
index 90c550b8..a2d594fd 100644
--- a/rococoa/rococoa-core/src/main/java/org/rococoa/internal/Pair.java
+++ b/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MethodFunctionPair.java
@@ -19,13 +19,17 @@
package org.rococoa.internal;
-public class Pair {
+import com.sun.jna.Function;
+
+import java.lang.reflect.Method;
+
+public class MethodFunctionPair {
- public final T1 a;
- public final T2 b;
+ public final Method method;
+ public final Function function;
- public Pair(T1 a, T2 b) {
- this.a = a;
- this.b = b;
+ public MethodFunctionPair(Method method, Function function) {
+ this.method = method;
+ this.function = function;
}
}
\ No newline at end of file
diff --git a/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendHandler.java b/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendHandler.java
index 8154aff2..a12cbb8e 100644
--- a/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendHandler.java
+++ b/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendHandler.java
@@ -19,8 +19,8 @@
package org.rococoa.internal;
-import com.sun.jna.Function;
import com.sun.jna.Library;
+import com.sun.jna.NativeLibrary;
import com.sun.jna.NativeLong;
import com.sun.jna.Structure;
import org.rococoa.ID;
@@ -28,10 +28,12 @@
import org.rococoa.Selector;
import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import java.util.Optional;
/**
* Very special case InvocationHandler that invokes the correct message dispatch
@@ -63,7 +65,6 @@ class MsgSendHandler implements InvocationHandler {
* @see com.sun.jna.Function#OPTION_INVOKING_METHOD
*/
private final String OPTION_INVOKING_METHOD = "invoking-method";
- // TODO - use JNA string when made public
private final static int I386_STRET_CUTOFF = 9;
private final static int IA64_STRET_CUTOFF = 17;
@@ -73,13 +74,15 @@ class MsgSendHandler implements InvocationHandler {
public final static boolean AARCH64 = System.getProperty("os.arch").trim().equalsIgnoreCase("aarch64");
public final static boolean PPC = System.getProperty("os.arch").trim().equalsIgnoreCase("ppc");
- private final static Method OBJC_MSGSEND;
+ private final static Method OBJC_MSGSEND_FIXED_ARGS;
+ private final static Method OBJC_MSGSEND_VAR_ARGS;
private final static Method OBJC_MSGSEND_STRET;
static {
try {
- OBJC_MSGSEND = MsgSendLibrary.class.getDeclaredMethod("objc_msgSend",
- ID.class, Selector.class, Object[].class);
+ OBJC_MSGSEND_FIXED_ARGS = null;
+ OBJC_MSGSEND_VAR_ARGS = MsgSendLibrary.class.getDeclaredMethod("objc_msgSend",
+ ID.class, Selector.class, Object.class, Object[].class);
OBJC_MSGSEND_STRET = MsgSendLibrary.class.getDeclaredMethod("objc_msgSend_stret",
ID.class, Selector.class, Object[].class);
} catch (NoSuchMethodException x) {
@@ -87,42 +90,40 @@ class MsgSendHandler implements InvocationHandler {
}
}
- private final Pair objc_msgSend_stret_Pair;
- private final Pair objc_msgSend_Pair;
+ private final MethodFunctionPair objc_msgSend_stret_Pair;
+ private final MethodFunctionPair objc_msgSend_varArgs_Pair;
+ private final MethodFunctionPair objc_msgSend_fixedArgs_Pair;
private final RococoaTypeMapper rococoaTypeMapper = new RococoaTypeMapper();
- public MsgSendHandler(Optional objc_msgSend_Function, Optional objc_msgSend_stret_Function) {
- this.objc_msgSend_Pair = new Pair<>(OBJC_MSGSEND, objc_msgSend_Function.orElse(null));
- this.objc_msgSend_stret_Pair = new Pair<>(OBJC_MSGSEND_STRET, objc_msgSend_stret_Function.orElse(null));
+ public MsgSendHandler(final NativeLibrary lib) {
+ this.objc_msgSend_fixedArgs_Pair = new MethodFunctionPair(OBJC_MSGSEND_FIXED_ARGS,
+ lib.getFunction("objc_msgSend"));
+ this.objc_msgSend_varArgs_Pair = new MethodFunctionPair(OBJC_MSGSEND_VAR_ARGS,
+ lib.getFunction("objc_msgSend"));
+ this.objc_msgSend_stret_Pair = new MethodFunctionPair(OBJC_MSGSEND_STRET,
+ AARCH64 ? null : lib.getFunction("objc_msgSend_stret"));
}
- public Object invoke(Object proxy, Method method, Object[] args) {
+ public Object invoke(final Object proxy, final Method method, final Object[] args) {
Class> returnTypeForThisCall = (Class>) args[0];
- Object[] argsWithoutReturnType = this.removeReturnTypeFrom(args);
-
- Map options = new HashMap<>(1);
- options.put(Library.OPTION_TYPE_MAPPER, rococoaTypeMapper);
-
- Pair invocation = this.invocationFor(returnTypeForThisCall);
- options.put(OPTION_INVOKING_METHOD, invocation.a);
- return invocation.b.invoke(returnTypeForThisCall, argsWithoutReturnType, options);
+ MethodFunctionPair invocation = this.invocationFor(returnTypeForThisCall, MsgSendInvocationMapper.SYNTHETIC_SEND_VARARGS_MSG.equals(method));
+ Map options = new HashMap<>(Collections.singletonMap(Library.OPTION_TYPE_MAPPER, rococoaTypeMapper));
+ options.put(OPTION_INVOKING_METHOD, invocation.method);
+ return invocation.function.invoke(returnTypeForThisCall, Arrays.copyOfRange(args, 1, args.length), options);
}
- private Object[] removeReturnTypeFrom(Object[] args) {
- Object[] result = new Object[args.length - 1];
- System.arraycopy(args, 1, result, 0, args.length - 2);
- return result;
- }
-
- private Pair invocationFor(Class> returnTypeForThisCall) {
+ private MethodFunctionPair invocationFor(Class> returnTypeForThisCall, boolean varArgs) {
if (AARCH64) {
- return objc_msgSend_Pair;
+ if (varArgs) {
+ return objc_msgSend_varArgs_Pair;
+ }
+ return objc_msgSend_fixedArgs_Pair;
}
boolean isStruct = Structure.class.isAssignableFrom(returnTypeForThisCall);
boolean isStructByValue = isStruct && Structure.ByValue.class.isAssignableFrom(returnTypeForThisCall);
if (!isStructByValue) {
- return objc_msgSend_Pair;
+ return objc_msgSend_varArgs_Pair;
}
try {
if (PPC) {
@@ -130,9 +131,9 @@ private Pair invocationFor(Class> returnTypeForThisCall) {
return objc_msgSend_stret_Pair;
}
// on i386 structs with sizeof exactly equal to 1, 2, 4, or 8 return in registers
- Structure prototype = (Structure) returnTypeForThisCall.newInstance();
- return prototype.size() < STRET_CUTOFF ? objc_msgSend_Pair : objc_msgSend_stret_Pair;
- } catch (InstantiationException | IllegalAccessException e) {
+ Structure prototype = (Structure) returnTypeForThisCall.getDeclaredConstructor().newInstance();
+ return prototype.size() < STRET_CUTOFF ? objc_msgSend_varArgs_Pair : objc_msgSend_stret_Pair;
+ } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
throw new RococoaException(e);
}
}
diff --git a/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendInvocationMapper.java b/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendInvocationMapper.java
index 841331fa..c330537f 100644
--- a/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendInvocationMapper.java
+++ b/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendInvocationMapper.java
@@ -17,53 +17,52 @@
* along with Rococoa. If not, see .
*/
-/**
- *
- */
package org.rococoa.internal;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.util.Optional;
-
+import com.sun.jna.InvocationMapper;
+import com.sun.jna.NativeLibrary;
import org.rococoa.ID;
+import org.rococoa.RococoaException;
import org.rococoa.Selector;
-import com.sun.jna.InvocationMapper;
-import com.sun.jna.NativeLibrary;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
/**
* A JNA InvocationMapper that maps calls to syntheticSendMessage to a MsgSendHandler.
- *
+ *
* This allows us to dispatch all calls to syntheticSendMessage and have MsgSendHandler
* call objc_msgSend or objc_msgSend_stret as appropriate, casting the return
* type appropriately.
- *
+ *
* @author duncan
*/
public class MsgSendInvocationMapper implements InvocationMapper {
- private final static Method SYNTHETIC_SEND_MSG;
+ public final static Method SYNTHETIC_SEND_MSG;
+ public final static Method SYNTHETIC_SEND_VARARGS_MSG;
static {
try {
- SYNTHETIC_SEND_MSG = MsgSendLibrary.class.getDeclaredMethod("syntheticSendMessage",
+ SYNTHETIC_SEND_MSG = MsgSendLibrary.class.getDeclaredMethod("syntheticSendMessage",
Class.class, ID.class, Selector.class, Object[].class);
+ } catch (Exception e) {
+ throw new RococoaException("Error retrieving method");
}
- catch (Exception e) {
- throw new Error("Error retrieving method");
+ try {
+ SYNTHETIC_SEND_VARARGS_MSG = MsgSendLibrary.class.getDeclaredMethod("syntheticSendVarArgsMessage",
+ Class.class, ID.class, Selector.class, Object[].class);
+ } catch (Exception e) {
+ throw new RococoaException("Error retrieving method");
}
}
-
+
public InvocationHandler getInvocationHandler(NativeLibrary lib, Method m) {
- if (!m.equals(SYNTHETIC_SEND_MSG))
- return null; // default handler
-
- // Have to late bind this, as it's the only time we get to see lib.
- // Not too bad as the results are cached.
- return new MsgSendHandler(
- Optional.of(lib.getFunction("objc_msgSend")),
- MsgSendHandler.AARCH64 ? Optional.ofNullable(null) : Optional.of(lib.getFunction("objc_msgSend_stret")));
+ if (m.equals(SYNTHETIC_SEND_MSG) || m.equals(SYNTHETIC_SEND_VARARGS_MSG)) {
+ // Have to late bind this, as it's the only time we get to see lib.
+ // Not too bad as the results are cached.
+ return new MsgSendHandler(lib);
+ }
+ return null; // default handler
}
-
}
\ No newline at end of file
diff --git a/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendLibrary.java b/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendLibrary.java
index 56db174b..80d16f5f 100644
--- a/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendLibrary.java
+++ b/rococoa/rococoa-core/src/main/java/org/rococoa/internal/MsgSendLibrary.java
@@ -32,8 +32,9 @@
public interface MsgSendLibrary extends Library {
// This doesn't exist in the library, but is synthesised by msgSendHandler
Object syntheticSendMessage(Class> returnType, ID receiver, Selector selector, Object... args);
-
+ Object syntheticSendVarArgsMessage(Class> returnType, ID receiver, Selector selector, Object... args);
+
// We don't call these directly, but through syntheticSendMessage
- Object objc_msgSend(ID receiver, Selector selector, Object... args);
- Structure objc_msgSend_stret(ID receiver, Selector selector, Object... args);
+ Object objc_msgSend(ID receiver, Selector selector, Object arg, Object... args);
+ Structure objc_msgSend_stret(ID receiver, Selector selector, Object... args);
}
\ No newline at end of file