org.codehaus.mojo
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 3eedacb1..b92521a1 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
@@ -1,13 +1,13 @@
/*
* Copyright 2007, 2008 Duncan McGregor
- *
+ *
* This file is part of Rococoa, a library to allow Java to talk to Cocoa.
- *
+ *
* Rococoa is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* Rococoa is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -16,49 +16,48 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Rococoa. If not, see .
*/
-
-package org.rococoa.internal;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
+package org.rococoa.internal;
+import com.sun.jna.Library;
+import com.sun.jna.NativeLibrary;
+import com.sun.jna.NativeLong;
+import com.sun.jna.Structure;
import org.rococoa.ID;
import org.rococoa.RococoaException;
import org.rococoa.Selector;
-import com.sun.jna.Function;
-import com.sun.jna.Library;
-import com.sun.jna.NativeLong;
-import com.sun.jna.Structure;
+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;
/**
* Very special case InvocationHandler that invokes the correct message dispatch
* function for different return types.
- *
+ *
* Either objc_msgSend or objc_msgSend_stret should be called, depending on the
* return type. The latter is usually for struct by value, but the former is
* used for small structs on Intel! Oh and the call has to be mangled in all
* cases as the result is returned on the stack, but is different sizes
* depending on its type. Luckily jna and libffi take care of the details -
* provided they know what the return type is.
- *
+ *
* This InvocationHandler is passed the return type as the first arg to the method call that it
* intercepts, it uses it to determine which function to call, and removes it before
* calling invoking.
- *
+ *
+ * @author duncan
* @see "http://www.cocoabuilder.com/archive/message/cocoa/2006/6/25/166236"
* @see "http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/LowLevelABI/Mac_OS_X_ABI_Function_Calls.pdf"
* @see "http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html"
- *
- * Note also that there is a objc_msgSend_fret that is used supposed to be for
+ *
+ * Note also that there is a objc_msgSend_fret that is used supposed to be for
* floating point return types, but that I haven't (yet) had to use.
- *
* @see "http://www.sealiesoftware.com/blog/archive/2008/11/16/objc_explain_objc_msgSend_fpret.html"
- *
- * @author duncan
- *
*/
class MsgSendHandler implements InvocationHandler {
@@ -66,75 +65,76 @@ 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;
- private final static int stretCutoff = NativeLong.SIZE == 8 ? IA64_STRET_CUTOFF : I386_STRET_CUTOFF;
+ private final static int STRET_CUTOFF = NativeLong.SIZE == 8 ? IA64_STRET_CUTOFF : I386_STRET_CUTOFF;
- private final static boolean ppc = System.getProperty("os.arch").trim().equalsIgnoreCase("ppc");
+ 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_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_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) {
+ } catch (NoSuchMethodException x) {
throw new RococoaException(x);
}
}
- 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_Pair;
- private RococoaTypeMapper rococoaTypeMapper = new RococoaTypeMapper();
+ private final RococoaTypeMapper rococoaTypeMapper = new RococoaTypeMapper();
- public MsgSendHandler(Function objc_msgSend_Function, Function objc_msgSend_stret_Function) {
- this.objc_msgSend_Pair = new Pair(OBJC_MSGSEND, objc_msgSend_Function);
- this.objc_msgSend_stret_Pair = new Pair(OBJC_MSGSEND_STRET, objc_msgSend_stret_Function);
+ public MsgSendHandler(final NativeLibrary lib) {
+ this.objc_msgSend_Pair = new MethodFunctionPair(AARCH64 ? null : OBJC_MSGSEND,
+ 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) throws Throwable {
+
+ 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) {
+ if (varArgs) {
+ return objc_msgSend_varArgs_Pair;
+ }
+ return objc_msgSend_Pair;
+ }
boolean isStruct = Structure.class.isAssignableFrom(returnTypeForThisCall);
boolean isStructByValue = isStruct && Structure.ByValue.class.isAssignableFrom(returnTypeForThisCall);
- if (!isStructByValue)
+ if (!isStructByValue) {
return objc_msgSend_Pair;
+ }
try {
- if(ppc) {
+ if (PPC) {
// on ppc32 structs never return in registers
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() < stretCutoff ? objc_msgSend_Pair : objc_msgSend_stret_Pair;
- }
- catch(InstantiationException e) {
- throw new RococoaException(e);
- }
- catch(IllegalAccessException e) {
+ Structure prototype = (Structure) returnTypeForThisCall.getDeclaredConstructor().newInstance();
+ return prototype.size() < STRET_CUTOFF ? objc_msgSend_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 3ef60683..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,52 +17,52 @@
* along with Rococoa. If not, see .
*/
-/**
- *
- */
package org.rococoa.internal;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-
+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(
- lib.getFunction("objc_msgSend"),
- 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..16ee497b 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,10 @@
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... 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
diff --git a/rococoa/rococoa-core/src/main/native/Rococoa.h b/rococoa/rococoa-core/src/main/native/Rococoa.h
index 427b07f4..c47b12a8 100644
--- a/rococoa/rococoa-core/src/main/native/Rococoa.h
+++ b/rococoa/rococoa-core/src/main/native/Rococoa.h
@@ -1,7 +1,7 @@
#import
#include
-void callOnMainThread(void (*fn)(), BOOL waitUntilDone);
+void callOnMainThread(void (*fn)(void), BOOL waitUntilDone);
@interface RococoaHelper : NSObject
+ (void) callback: (NSValue*) fn;
diff --git a/rococoa/rococoa-core/src/main/native/Rococoa.m b/rococoa/rococoa-core/src/main/native/Rococoa.m
index 5caadffd..48481d89 100644
--- a/rococoa/rococoa-core/src/main/native/Rococoa.m
+++ b/rococoa/rococoa-core/src/main/native/Rococoa.m
@@ -1,6 +1,6 @@
#include "Rococoa.h"
-void callOnMainThread(void (*fn)(), BOOL waitUntilDone) {
+void callOnMainThread(void (*fn)(void), BOOL waitUntilDone) {
// NSLog(@"callOnMainThread function at address %p", fn);
// Pool is required as we're being called from Java, which probably doesn't have a pool to
// allocate the NSValue from.
@@ -13,7 +13,7 @@ void callOnMainThread(void (*fn)(), BOOL waitUntilDone) {
@implementation RococoaHelper : NSObject
+ (void) callback: (NSValue*) fnAsValue {
- void (*fn)() = [fnAsValue pointerValue];
+ void (*fn)(void) = [fnAsValue pointerValue];
(*fn)();
}