-
Notifications
You must be signed in to change notification settings - Fork 122
/
index.js
234 lines (221 loc) · 8.92 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
module.exports = exports = NodObjC;
/**
* **NodObjC** is the bridge between Node.js and the Objective-C runtime and
* frameworks, making it possible to write native Cocoa applications (both GUI
* and command-line) using 100% Node.js. Applications are written entirely in
* JavaScript and interpreted at runtime.
*
* ## Getting Started
*
* Every **NodObjC** application begins with requiring the `NodObjC` module.
* You can name the returned module variable anything you want, but the
* "canonical" name for it is `$`. This is mostly because you're going to be using
* the variable all over the place, and probably want to keep it short.
*
* var $ = require('NodObjC')
*
* The next step is to [`import()`](import.html) a desired "Framework" that is
* installed on the system. These frameworks are the APIs provided to Objective-C,
* which could be the default frameworks (provided by Apple) or 3rd party
* frameworks written by others (or you). The "Foundation" framework is the...
* well.. foundation of these APIs, providing the most basic and important
* classes like `NSString` and `NSArray`.
*
* $.import('Foundation')
*
* [`import()`](import.html) doesn't return anything, however it will throw an
* Error if anything goes wrong. What happens after the import call is that the
* `$` variable now has a whole bunch of new properties attached to it, the
* exports from the imported framework. At this point, you can fully interact
* with these Objective-C classes, creating instances, subclassing, swizzling,
* etc.
*
* A lot of core classes expect an `NSAutoreleasePool` instance on the stack, so
* the first Objective-C object instance you create is usually one of those.
*
* var pool = $.NSAutoreleasePool('alloc')('init')
*
* Pretty simple! You don't need to worry about the autorelease pool after this.
* Now, for an example, try creating an `NSArray` instance, well, an
* `NSMutableArray` technically, so we can also add an `NSString` to it.
*
* var array = $.NSMutableArray('alloc')('init')
*
* array('addObject', $('Hello World!'))
*
* console.log(array)
* // (
* // "Hello World!"
* // )
*
* So there's an `NSArray` instance with a `count` (Objective-C's version of
* `Array#length`) of `1`, containing an `NSString` with the text "Hello World!".
* From here on out, you will need to refer to your Cocoa documentation for the
* rest of the available methods `NSArray` offers.
*
* ## Message Sending Syntax
*
* To send an Objective-C message to an Objective-C object using **NodObjC**, you
* have to **invoke the object as a function**, where the **even number arguments
* make up the message name** and the **odd numbered arguments are the arguments**
* to send to the [object](id.html).
*
* object('messageNameWithArg', someArg, 'andArg', anotherArg)
*
* This sounds and probably looks strange at first, but this is the cleanest
* syntax while still being valid JS. It also maintains the "readabililty" of
* typical Objective-C method names.
*
* ## Dynamic Object Introspection
*
* Since **NodObjC** runs in an interpreted environment, it is actually *very
* easy* to dynamically inspect the defined methods, instance variables (ivars),
* implemented protocols, and more of any given Objective-C object (a.k.a.
* [`id`](id.html) instances).
*
* Using the same `array` instance as before, you can retreive a list of the
* type of class, and it's subclasses, by calling the `.ancestors()` function.
*
* array.ancestors()
* // [ '__NSArrayM',
* // 'NSMutableArray',
* // 'NSArray',
* // 'NSObject' ]
*
* Also commonly of interest are the given methods an object responds to. Use the
* `.methods()` function for that.
*
* array.methods()
* // [ 'addObject:',
* // 'copyWithZone:',
* // 'count',
* // 'dealloc',
* // 'finalize',
* // 'getObjects:range:',
* // 'indexOfObjectIdenticalTo:',
* // 'insertObject:atIndex:',
* // 'objectAtIndex:',
* // 'removeLastObject',
* // 'removeObjectAtIndex:',
* // 'replaceObjectAtIndex:withObject:' ]
*
* ## More Docs
*
* Check out the rest of the doc pages for some of the other important
* **NodObjC** pieces.
*
* * [Block](block.html) - How to use an Objective-C "block" function.
* * [Class](class.html) - Subclassing and adding methods at runtime.
* * [Exception](exception.html) - **NodObjC** exceptions *are* JavaScript `Error` objects.
* * [id](id.html) - The wrapper class for every Objective-C object.
* * [Import](import.html) - Importing "Frameworks" into the process.
* * [Ivars](ivar.html) - Instance variable definitions.
* * [Method](method.html) - Method definitions and swizzling.
* * [Structs](struct.html) - Using Structs and C functions in **NodObjC**.
*/
/**
* `NodObjC` makes it seamless to catch Objective-C exceptions, using the standard
* JavaScript `try`/`catch` syntax you are already familiar with.
*
* When an Objective-C method or function throws an `NSException`, you can catch
* the exception and inspect it further. The error object that gets passed can be
* invoked to send messages, just like any other Objective-C object in `NodObjC`.
* The error object also has it's `message` and `stack` properties set, so that
* you can easily retrieve the error message and get a dump of the stack trace.
*
* var array = $.NSMutableArray('alloc')('init')
*
* try {
*
* // This will throw an exception since you can't add a null pointer
* array('addObject', null)
*
* } catch (err) {
*
* err('name')
* // 'NSInvalidArgumentException'
*
* err('reason')
* // '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
*
* err('reason') == err.message
* // true
*
* err.stack
* // NSInvalidArgumentException: *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
* // at Function.msgSend (/Users/nrajlich/NodObjC/lib/id.js:139:21)
* // at id (/Users/nrajlich/NodObjC/lib/id.js:105:15)
* // at Object.<anonymous> (/Users/nrajlich/NodObjC/array-exception.js:8:3)
* // at Module._compile (module.js:411:26)
* // at Object..js (module.js:417:10)
* // at Module.load (module.js:343:31)
* // at Function._load (module.js:302:12)
* // at Array.0 (module.js:430:10)
* // at EventEmitter._tickCallback (node.js:126:26)
*
* }
*/
/**
* This function accepts native JS types (`String`, `Number`, `Date`. `RegExp`, `Buffer`, `function, [rtnType, [self, argType1, argType2, ...]`) and converts
* them to the proper Objective-C type (`NSString`, `NSNumber`, `NSDate`, `NSRegularExpression`, `NSData`, `Objective-C block ^`).
*
* Often times, you will use this function to cast a JS String into an NSString
* for methods that accept NSStrings (since NodObjC doesn't automatically cast to
* NSStrings in those instances).
*
* var jsString = 'a javascript String'
* var nsString = $(jsString)
*
* $.NSLog(nsString)
*
* @param {String|Number|Date} o the JS object to convert to a Cocoa equivalent type.
* @return {id} The equivalent Cocoa type as the input object. Could be an `NSString`, `NSNumber` or `NSDate`.
*/
function NodObjC (o, m) {
var t = typeof o;
if (t == 'string') {
return exports.NSString('stringWithUTF8String', String(o));
} else if (t == 'number') {
return exports.NSNumber('numberWithDouble', Number(o));
} else if ((o instanceof Date ) || (Object.prototype.toString.call(o) == '[object Date]')) {
return exports.NSDate('dateWithTimeIntervalSince1970', o / 1000);
} else if (Buffer.isBuffer(o)) {
return exports.NSData(
'dataWithBytesNoCopy', o,
'length', o.length);
} else if (Object.prototype.toString.call(o) === '[object RegExp]') {
var options = 0;
//if (o.global) options |= exports. ???
if (o.ignoreCase) options |= exports.NSRegularExpressionCaseInsensitive;
if (o.multiline) options |= exports.NSRegularExpressionAnchorsMatchLines;
// TODO: add NSError support here
var err = null;
return exports.NSRegularExpression(
'regularExpressionWithPattern', exports(o.source),
'options', options,
'error', err);
} else if (t == 'function') {
// create a block pointer
if (m) {
return Import.createBlock(o, m);
}
//TODO: create a function pointer, is this necessary?...
}
throw new Error('Unsupported object passed in to convert: ' + o);
}
var Import = require('./import');
var Types = require('./types');
exports.resolve = Import.resolve;
exports.import =
exports.framework =
exports.importFramework =
function framework (name, _recursive) {
var recursive = arguments.length <= 1 ? 99999999 : _recursive;
Import.import(name, false, exports, recursive);
};
exports.alloc = Import.allocReference;
exports.YES = '\x01';
exports.NO = '\x00';
exports.id = '@';
exports.selector = ':';
Types.registerTypes(exports);