Skip to content

Instantly share code, notes, and snippets.

@krasnobaev
Forked from TooTallNate/objc.js
Last active July 8, 2018 19:47
Show Gist options
  • Save krasnobaev/06f6c14a390dfb85fa5e261ae8dcab8e to your computer and use it in GitHub Desktop.
Save krasnobaev/06f6c14a390dfb85fa5e261ae8dcab8e to your computer and use it in GitHub Desktop.
Experimentations with node-ffi
// https://developer.apple.com/documentation/foundation/nsfilemanager/1642853-homedirectoryforuser
var ffi = require('ffi');
var objc = new ffi.Library(null, {
'objc_getClass': [ 'pointer', [ 'string' ] ],
'sel_registerName': [ 'pointer', [ 'string' ] ],
'objc_msgSend': [ 'pointer', [ 'pointer', 'pointer' ] ]
});
var objc2 = new ffi.Library(null, {
'objc_msgSend': [ 'string', [ 'pointer', 'pointer' ] ]
});
var objc4 = new ffi.Library(null, {
'objc_msgSend': [ 'pointer', [ 'pointer', 'pointer', 'string' ] ]
});
function getDescription (v) {
const description = objc.sel_registerName('description');
return objc2.objc_msgSend(objc.objc_msgSend(v, description), objc.sel_registerName('UTF8String'));
}
new ffi.DynamicLibrary('/System/Library/Frameworks/Foundation.framework/Foundation');
var NSFM = objc.objc_getClass('NSFileManager');
var nsfm = objc.objc_msgSend(NSFM, objc.sel_registerName('defaultManager'));
// (NSURL *)homeDirectoryForUser:(NSString *)userName;
var objc2_ = new ffi.Library(null, {
'objc_msgSend': [ 'pointer', [ 'pointer',
'pointer', 'pointer'
] ]
});
var NSString = objc.objc_getClass('NSString');
var stringWithUTF8String = objc.sel_registerName('stringWithUTF8String:');
var str = objc4.objc_msgSend(NSString, stringWithUTF8String, 'sypwex');
var homedir = objc2_.objc_msgSend(nsfm,
objc.sel_registerName('homeDirectoryForUser:'), str
);
getDescription(homedir);
var ffi = require('ffi');
var objc = new ffi.Library(null, {
'objc_getClass': [ 'pointer', [ 'string' ] ],
// | \> parameter type
// \> returned value type
'class_getName': [ 'string', [ 'pointer' ] ],
'sel_registerName': [ 'pointer', [ 'string' ] ],
'sel_getName': [ 'string', [ 'pointer' ] ],
'objc_msgSend': [ 'pointer', [ 'pointer', 'pointer' ] ]
});
// The problem with libffi and C functions that accepts varargs is that we
// have to create new wrappers for each different argument and return value
// signature that we encounter
var objc1 = new ffi.Library(null, {
'objc_msgSend': [ 'pointer', [ 'pointer', 'pointer', 'int32' ] ]
});
var objc2 = new ffi.Library(null, {
'objc_msgSend': [ 'string', [ 'pointer', 'pointer' ] ]
});
var objc3 = new ffi.Library(null, {
'objc_msgSend': [ 'pointer', [ 'pointer', 'pointer', 'pointer' ] ]
});
var objc4 = new ffi.Library(null, {
'objc_msgSend': [ 'pointer', [ 'pointer', 'pointer', 'string' ] ]
});
function getDescription (v) {
const description = objc.sel_registerName('description');
return objc2.objc_msgSend(objc.objc_msgSend(v, description), objc.sel_registerName('UTF8String'));
}
// dlopen equivalent
new ffi.DynamicLibrary('/System/Library/Frameworks/Foundation.framework/Foundation');
new ffi.DynamicLibrary('/System/Library/Frameworks/ScriptingBridge.framework/ScriptingBridge');
// NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
var NSAP = objc.objc_getClass('NSAutoreleasePool');
var alloc = objc.sel_registerName('alloc');
var init = objc.sel_registerName('init');
var pool = objc.objc_msgSend(objc.objc_msgSend(NSAP, alloc), init);
// NSMutableArray
var NSArray = objc.objc_getClass('NSMutableArray');
console.log(NSArray.address);
console.log(objc.class_getName(NSArray));
// NSString* str = [NSString stringWithUTF8String:"Hello Objective-C!"];
var NSString = objc.objc_getClass('NSString');
var stringWithUTF8String = objc.sel_registerName('stringWithUTF8String:');
var str = objc4.objc_msgSend(NSString, stringWithUTF8String, 'Hello Objective-C!');
console.log(getDescription(str));
//
var initWithCapacity = objc.sel_registerName('initWithCapacity:');
var addObject = objc.sel_registerName('addObject:');
console.log(objc.sel_getName(alloc));
var array = objc.objc_msgSend(NSArray, alloc);
array = objc1.objc_msgSend(array, init, 10);
objc3.objc_msgSend(array, addObject, NSArray);
objc3.objc_msgSend(array, addObject, str);
console.log(getDescription(array));
// Normally this wouldn't work properly. However we have dynamically loaded the
// ScriptingBridge framework binary file above so this will work properly.
var SBApplication = objc.objc_getClass('SBApplication');
console.log(SBApplication.address);
console.log(objc.class_getName(SBApplication));
var ffi = require('ffi');
var stdio = new ffi.Library(null, {
'printf': [ 'int32', [ 'string', 'string' ]]
});
stdio.printf('hello %s\n', 'world');
// hello world
var ffi = require('ffi');
var time = new ffi.Library(null, {
'time': [ 'int32', [ 'pointer' ] ]
});
// TypeError: ffi.Pointer is not a constructor ???
var NULL = new ffi.Pointer(0);
// Passing JS null converts to C NULL
console.log(time.time(null));
// new ffi.Pointer(0) creates a NULL pointer
console.log(time.time(NULL));
var time_t = new ffi.Pointer(4);
console.log(time.time(time_t));
console.log(time_t.getInt32());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment