| 1 |
/** |
|---|
| 2 |
* Copyright: Copyright (c) 2009 Jacob Carlborg. |
|---|
| 3 |
* Authors: Jacob Carlborg |
|---|
| 4 |
* Version: Initial created: Feb 4, 2009 |
|---|
| 5 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) |
|---|
| 6 |
*/ |
|---|
| 7 |
module dstep.objc.bridge.Bridge; |
|---|
| 8 |
|
|---|
| 9 |
version (Tango) |
|---|
| 10 |
{ |
|---|
| 11 |
import tango.core.Memory; |
|---|
| 12 |
import tango.core.Traits : ParameterTupleOf, ReturnTypeOf; |
|---|
| 13 |
} |
|---|
| 14 |
|
|---|
| 15 |
else |
|---|
| 16 |
{ |
|---|
| 17 |
import GC = std.gc : addRoot; |
|---|
| 18 |
import std.traits : ParameterTypeTuple, ReturnType; |
|---|
| 19 |
|
|---|
| 20 |
alias ReturnType ReturnTypeOf; |
|---|
| 21 |
alias ParameterTypeTuple ParameterTupleOf; |
|---|
| 22 |
} |
|---|
| 23 |
|
|---|
| 24 |
import dstep.internal.String; |
|---|
| 25 |
import dstep.internal.Version; |
|---|
| 26 |
import dstep.objc.bridge.Capsule; |
|---|
| 27 |
import dstep.objc.bridge.ClassInitializer; |
|---|
| 28 |
import dstep.objc.bridge.Type; |
|---|
| 29 |
import dstep.objc.bridge.TypeEncoding; |
|---|
| 30 |
import dstep.objc.bridge.Wrapper; |
|---|
| 31 |
import dstep.objc.message; |
|---|
| 32 |
import dstep.objc.objc; |
|---|
| 33 |
import dstep.objc.runtime; |
|---|
| 34 |
|
|---|
| 35 |
/** |
|---|
| 36 |
* Builds a string representing a selector out of the given method |
|---|
| 37 |
* |
|---|
| 38 |
* It will build the string using the parameter names as a part of the selector, |
|---|
| 39 |
* like this: |
|---|
| 40 |
* |
|---|
| 41 |
* Examples: |
|---|
| 42 |
* --- |
|---|
| 43 |
* foo (int x, int y); |
|---|
| 44 |
* bar (); |
|---|
| 45 |
* fooBar (int x); |
|---|
| 46 |
* |
|---|
| 47 |
* static assert(selectorAsString!(foo) == "foo:y:"); |
|---|
| 48 |
* static assert(selectorAsString!(bar) == "bar"); |
|---|
| 49 |
* static assert(selectorAsString!(fooBar) == "fooBar:"); |
|---|
| 50 |
* --- |
|---|
| 51 |
* |
|---|
| 52 |
* Params: |
|---|
| 53 |
* method = the method alias to build the selector of |
|---|
| 54 |
* |
|---|
| 55 |
* Returns: a string representing the selector |
|---|
| 56 |
*/ |
|---|
| 57 |
template selectorAsString (alias method) |
|---|
| 58 |
{ |
|---|
| 59 |
const selectorAsString = buildSelector!(method); |
|---|
| 60 |
} |
|---|
| 61 |
|
|---|
| 62 |
/** |
|---|
| 63 |
* Registers a method with the Objective-C runtime system, |
|---|
| 64 |
* maps the method name to a selector, and returns the selector value. |
|---|
| 65 |
* |
|---|
| 66 |
* You must register a method name with the Objective-C runtime system to obtain |
|---|
| 67 |
* the methodâs selector before you can add the method to a class definition. |
|---|
| 68 |
* If the method name has already been registered, this function simply returns |
|---|
| 69 |
* the selector. |
|---|
| 70 |
* |
|---|
| 71 |
* Examples: |
|---|
| 72 |
* --- |
|---|
| 73 |
* SEL sel = selector!("foo:"); |
|---|
| 74 |
* --- |
|---|
| 75 |
* |
|---|
| 76 |
* Params: |
|---|
| 77 |
* str = the string to register |
|---|
| 78 |
* |
|---|
| 79 |
* Returns: a pointer of type SEL specifying the selector for the named method. |
|---|
| 80 |
*/ |
|---|
| 81 |
SEL selector (string str) () |
|---|
| 82 |
{ |
|---|
| 83 |
return sel.registerName!(str); |
|---|
| 84 |
} |
|---|
| 85 |
|
|---|
| 86 |
/** |
|---|
| 87 |
* Registers a method with the Objective-C runtime system, |
|---|
| 88 |
* maps the method name to a selector, and returns the selector value. |
|---|
| 89 |
* |
|---|
| 90 |
* Using selectorAsString to get the string representation of the selector. |
|---|
| 91 |
* |
|---|
| 92 |
* You must register a method name with the Objective-C runtime system to obtain |
|---|
| 93 |
* the methodâs selector before you can add the method to a class definition. |
|---|
| 94 |
* If the method name has already been registered, this function simply returns |
|---|
| 95 |
* the selector. |
|---|
| 96 |
* |
|---|
| 97 |
* Examples: |
|---|
| 98 |
* --- |
|---|
| 99 |
* foo (int x); |
|---|
| 100 |
* SEL sel = selector!(foo); |
|---|
| 101 |
* --- |
|---|
| 102 |
* |
|---|
| 103 |
* Params: |
|---|
| 104 |
* method = the method to register |
|---|
| 105 |
* |
|---|
| 106 |
* Returns: a pointer of type SEL specifying the selector for the named method. |
|---|
| 107 |
*/ |
|---|
| 108 |
SEL selector (alias method) () |
|---|
| 109 |
{ |
|---|
| 110 |
return sel.registerName!(selectorAsString!(method)); |
|---|
| 111 |
} |
|---|
| 112 |
|
|---|
| 113 |
/** |
|---|
| 114 |
* All Objective-C wrappers should mix in this template. |
|---|
| 115 |
* |
|---|
| 116 |
* Mixes in: $(D_PSYMBOL dstep.objc.bridge.ClassInitializer.ObjcSubclassInitializer) |
|---|
| 117 |
* |
|---|
| 118 |
* |
|---|
| 119 |
* Examples: |
|---|
| 120 |
* --- |
|---|
| 121 |
* class AppController : NSObject |
|---|
| 122 |
* { |
|---|
| 123 |
* mixin ObjcWrap; |
|---|
| 124 |
* } |
|---|
| 125 |
* --- |
|---|
| 126 |
*/ |
|---|
| 127 |
template ObjcWrap () |
|---|
| 128 |
{ |
|---|
| 129 |
/// This variable represents the Objective-C class. |
|---|
| 130 |
static private dstep.objc.objc.Class __objcClass; |
|---|
| 131 |
|
|---|
| 132 |
/// This variable represents the Objective-C super class. |
|---|
| 133 |
static private dstep.objc.objc.Class __objcSuperClass; |
|---|
| 134 |
|
|---|
| 135 |
/** |
|---|
| 136 |
* Allocates a new instance of the receiver. |
|---|
| 137 |
* |
|---|
| 138 |
* Returns: a new instance of the receiver |
|---|
| 139 |
*/ |
|---|
| 140 |
static typeof(this) alloc () |
|---|
| 141 |
{ |
|---|
| 142 |
return invokeObjcSelfClass!(typeof(this), "alloc"); |
|---|
| 143 |
} |
|---|
| 144 |
|
|---|
| 145 |
mixin dstep.objc.bridge.ClassInitializer.ObjcSubclassInitializer!(this.stringof, super.stringof); |
|---|
| 146 |
} |
|---|
| 147 |
|
|---|
| 148 |
/** |
|---|
| 149 |
* All Objective-C wrappers should mix in this string. |
|---|
| 150 |
* |
|---|
| 151 |
* Mixes in: $(D_PSYMBOL dstep.objc.bridge.ClassInitializer.ObjcSubclassInitializer) |
|---|
| 152 |
* |
|---|
| 153 |
* Examples: |
|---|
| 154 |
* --- |
|---|
| 155 |
* class NSString : NSObject |
|---|
| 156 |
* { |
|---|
| 157 |
* mixin ObjcClusterWrap; |
|---|
| 158 |
* } |
|---|
| 159 |
* --- |
|---|
| 160 |
*/ |
|---|
| 161 |
template ObjcClusterWrap () |
|---|
| 162 |
{ |
|---|
| 163 |
/// This variable represents the Objective-C class. |
|---|
| 164 |
static private dstep.objc.objc.Class __objcClass; |
|---|
| 165 |
|
|---|
| 166 |
/// This variable represents the Objective-C super class. |
|---|
| 167 |
static private dstep.objc.objc.Class __objcSuperClass; |
|---|
| 168 |
|
|---|
| 169 |
/** |
|---|
| 170 |
* Allocates a new instance of the receiver. |
|---|
| 171 |
* |
|---|
| 172 |
* Returns: a new instance of the receiver |
|---|
| 173 |
*/ |
|---|
| 174 |
static typeof(this) alloc () |
|---|
| 175 |
{ |
|---|
| 176 |
return invokeObjcSuperClass!(typeof(this), "alloc"); |
|---|
| 177 |
} |
|---|
| 178 |
|
|---|
| 179 |
mixin dstep.objc.bridge.ClassInitializer.ObjcSubclassInitializer!(this.stringof, super.stringof); |
|---|
| 180 |
} |
|---|
| 181 |
|
|---|
| 182 |
/** |
|---|
| 183 |
* Makes the given field available as an IBOutlet. |
|---|
| 184 |
* |
|---|
| 185 |
* Mixes in a method that is called by the Objective-C side to set the value of the |
|---|
| 186 |
* given field. |
|---|
| 187 |
* |
|---|
| 188 |
* Mixes in: $(D_PSYMBOL ObjcBindMethod) |
|---|
| 189 |
* |
|---|
| 190 |
* Examples: |
|---|
| 191 |
* --- |
|---|
| 192 |
* class AppController : NSObject |
|---|
| 193 |
* { |
|---|
| 194 |
* NSButton button; |
|---|
| 195 |
* mixin IBOutlet!(button); |
|---|
| 196 |
* } |
|---|
| 197 |
* --- |
|---|
| 198 |
* |
|---|
| 199 |
* Params: |
|---|
| 200 |
* field = the field make available as an IBOutlet |
|---|
| 201 |
*/ |
|---|
| 202 |
template IBOutlet (alias field) |
|---|
| 203 |
{ |
|---|
| 204 |
static assert (is(typeof(field) : Object), dstep.objc.bridge.Bridge.buildIBOutletErrorMessage!(field)); |
|---|
| 205 |
|
|---|
| 206 |
/// Sets the field |
|---|
| 207 |
void __setMethod (typeof(field) value) |
|---|
| 208 |
{ |
|---|
| 209 |
field = value; |
|---|
| 210 |
} |
|---|
| 211 |
|
|---|
| 212 |
mixin dstep.objc.bridge.Bridge.ObjcBindMethod!(__setMethod, "set" ~ dstep.objc.bridge.Bridge.toUpper(field.stringof[0]) ~ field.stringof[1 .. $] ~ ":"); |
|---|
| 213 |
} |
|---|
| 214 |
|
|---|
| 215 |
char toUpper (char c) |
|---|
| 216 |
{ |
|---|
| 217 |
if (c >= 'a' && c <= 'z') |
|---|
| 218 |
return c - 32; |
|---|
| 219 |
|
|---|
| 220 |
return c; |
|---|
| 221 |
} |
|---|
| 222 |
|
|---|
| 223 |
template buildIBOutletErrorMessage (alias field) |
|---|
| 224 |
{ |
|---|
| 225 |
const buildIBOutletErrorMessage = `The type "` ~ typeof(field).stringof ~ `" of the given field "` ~ field.stringof ~ `" in the class "` ~ typeof(this).stringof ~ `" is not a valid IBOutlet type. IBOutlets can only be of the type Object (or any of its subclasses)`; |
|---|
| 226 |
} |
|---|
| 227 |
|
|---|
| 228 |
/** |
|---|
| 229 |
* Binds a selector to an instance method. |
|---|
| 230 |
* |
|---|
| 231 |
* This will create a receiver function which will forward the call to $(D_PARAM method), |
|---|
| 232 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 233 |
* This $(D_KEYWORD template) will use the buildSelector $(D_KEYWORD template) to build |
|---|
| 234 |
* the selector. It will automatically infer the return type and the argument types |
|---|
| 235 |
* of the method. An action method can only have one parameter and of the type Object |
|---|
| 236 |
* (or any of its subclasses). |
|---|
| 237 |
* |
|---|
| 238 |
* Mixes in: ObjcBindMethod |
|---|
| 239 |
* |
|---|
| 240 |
* Examples: |
|---|
| 241 |
* --- |
|---|
| 242 |
* class AppController : NSObject |
|---|
| 243 |
* { |
|---|
| 244 |
* void foo (Object sender) {} |
|---|
| 245 |
* mixin IBAction!(foo); |
|---|
| 246 |
* } |
|---|
| 247 |
* --- |
|---|
| 248 |
* |
|---|
| 249 |
* Params: |
|---|
| 250 |
* method = the method to bind |
|---|
| 251 |
*/ |
|---|
| 252 |
template IBAction (alias method) |
|---|
| 253 |
{ |
|---|
| 254 |
static assert (dstep.objc.bridge.Bridge.ParameterTupleOf!(method).length == 1, "An action method is only allowed to have one parameter"); |
|---|
| 255 |
static assert (is(dstep.objc.bridge.Bridge.ParameterTupleOf!(method)[0] : Object), "An action method can only have a parameter of the type Object (or any of its subclasses)"); |
|---|
| 256 |
|
|---|
| 257 |
mixin ObjcBindMethod!(method, dstep.objc.bridge.TypeEncoding.buildSelector!(method)); |
|---|
| 258 |
} |
|---|
| 259 |
|
|---|
| 260 |
/** |
|---|
| 261 |
* Binds a selector to an instance method. |
|---|
| 262 |
* |
|---|
| 263 |
* This will create a receiver function which will forward the call to $(D_PARAM method), |
|---|
| 264 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 265 |
* This $(D_KEYWORD template) will use the buildSelector $(D_KEYWORD template) to build |
|---|
| 266 |
* the selector. It will automatically infer the return type and the argument types |
|---|
| 267 |
* of the method. |
|---|
| 268 |
* |
|---|
| 269 |
* Mixes in: ObjcBindMethod |
|---|
| 270 |
* |
|---|
| 271 |
* Examples: |
|---|
| 272 |
* --- |
|---|
| 273 |
* class AppController : NSObject |
|---|
| 274 |
* { |
|---|
| 275 |
* void foo () {} |
|---|
| 276 |
* mixin ObjcBindMethod!(foo); |
|---|
| 277 |
* } |
|---|
| 278 |
* --- |
|---|
| 279 |
* |
|---|
| 280 |
* Params: |
|---|
| 281 |
* method = the method to bind |
|---|
| 282 |
*/ |
|---|
| 283 |
template ObjcBindMethod (alias method) |
|---|
| 284 |
{ |
|---|
| 285 |
mixin ObjcBindMethod!(method, dstep.objc.bridge.TypeEncoding.buildSelector!(method)); |
|---|
| 286 |
} |
|---|
| 287 |
|
|---|
| 288 |
/** |
|---|
| 289 |
* Binds a selector to an instance method. |
|---|
| 290 |
* |
|---|
| 291 |
* This will create a receiver function which will forward the call to $(D_PARAM method), |
|---|
| 292 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 293 |
* It will automatically infer the return type and the argument types |
|---|
| 294 |
* of the method. |
|---|
| 295 |
* |
|---|
| 296 |
* Mixes in: ObjcBindMethod |
|---|
| 297 |
* |
|---|
| 298 |
* Examples: |
|---|
| 299 |
* --- |
|---|
| 300 |
* class AppController : NSObject |
|---|
| 301 |
* { |
|---|
| 302 |
* void foo () {} |
|---|
| 303 |
* mixin ObjcBindMethod!(foo, "foo"); |
|---|
| 304 |
* } |
|---|
| 305 |
* --- |
|---|
| 306 |
* |
|---|
| 307 |
* Params: |
|---|
| 308 |
* method = the method to bind |
|---|
| 309 |
* selector = the selector to bind the method to |
|---|
| 310 |
*/ |
|---|
| 311 |
template ObjcBindMethod (alias method, string selector) |
|---|
| 312 |
{ |
|---|
| 313 |
mixin ObjcBindMethod!(method, dstep.objc.bridge.Bridge.ReturnTypeOf!(method), selector, dstep.objc.bridge.Bridge.ParameterTupleOf!(method)); |
|---|
| 314 |
} |
|---|
| 315 |
|
|---|
| 316 |
/** |
|---|
| 317 |
* Binds a selector to an instance method. |
|---|
| 318 |
* |
|---|
| 319 |
* This will create a receiver method which will forward the call to $(D_PARAM method), |
|---|
| 320 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 321 |
* |
|---|
| 322 |
* Mixes in: ObjcWrap |
|---|
| 323 |
* |
|---|
| 324 |
* Examples: |
|---|
| 325 |
* --- |
|---|
| 326 |
* class AppController : NSObject |
|---|
| 327 |
* { |
|---|
| 328 |
* int foo (int x) |
|---|
| 329 |
* { |
|---|
| 330 |
* return x; |
|---|
| 331 |
* } |
|---|
| 332 |
* |
|---|
| 333 |
* mixin ObjcBindMethod!(foo, int, "foo:", int); |
|---|
| 334 |
* } |
|---|
| 335 |
* --- |
|---|
| 336 |
* |
|---|
| 337 |
* Params: |
|---|
| 338 |
* method = the method to bind |
|---|
| 339 |
* R = the return type of the method |
|---|
| 340 |
* selector = the selector to bind the method to |
|---|
| 341 |
* ARGS = the argument types of the method |
|---|
| 342 |
*/ |
|---|
| 343 |
template ObjcBindMethod (alias method, R, string selector, ARGS...) |
|---|
| 344 |
{ |
|---|
| 345 |
private |
|---|
| 346 |
{ |
|---|
| 347 |
/** |
|---|
| 348 |
* Resolves the virtual call |
|---|
| 349 |
* |
|---|
| 350 |
* Returns: a $(D_KEYWORD delegate) to the binded method |
|---|
| 351 |
*/ |
|---|
| 352 |
R delegate (ARGS) __resolveVirtualCall () |
|---|
| 353 |
{ |
|---|
| 354 |
return &method; |
|---|
| 355 |
} |
|---|
| 356 |
|
|---|
| 357 |
/// A type tuple with all the encapsulated types |
|---|
| 358 |
alias dstep.objc.bridge.Type.ObjcTypes!(ARGS) __ObjcArgs; |
|---|
| 359 |
|
|---|
| 360 |
/** |
|---|
| 361 |
* The receiver method, this will be the method called from the Objective-C side |
|---|
| 362 |
* |
|---|
| 363 |
* Params: |
|---|
| 364 |
* self = the Objective-C instance to call the method on |
|---|
| 365 |
* cmd = the Objective-C selector representing the method to call |
|---|
| 366 |
* objcArgs = the encapsulated arguments to the binded method |
|---|
| 367 |
* |
|---|
| 368 |
* Returns: whatever the binded method returns, encapsulated |
|---|
| 369 |
*/ |
|---|
| 370 |
extern (C) static dstep.objc.bridge.Type.ObjcType!(R) __forwardVirtualCall (dstep.objc.objc.id self, dstep.objc.objc.SEL cmd, __ObjcArgs objcArgs) |
|---|
| 371 |
in |
|---|
| 372 |
{ |
|---|
| 373 |
assert(dstep.objc.bridge.Capsule.isCapsule(self)); |
|---|
| 374 |
} |
|---|
| 375 |
body |
|---|
| 376 |
{ |
|---|
| 377 |
R delegate (ARGS) delegate () dg; |
|---|
| 378 |
dg.ptr = cast(void*) dstep.objc.bridge.Capsule.decapsule!(typeof(this))(self); |
|---|
| 379 |
dg.funcptr = &__resolveVirtualCall; |
|---|
| 380 |
ARGS args; |
|---|
| 381 |
|
|---|
| 382 |
foreach (i, a ; objcArgs) |
|---|
| 383 |
{ |
|---|
| 384 |
alias typeof(args[i]) ArgType; |
|---|
| 385 |
args[i] = dstep.objc.bridge.Capsule.decapsule!(ArgType)(a); |
|---|
| 386 |
} |
|---|
| 387 |
|
|---|
| 388 |
static if (is(R == void)) |
|---|
| 389 |
dg()(args); |
|---|
| 390 |
|
|---|
| 391 |
else |
|---|
| 392 |
return dstep.objc.bridge.Capsule.encapsule!(R)(dg()(args)); |
|---|
| 393 |
} |
|---|
| 394 |
|
|---|
| 395 |
/// The Objective-C method declaration for the binded method |
|---|
| 396 |
ObjcMethodDeclaration!(__forwardVirtualCall, R, selector, ARGS) __objcMethodDeclaration; |
|---|
| 397 |
|
|---|
| 398 |
static if (!is(typeof(this.__objcClass))) |
|---|
| 399 |
mixin ObjcWrap; |
|---|
| 400 |
} |
|---|
| 401 |
} |
|---|
| 402 |
|
|---|
| 403 |
/** |
|---|
| 404 |
* Binds a selector to a class (static) method. |
|---|
| 405 |
* |
|---|
| 406 |
* This will create a receiver function which will forward the call to $(D_PARAM method), |
|---|
| 407 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 408 |
* This $(D_KEYWORD template) will use the buildSelector $(D_KEYWORD template) |
|---|
| 409 |
* to build the selector. It will automatically infer the return type and the |
|---|
| 410 |
* argument types of the method. |
|---|
| 411 |
* |
|---|
| 412 |
* Mixes in: $(D_PSYMBOL ObjcBindClassMethod) |
|---|
| 413 |
* |
|---|
| 414 |
* Examples: |
|---|
| 415 |
* --- |
|---|
| 416 |
* class AppController : NSObject |
|---|
| 417 |
* { |
|---|
| 418 |
* static void foo () {} |
|---|
| 419 |
* mixin ObjcBindClassMethod!(foo); |
|---|
| 420 |
* } |
|---|
| 421 |
* --- |
|---|
| 422 |
* |
|---|
| 423 |
* Params: |
|---|
| 424 |
* method = the method to bind |
|---|
| 425 |
*/ |
|---|
| 426 |
template ObjcBindClassMethod (alias method) |
|---|
| 427 |
{ |
|---|
| 428 |
mixin ObjcBindClassMethod!(method, dstep.objc.bridge.TypeEncoding.buildSelector!(method)); |
|---|
| 429 |
} |
|---|
| 430 |
|
|---|
| 431 |
/** |
|---|
| 432 |
* Binds a selector to a class (static) method. |
|---|
| 433 |
* |
|---|
| 434 |
* This will create a receiver function which will forward the call to $(D_PARAM method), |
|---|
| 435 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 436 |
* It will automatically infer the return type and the argument types |
|---|
| 437 |
* of the method. |
|---|
| 438 |
* |
|---|
| 439 |
* Mixes in: $(D_PSYMBOL ObjcBindClassMethod) |
|---|
| 440 |
* |
|---|
| 441 |
* Examples: |
|---|
| 442 |
* --- |
|---|
| 443 |
* class AppController : NSObject |
|---|
| 444 |
* { |
|---|
| 445 |
* static void foo () {} |
|---|
| 446 |
* mixin ObjcBindClassMethod!(foo, "foo"); |
|---|
| 447 |
* } |
|---|
| 448 |
* --- |
|---|
| 449 |
* |
|---|
| 450 |
* Params: |
|---|
| 451 |
* method = the method to bind |
|---|
| 452 |
* selector = the selector to bind the method to |
|---|
| 453 |
*/ |
|---|
| 454 |
template ObjcBindClassMethod (alias method, string selector) |
|---|
| 455 |
{ |
|---|
| 456 |
mixin ObjcBindClassMethod!(method, dstep.objc.bridge.Bridge.ReturnTypeOf!(method), selector, dstep.objc.bridge.Bridge.ParameterTupleOf!(method)); |
|---|
| 457 |
} |
|---|
| 458 |
|
|---|
| 459 |
/** |
|---|
| 460 |
* Binds a selector to a class (static) method. |
|---|
| 461 |
* |
|---|
| 462 |
* This will create a receiver method which will forward the call to $(D_PARAM method), |
|---|
| 463 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 464 |
* |
|---|
| 465 |
* Examples: |
|---|
| 466 |
* --- |
|---|
| 467 |
* class AppController : NSObject |
|---|
| 468 |
* { |
|---|
| 469 |
* static int foo (int x) |
|---|
| 470 |
* { |
|---|
| 471 |
* return x; |
|---|
| 472 |
* } |
|---|
| 473 |
* |
|---|
| 474 |
* mixin ObjcBindClassMethod!(foo, int, "foo:", int); |
|---|
| 475 |
* } |
|---|
| 476 |
* --- |
|---|
| 477 |
* |
|---|
| 478 |
* Params: |
|---|
| 479 |
* method = the method to bind |
|---|
| 480 |
* R = the return type of the method |
|---|
| 481 |
* selector = the selector to bind the method to |
|---|
| 482 |
* ARGS = the argument types of the method |
|---|
| 483 |
*/ |
|---|
| 484 |
template ObjcBindClassMethod (alias method, R, string selector, ARGS...) |
|---|
| 485 |
{ |
|---|
| 486 |
private |
|---|
| 487 |
{ |
|---|
| 488 |
/// A type tuple with all the encapsulated types |
|---|
| 489 |
alias dstep.objc.bridge.Type.ObjcTypes!(ARGS) __ObjcArgs; |
|---|
| 490 |
|
|---|
| 491 |
/** |
|---|
| 492 |
* The receiver method, this will be the method called from the Objective-C side |
|---|
| 493 |
* |
|---|
| 494 |
* Params: |
|---|
| 495 |
* objcArgs = the encapsulated arguments to the binded method |
|---|
| 496 |
* |
|---|
| 497 |
* Returns: whatever the binded method returns, encapsulated |
|---|
| 498 |
*/ |
|---|
| 499 |
extern (C) static dstep.objc.bridge.Type.ObjcType!(R) __forwardStaticCall (dstep.objc.objc.Class self, dstep.objc.objc.SEL cmd, __ObjcArgs objcArgs) |
|---|
| 500 |
in |
|---|
| 501 |
{ |
|---|
| 502 |
assert(dstep.objc.bridge.Capsule.isCapsule(self)); |
|---|
| 503 |
} |
|---|
| 504 |
body |
|---|
| 505 |
{ |
|---|
| 506 |
R function (ARGS) funcPtr = &method; |
|---|
| 507 |
ARGS args; |
|---|
| 508 |
|
|---|
| 509 |
foreach (i, a ; objcArgs) |
|---|
| 510 |
{ |
|---|
| 511 |
alias typeof(args[i]) ArgType; |
|---|
| 512 |
args[i] = dstep.objc.bridge.Capsule.decapsule!(ArgType)(a); |
|---|
| 513 |
} |
|---|
| 514 |
|
|---|
| 515 |
static if (is(R == void)) |
|---|
| 516 |
funcPtr()(args); |
|---|
| 517 |
|
|---|
| 518 |
else |
|---|
| 519 |
return dstep.objc.bridge.Capsule.encapsule!(R)(funcPtr()(args)); |
|---|
| 520 |
} |
|---|
| 521 |
|
|---|
| 522 |
/// The Objective-C method declaration for the binded method |
|---|
| 523 |
ObjcMethodDeclaration!(__forwardStaticCall, R, selector, ARGS) __objcClassMethodDeclaration; |
|---|
| 524 |
|
|---|
| 525 |
static if (is(typeof(this.__objcClass))) |
|---|
| 526 |
mixin ObjcWrap; |
|---|
| 527 |
} |
|---|
| 528 |
} |
|---|
| 529 |
|
|---|
| 530 |
/** |
|---|
| 531 |
* This $(D_KEYWORD struct) represents an Objective-C method declaration. |
|---|
| 532 |
* |
|---|
| 533 |
* Examples: |
|---|
| 534 |
* --- |
|---|
| 535 |
* class C : NSObject |
|---|
| 536 |
* { |
|---|
| 537 |
* void foo (int x) {} |
|---|
| 538 |
* ObjcMethodDeclaration!(foo, void, "foo", int) objcMethodDecl; |
|---|
| 539 |
* } |
|---|
| 540 |
* --- |
|---|
| 541 |
* |
|---|
| 542 |
* Params: |
|---|
| 543 |
* imp = the D method |
|---|
| 544 |
* R = the return type of the method |
|---|
| 545 |
* name = the name of the method |
|---|
| 546 |
* ARGS = the argument types of the method |
|---|
| 547 |
*/ |
|---|
| 548 |
struct ObjcMethodDeclaration (alias imp, R, string name, ARGS...) |
|---|
| 549 |
{ |
|---|
| 550 |
dstep.objc.objc.IMP methodImp = cast(dstep.objc.objc.IMP) &imp; |
|---|
| 551 |
alias R returnType; |
|---|
| 552 |
const string methodName = name; |
|---|
| 553 |
alias ARGS argsType; |
|---|
| 554 |
} |
|---|
| 555 |
|
|---|
| 556 |
/** |
|---|
| 557 |
* Binds a D free function to an Objective-C free function. |
|---|
| 558 |
* |
|---|
| 559 |
* This will create a receiver function which will forward the call to the |
|---|
| 560 |
* binded function, decapsulating arguments and encapsulating the return value |
|---|
| 561 |
* as appropriate. |
|---|
| 562 |
* |
|---|
| 563 |
* Mixes in: $(D_PSYMBOL ObjcBindFunction) |
|---|
| 564 |
* |
|---|
| 565 |
* Examples: |
|---|
| 566 |
* --- |
|---|
| 567 |
* void foo (); |
|---|
| 568 |
* mixin ObjcBindFunction!(foo); |
|---|
| 569 |
* --- |
|---|
| 570 |
*/ |
|---|
| 571 |
template ObjcBindFunction (alias func) |
|---|
| 572 |
{ |
|---|
| 573 |
mixin ObjcBindFunction!(func, dstep.objc.bridge.Bridge.ReturnTypeOf!(func), dstep.objc.bridge.Bridge.ParameterTupleOf!(func)); |
|---|
| 574 |
} |
|---|
| 575 |
|
|---|
| 576 |
/** |
|---|
| 577 |
* Binds a D free function to an Objective-C free function. |
|---|
| 578 |
* |
|---|
| 579 |
* This will create a receiver function which will forward the call to the |
|---|
| 580 |
* binded function, decapsulating arguments and encapsulating the return value |
|---|
| 581 |
* as appropriate. |
|---|
| 582 |
* |
|---|
| 583 |
* Examples: |
|---|
| 584 |
* --- |
|---|
| 585 |
* char foo (int); |
|---|
| 586 |
* mixin ObjcBindFunction!(foo, char, int); |
|---|
| 587 |
* --- |
|---|
| 588 |
* |
|---|
| 589 |
* Params: |
|---|
| 590 |
* func = the function to bind |
|---|
| 591 |
* R = the return type of the function |
|---|
| 592 |
* ARGS = the argument types of the function |
|---|
| 593 |
*/ |
|---|
| 594 |
template ObjcBindFunction (alias func, R, ARGS...) |
|---|
| 595 |
{ |
|---|
| 596 |
private |
|---|
| 597 |
{ |
|---|
| 598 |
/// A type tuple with all the encapsulated types |
|---|
| 599 |
alias dstep.objc.bridge.Type.ObjcTypes!(ARGS) __ObjcArgs; |
|---|
| 600 |
|
|---|
| 601 |
/** |
|---|
| 602 |
* The receiver function, this will be the function called from the Objective-C side |
|---|
| 603 |
* |
|---|
| 604 |
* Params: |
|---|
| 605 |
* objcArgs = the encapsulated arguments to the binded function |
|---|
| 606 |
* |
|---|
| 607 |
* Returns: whatever the binded function returns, encapsulated |
|---|
| 608 |
*/ |
|---|
| 609 |
extern (C) dstep.internal.Types.ObjcType!(R) __forwardFunctionCall (__ObjcArgs objcArgs) |
|---|
| 610 |
{ |
|---|
| 611 |
R function (ARGS) funcPtr = &func; |
|---|
| 612 |
ARGS args; |
|---|
| 613 |
|
|---|
| 614 |
foreach (i, a ; objcArgs) |
|---|
| 615 |
{ |
|---|
| 616 |
alias typeof(args[i]) ArgType; |
|---|
| 617 |
args[i] = dstep.objc.bridge.Capsule.decapsule!(ArgType)(a); |
|---|
| 618 |
} |
|---|
| 619 |
|
|---|
| 620 |
static if (is(R == void)) |
|---|
| 621 |
funcPtr()(args); |
|---|
| 622 |
|
|---|
| 623 |
else |
|---|
| 624 |
return dstep.objc.bridge.Capsule.encapsule!(R)(funcPtr()(args)); |
|---|
| 625 |
} |
|---|
| 626 |
} |
|---|
| 627 |
} |
|---|
| 628 |
|
|---|
| 629 |
/// This $(D_KEYWORD class) acts like a name space for various methods and functions |
|---|
| 630 |
class Bridge |
|---|
| 631 |
{ |
|---|
| 632 |
private static Bridge bridgeInstance; |
|---|
| 633 |
|
|---|
| 634 |
/// The name of the method declaration variable mixed in in a $(D_KEYWORD class) |
|---|
| 635 |
const objcMethodDeclarationVar = "__objcMethodDeclaration"; |
|---|
| 636 |
|
|---|
| 637 |
/// The name of the class method declaration variable mixed in in a $(D_KEYWORD class) |
|---|
| 638 |
const objcClassMethodDeclarationVar = "__objcClassMethodDeclaration"; |
|---|
| 639 |
|
|---|
| 640 |
/// The name of the variable used on the Objective-C side to store the D object |
|---|
| 641 |
const dObjectVar = "dObject"; |
|---|
| 642 |
|
|---|
| 643 |
/// This alias is used as an internal representation of a D object |
|---|
| 644 |
//alias Object DObjectType; |
|---|
| 645 |
alias void* DObjectType; |
|---|
| 646 |
|
|---|
| 647 |
/** |
|---|
| 648 |
* Gets the only instance of this class |
|---|
| 649 |
* |
|---|
| 650 |
* Returns: the instance |
|---|
| 651 |
*/ |
|---|
| 652 |
static Bridge instance () |
|---|
| 653 |
{ |
|---|
| 654 |
if (bridgeInstance) |
|---|
| 655 |
return bridgeInstance; |
|---|
| 656 |
|
|---|
| 657 |
return bridgeInstance = new Bridge; |
|---|
| 658 |
} |
|---|
| 659 |
|
|---|
| 660 |
static: |
|---|
| 661 |
|
|---|
| 662 |
/** |
|---|
| 663 |
* Gets the value of an Objective-C instance variable |
|---|
| 664 |
* |
|---|
| 665 |
* Examples: |
|---|
| 666 |
* --- |
|---|
| 667 |
* id self; |
|---|
| 668 |
* int x = getObjcIvar!(int, "x")(self); |
|---|
| 669 |
* --- |
|---|
| 670 |
* |
|---|
| 671 |
* Params: |
|---|
| 672 |
* T = the type of the instance variable |
|---|
| 673 |
* name = the name of the instance variable |
|---|
| 674 |
* self = the Objective-C instance |
|---|
| 675 |
* |
|---|
| 676 |
* Returns: the value of the Objective-C variable |
|---|
| 677 |
*/ |
|---|
| 678 |
T getObjcIvar (T, string name) (id self) |
|---|
| 679 |
{ |
|---|
| 680 |
T value; |
|---|
| 681 |
|
|---|
| 682 |
self.getInstanceVariable!(T, name)(value); |
|---|
| 683 |
|
|---|
| 684 |
return value; |
|---|
| 685 |
} |
|---|
| 686 |
|
|---|
| 687 |
/** |
|---|
| 688 |
* Sets the value of an Objective-C instance variable |
|---|
| 689 |
* |
|---|
| 690 |
* Examples: |
|---|
| 691 |
* --- |
|---|
| 692 |
* id self; |
|---|
| 693 |
* Bridge.setObjcIvar!(int, "x")(self, 3); |
|---|
| 694 |
* --- |
|---|
| 695 |
* |
|---|
| 696 |
* Params: |
|---|
| 697 |
* T = the type of the instance variable |
|---|
| 698 |
* name = the name of the instance |
|---|
| 699 |
* objcObject = the Objective-C instance |
|---|
| 700 |
* value = the value to set |
|---|
| 701 |
*/ |
|---|
| 702 |
void setObjcIvar (T, string name) (id self, T value) |
|---|
| 703 |
{ |
|---|
| 704 |
GC.addRoot(value); |
|---|
| 705 |
self.setInstanceVariable!(T, dObjectVar)(value); |
|---|
| 706 |
} |
|---|
| 707 |
|
|---|
| 708 |
/** |
|---|
| 709 |
* Gets the D object stored in the given Objective-C instance |
|---|
| 710 |
* |
|---|
| 711 |
* Examples: |
|---|
| 712 |
* --- |
|---|
| 713 |
* NSObject object = new NSObject; |
|---|
| 714 |
* id self = object.objcObject; |
|---|
| 715 |
* assert(object == Bridge.getDObject(self)); |
|---|
| 716 |
* --- |
|---|
| 717 |
* |
|---|
| 718 |
* Params: |
|---|
| 719 |
* self = the Objective-C instance |
|---|
| 720 |
* |
|---|
| 721 |
* Returns: the D object or null |
|---|
| 722 |
*/ |
|---|
| 723 |
package DObjectType getDObject (id self) |
|---|
| 724 |
{ |
|---|
| 725 |
return getObjcIvar!(DObjectType, dObjectVar)(self); |
|---|
| 726 |
} |
|---|
| 727 |
|
|---|
| 728 |
/** |
|---|
| 729 |
* Stores the given D object in the given Objective-C instance |
|---|
| 730 |
* |
|---|
| 731 |
* Examples: |
|---|
| 732 |
* --- |
|---|
| 733 |
* NSObject object = new NSObject; |
|---|
| 734 |
* id self = object.objcObject; |
|---|
| 735 |
* Bridge.setDObject(object, self); |
|---|
| 736 |
* --- |
|---|
| 737 |
* |
|---|
| 738 |
* Params: |
|---|
| 739 |
* dObject = the D object to store |
|---|
| 740 |
* objcObject = the Objective-C instance to store the D object in |
|---|
| 741 |
* |
|---|
| 742 |
* Returns: the Objective-C instance |
|---|
| 743 |
*/ |
|---|
| 744 |
package id setDObject (Object dObject, id objcObject) |
|---|
| 745 |
{ |
|---|
| 746 |
auto o = cast(DObjectType) dObject; |
|---|
| 747 |
setObjcIvar!(DObjectType, dObjectVar)(objcObject, o); |
|---|
| 748 |
|
|---|
| 749 |
return objcObject; |
|---|
| 750 |
} |
|---|
| 751 |
|
|---|
| 752 |
/** |
|---|
| 753 |
* Deregisters the given Objective-C instance from the bridge |
|---|
| 754 |
* |
|---|
| 755 |
* Params: |
|---|
| 756 |
* objcInstance = the Objective-C instance to deregister |
|---|
| 757 |
*/ |
|---|
| 758 |
package void deregisterObjcInstance (id objcInstance) |
|---|
| 759 |
{ |
|---|
| 760 |
GC.removeRoot(getObjcIvar!(DObjectType, dObjectVar)(objcInstance)); |
|---|
| 761 |
} |
|---|
| 762 |
|
|---|
| 763 |
/** |
|---|
| 764 |
* This method wraps the family of $(D_PSYMBOL objc_msgSend) methods which is used to send a message |
|---|
| 765 |
* to an instance of a class. |
|---|
| 766 |
* |
|---|
| 767 |
* This method chooses the appropriate $(D_PSYMBOL objc_msgSend) method depending on the return value, |
|---|
| 768 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 769 |
* |
|---|
| 770 |
* Params: |
|---|
| 771 |
* R = the return type |
|---|
| 772 |
* selector = the selector to call |
|---|
| 773 |
* ARGS = the argument types |
|---|
| 774 |
* self = the reciver of the call |
|---|
| 775 |
* args = the arguments to the method |
|---|
| 776 |
* |
|---|
| 777 |
* Returns: whatever the method returns, encapsulaed |
|---|
| 778 |
*/ |
|---|
| 779 |
R invokeObjcMethod (R, string selector, ARGS...) (id self, ARGS args) |
|---|
| 780 |
{ |
|---|
| 781 |
static assert (checkSelector!(selector, ARGS), "The selector \"" ~ selector ~ "\" and the arguments " ~ ARGS.stringof ~ " do not match"); |
|---|
| 782 |
|
|---|
| 783 |
SEL sel = sel.registerName!(selector); |
|---|
| 784 |
ObjcTypes!(ARGS) objcArgs; |
|---|
| 785 |
|
|---|
| 786 |
foreach (i, a ; args) |
|---|
| 787 |
{ |
|---|
| 788 |
alias typeof(a) ArgType; |
|---|
| 789 |
|
|---|
| 790 |
objcArgs[i] = encapsule!(ArgType)(a); |
|---|
| 791 |
} |
|---|
| 792 |
|
|---|
| 793 |
static if (is(R == struct)) |
|---|
| 794 |
{ |
|---|
| 795 |
R result; |
|---|
| 796 |
self.msgSend_stret(result, sel, objcArgs); |
|---|
| 797 |
|
|---|
| 798 |
return result; |
|---|
| 799 |
} |
|---|
| 800 |
|
|---|
| 801 |
else static if (is(R == float) || is(R == double) || is(R == real)) |
|---|
| 802 |
{ |
|---|
| 803 |
version (X86) |
|---|
| 804 |
return self.msgSend_fpret!(R, ObjcTypes!(ARGS))(sel, objcArgs); |
|---|
| 805 |
|
|---|
| 806 |
else version (X86_64) |
|---|
| 807 |
{ |
|---|
| 808 |
static if (is(R == real)) |
|---|
| 809 |
return self.msgSend_fpret!(R)(sel, objcArgs); |
|---|
| 810 |
|
|---|
| 811 |
else |
|---|
| 812 |
return self.msgSend!(R)(sel, objcArgs); |
|---|
| 813 |
} |
|---|
| 814 |
|
|---|
| 815 |
else |
|---|
| 816 |
return self.msgSend!(R)(sel, objcArgs); |
|---|
| 817 |
} |
|---|
| 818 |
|
|---|
| 819 |
else static if (needsEncapsulation!(R)) |
|---|
| 820 |
return decapsule!(R)(self.msgSend(sel, objcArgs)); |
|---|
| 821 |
|
|---|
| 822 |
else |
|---|
| 823 |
return self.msgSend!(R, ObjcTypes!(ARGS))(sel, objcArgs); |
|---|
| 824 |
} |
|---|
| 825 |
|
|---|
| 826 |
/** |
|---|
| 827 |
* This method wraps the family of $(D_PSYMBOL objc_msgSend) methods which is used to send a message |
|---|
| 828 |
* to a class. |
|---|
| 829 |
* |
|---|
| 830 |
* This method chooses the appropriate $(D_PSYMBOL objc_msgSend) method depending on the return value, |
|---|
| 831 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 832 |
* |
|---|
| 833 |
* Params: |
|---|
| 834 |
* R = the return type |
|---|
| 835 |
* selector = the selector to call |
|---|
| 836 |
* ARGS = the argument types |
|---|
| 837 |
* cls = the reciver of the call |
|---|
| 838 |
* args = the arguments to the method |
|---|
| 839 |
* |
|---|
| 840 |
* Returns: whatever the method returns, encapsulaed |
|---|
| 841 |
*/ |
|---|
| 842 |
R invokeObjcClassMethod (R, string selector, ARGS...) (Class cls, ARGS args) |
|---|
| 843 |
{ |
|---|
| 844 |
return invokeObjcMethod!(R, selector, ARGS)(cast(id) cls, args); |
|---|
| 845 |
} |
|---|
| 846 |
|
|---|
| 847 |
/** |
|---|
| 848 |
* This method wraps the family of $(D_PSYMBOL objc_msgSendSuper) methods which is used to send a message |
|---|
| 849 |
* to an instance of a superclass. |
|---|
| 850 |
* |
|---|
| 851 |
* This method chooses the appropriate $(D_PSYMBOL objc_msgSendSuper) method depending on the return value, |
|---|
| 852 |
* decapsulating arguments and encapsulating the return value as appropriate. |
|---|
| 853 |
* |
|---|
| 854 |
* Params: |
|---|
| 855 |
* R = the return type |
|---|
| 856 |
* selector = the selector to call |
|---|
| 857 |
* ARGS = the argument types |
|---|
| 858 |
* self = the reciver of the call |
|---|
| 859 |
* args = the arguments to the method |
|---|
| 860 |
* |
|---|
| 861 |
* Returns: whatever the method returns, encapsulaed |
|---|
| 862 |
*/ |
|---|
| 863 |
R invokeObjcSuperMethod (R, string selector, ARGS...) (objc_super* self, ARGS args) |
|---|
| 864 |
{ |
|---|
| 865 |
static assert (checkSelector!(selector, ARGS), "The selector \"" ~ selector ~ "\" and the arguments " ~ ARGS.stringof ~ " do not match"); |
|---|
| 866 |
|
|---|
| 867 |
SEL sel = sel.registerName!(selector); |
|---|
| 868 |
ObjcTypes!(ARGS) objcArgs; |
|---|
| 869 |
|
|---|
| 870 |
foreach (i, a ; args) |
|---|
| 871 |
{ |
|---|
| 872 |
alias typeof(a) ArgType; |
|---|
| 873 |
|
|---|
| 874 |
objcArgs[i] = encapsule!(ArgType)(a); |
|---|
| 875 |
} |
|---|
| 876 |
|
|---|
| 877 |
static if (is(R == struct)) |
|---|
| 878 |
{ |
|---|
| 879 |
R result; |
|---|
| 880 |
self.msgSendSuper_stret(result, sel, objcArgs); |
|---|
| 881 |
|
|---|
| 882 |
return result; |
|---|
| 883 |
} |
|---|
| 884 |
|
|---|
| 885 |
else static if (needsEncapsulation!(R)) |
|---|
| 886 |
return decapsule!(R)(self.msgSendSuper(sel, objcArgs)); |
|---|
| 887 |
|
|---|
| 888 |
else |
|---|
| 889 |
return self.msgSendSuper!(R, ObjcTypes!(ARGS))(sel, objcArgs); |
|---|
| 890 |
} |
|---|
| 891 |
|
|---|
| 892 |
/** |
|---|
| 893 |
* This method wraps a call to a regular C free function, decapsulating arguments and |
|---|
| 894 |
* encapsulating the return value as appropriate. |
|---|
| 895 |
* |
|---|
| 896 |
* Params: |
|---|
| 897 |
* R = the return type |
|---|
| 898 |
* func = the function to call |
|---|
| 899 |
* ARGS = the argument types |
|---|
| 900 |
* args = the arguments to the function |
|---|
| 901 |
* |
|---|
| 902 |
* Returns: whatever the method returns, encapsulaed |
|---|
| 903 |
*/ |
|---|
| 904 |
R invokeObjcFunction (R, alias func, ARGS...) (ARGS args) |
|---|
| 905 |
{ |
|---|
| 906 |
auto funcPtr = &func; // use a function pointer instead of the alias because the function may not be public. |
|---|
| 907 |
ObjcTypes!(ARGS) objcArgs; |
|---|
| 908 |
|
|---|
| 909 |
foreach (i, a ; args) |
|---|
| 910 |
{ |
|---|
| 911 |
alias typeof(a) ArgType; |
|---|
| 912 |
|
|---|
| 913 |
objcArgs[i] = encapsule!(ArgType)(a); |
|---|
| 914 |
} |
|---|
| 915 |
|
|---|
| 916 |
static if (is(R == void)) |
|---|
| 917 |
funcPtr(objcArgs); |
|---|
| 918 |
|
|---|
| 919 |
else |
|---|
| 920 |
return decapsule!(R)(funcPtr(objcArgs)); |
|---|
| 921 |
} |
|---|
| 922 |
} |
|---|