Files
lime/lime/system/CFFI.hx
2015-11-10 13:29:09 -08:00

768 lines
15 KiB
Haxe

package lime.system; #if !macro
#if sys
import sys.io.Process;
#end
class CFFI {
@:noCompletion private static var __moduleNames:Map<String, String> = null;
#if neko
private static var __loadedNekoAPI:Bool;
#elseif nodejs
private static var __nodeNDLLModule:Dynamic;
#end
public static var available:Bool;
public static var enabled:Bool;
private static function __init__ ():Void {
#if (cpp || neko || nodejs)
available = true;
enabled = #if disable_cffi false; #else true; #end
#else
available = false;
enabled = false;
#end
}
/**
* Tries to load a native CFFI primitive on compatible platforms
* @param library The name of the native library (such as "lime")
* @param method The exported primitive method name
* @param args The number of arguments
* @param lazy Whether to load the symbol immediately, or to allow lazy loading
* @return The loaded method
*/
public static function load (library:String, method:String, args:Int = 0, lazy:Bool = false):Dynamic {
#if (disable_cffi || macro)
var enabled = false;
#end
#if optional_cffi
if (library != "lime" || method != "neko_init") {
lazy = true;
}
#end
if (!enabled) {
return Reflect.makeVarArgs (function (__) return {});
}
var result:Dynamic = null;
#if (!disable_cffi && !macro)
#if (sys && !html5)
if (__moduleNames == null) __moduleNames = new Map<String, String> ();
if (lazy) {
__moduleNames.set (library, library);
try {
#if lime_legacy
if (library == "lime") return null;
#elseif !lime_hybrid
if (library == "lime-legacy") return null;
#end
#if neko
result = neko.Lib.loadLazy (library, method, args);
#elseif cpp
result = cpp.Lib.loadLazy (library, method, args);
#end
} catch (e:Dynamic) {}
} else {
#if (iphone || emscripten || android || static_link || tvos)
return cpp.Lib.load (library, method, args);
#end
if (__moduleNames.exists (library)) {
#if cpp
return cpp.Lib.load (__moduleNames.get (library), method, args);
#elseif neko
return neko.Lib.load (__moduleNames.get (library), method, args);
#elseif nodejs
return untyped __nodeNDLLModule.load_lib (__moduleNames.get (library), method, args);
#else
return null;
#end
}
#if waxe
if (library == "lime") {
flash.Lib.load ("waxe", "wx_boot", 1);
}
#elseif nodejs
if (__nodeNDLLModule == null) {
__nodeNDLLModule = untyped require('ndll');
}
#end
__moduleNames.set (library, library);
result = __tryLoad ("./" + library, library, method, args);
if (result == null) {
result = __tryLoad (".\\" + library, library, method, args);
}
if (result == null) {
result = __tryLoad (library, library, method, args);
}
if (result == null) {
var slash = (__sysName ().substr (7).toLowerCase () == "windows") ? "\\" : "/";
var haxelib = __findHaxelib ("lime");
if (haxelib != "") {
result = __tryLoad (haxelib + slash + "ndll" + slash + __sysName () + slash + library, library, method, args);
if (result == null) {
result = __tryLoad (haxelib + slash + "ndll" + slash + __sysName() + "64" + slash + library, library, method, args);
}
}
}
__loaderTrace ("Result : " + result);
}
#if neko
if (library == "lime" && method != "neko_init") {
__loadNekoAPI (lazy);
}
#end
#end
#else
result = function (_, _, _, _, _, _) { return { }; };
#end
return result;
}
public static macro function loadPrime (library:String, method:String, signature:String, lazy:Bool = false):Dynamic {
#if ((haxe_ver >= 3.2) && !display)
return cpp.Prime.load (library, method, signature, lazy);
#else
var args = signature.length - 1;
if (args > 5) {
args = -1;
}
return { call: CFFI.load (library, method, args, lazy) };
#end
}
private static function __findHaxelib (library:String):String {
#if (sys && !html5)
try {
var proc = new Process ("haxelib", [ "path", library ]);
if (proc != null) {
var stream = proc.stdout;
try {
while (true) {
var s = stream.readLine ();
if (s.substr (0, 1) != "-") {
stream.close ();
proc.close ();
__loaderTrace ("Found haxelib " + s);
return s;
}
}
} catch(e:Dynamic) { }
stream.close ();
proc.close ();
}
} catch (e:Dynamic) { }
#end
return "";
}
private static function __loaderTrace (message:String) {
#if (sys && !html5)
#if cpp
var get_env = cpp.Lib.load ("std", "get_env", 1);
var debug = (get_env ("OPENFL_LOAD_DEBUG") != null);
#else
var debug = (Sys.getEnv ("OPENFL_LOAD_DEBUG") !=null);
#end
if (debug) {
Sys.println (message);
}
#end
}
#if neko
private static function __loadNekoAPI (lazy:Bool):Void {
if (!__loadedNekoAPI) {
try {
var init = load ("lime", "neko_init", 5);
if (init != null) {
__loaderTrace ("Found nekoapi @ " + __moduleNames.get ("lime"));
init (function(s) return new String (s), function (len:Int) { var r = []; if (len > 0) r[len - 1] = null; return r; }, null, true, false);
} else if (!lazy) {
throw ("Could not find NekoAPI interface.");
}
#if lime_hybrid
var init = load ("lime-legacy", "neko_init", 5);
if (init != null) {
__loaderTrace ("Found nekoapi @ " + __moduleNames.get ("lime-legacy"));
init (function(s) return new String (s), function (len:Int) { var r = []; if (len > 0) r[len - 1] = null; return r; }, null, true, false);
} else if (!lazy) {
throw ("Could not find NekoAPI interface.");
}
#end
} catch (e:Dynamic) {
if (!lazy) {
throw ("Could not find NekoAPI interface.");
}
}
__loadedNekoAPI = true;
}
}
#end
private static function __sysName ():String {
#if (sys && !html5)
#if cpp
var sys_string = cpp.Lib.load ("std", "sys_string", 0);
return sys_string ();
#else
return Sys.systemName ();
#end
#else
return null;
#end
}
private static function __tryLoad (name:String, library:String, func:String, args:Int):Dynamic {
#if sys
try {
#if cpp
var result = cpp.Lib.load (name, func, args);
#elseif (neko)
var result = neko.Lib.load (name, func, args);
#elseif nodejs
var result = untyped __nodeNDLLModule.load_lib (name, func, args);
#else
var result = null;
#end
if (result != null) {
__loaderTrace ("Got result " + name);
__moduleNames.set (library, name);
return result;
}
} catch (e:Dynamic) {
__loaderTrace ("Failed to load : " + name);
}
#end
return null;
}
@:noCompletion @:dox(hide) public static var lime_cffi_set_finalizer = System.load ("lime", "lime_cffi_set_finalizer", 1);
}
#else
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.ComplexTypeTools;
using haxe.macro.ExprTools;
using haxe.macro.TypeTools;
class CFFI {
public static function build (defaultLibrary:String = "lime"):Array<Field> {
var pos = Context.currentPos ();
var fields = Context.getBuildFields ();
var newFields:Array<Field> = [];
#if !display
for (field in fields) {
switch (field) {
case _ => { kind: FFun (fun), meta: meta } :
for (m in meta) {
if (m.name == ":cffi") {
var library = null;
var method = null;
var lazy = false;
if (Reflect.hasField (m, "params")) {
if (m.params.length > 0) library = m.params[0].getValue ();
if (m.params.length > 1) method = m.params[1].getValue ();
if (m.params.length > 2) lazy = m.params[2].getValue ();
}
if (library == null || library == "") {
library = defaultLibrary;
}
if (method == null || method == "") {
method = field.name;
}
var typeArgs = [];
for (arg in fun.args) {
typeArgs.push ( { name: arg.name, opt: false, t: arg.type.toType () } );
}
var type = __getFunctionType (typeArgs, fun.ret.toType ());
var typeString = type.string;
var typeSignature = type.signature;
var expr = "";
if (Context.defined ("display") || Context.defined ("disable_cffi")) {
switch (type.result.toString ()) {
case "Int", "Float":
expr += "return 0";
case "Bool":
expr += "return false";
default:
expr += "return null";
}
} else {
var cffiName = "cffi_" + field.name;
var cffiExpr, cffiType;
#if (haxe_ver >= 3.2)
if (Context.defined ("cpp")) {
cffiExpr = 'new cpp.Callable<$typeString> (cpp.Prime._loadPrime ("$library", "$method", "$typeSignature", $lazy))';
} else {
var args = typeSignature.length - 1;
if (args > 5) {
args = -1;
}
cffiExpr = 'new cpp.Callable<$typeString> (lime.system.CFFI.load ("$library", "$method", $args, $lazy))';
}
cffiType = TPath ( { pack: [ "cpp" ], name: "Callable", params: [ TPType (TFun (type.args, type.result).toComplexType ()) ] } );
#else
var args = typeSignature.length - 1;
if (args > 5) {
args = -1;
}
cffiExpr = 'lime.system.CFFI.load ("$library", "$method", $args, $lazy)';
cffiType = TPath ( { pack: [ ], name: "Dynamic" } );
#end
newFields.push ( { name: cffiName, access: [ APrivate, AStatic ], kind: FieldType.FVar (cffiType, Context.parse (cffiExpr, field.pos)), pos: field.pos } );
if (type.result.toString () != "Void" && type.result.toString () != "cpp.Void") {
expr += "return ";
}
#if (haxe_ver >= 3.2)
expr += '$cffiName.call (';
#else
expr += '$cffiName (';
#end
for (i in 0...type.args.length) {
if (i > 0) expr += ", ";
expr += type.args[i].name;
}
expr += ")";
}
field.access.push (AInline);
fun.expr = Context.parse (expr, field.pos);
} else if (m.name == ":finalizer") {
for (otherField in fields) {
if (otherField.name == "new") {
switch (otherField.kind) {
case FFun (fun):
var fieldName = field.name;
var expr = fun.expr.toString ();
expr = expr.substring (0, expr.lastIndexOf ("}")) + '\n\t__finalizerTrigger = lime.system.CFFI.lime_cffi_set_finalizer ($fieldName);\n}';
fun.expr = Context.parse (expr, field.pos);
default:
}
}
}
newFields.push ({ name: "__finalizerTrigger", access: [ APrivate ], kind: FieldType.FVar (TPath ({ pack: [ ], name: "Dynamic" }), null), pos: field.pos });
}
}
default:
}
}
#end
fields = fields.concat (newFields);
return fields;
}
private static function __getFunctionType (args:Array<{ name : String, opt : Bool, t : Type }>, result:Type) {
#if ((haxe_ver >= 3.2) && !disable_cffi && !display)
var useCPPTypes = Context.defined ("cpp");
#else
var useCPPTypes = false;
#end
var typeArgs = [];
var typeResult = null;
var typeSignature = "";
for (arg in args) {
switch (arg.t.toString ()) {
case "Int", "cpp.Int16", "cpp.Int32":
typeArgs.push (arg);
typeSignature += "i";
case "Bool":
typeArgs.push (arg);
typeSignature += "b";
case "cpp.Float32":
if (useCPPTypes) {
typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Float32).toType () } );
} else {
typeArgs.push (arg);
}
typeSignature += "f";
case "Float", "cpp.Float64":
typeArgs.push (arg);
typeSignature += "d";
case "String":
typeArgs.push (arg);
typeSignature += "s";
case "cpp.ConstCharStar":
typeArgs.push (arg);
typeSignature += "c";
case "Void", "cpp.Void":
if (useCPPTypes) {
typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Void).toType () } );
} else {
typeArgs.push (arg);
}
typeSignature += "v";
default:
if (useCPPTypes) {
typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Object).toType () } );
} else {
typeArgs.push ( { name: arg.name, opt: false, t: (macro :Dynamic).toType () } );
}
typeSignature += "o";
}
}
switch (result.toString ()) {
case "Int", "cpp.Int16", "cpp.Int32":
typeResult = result;
typeSignature += "i";
case "Bool":
typeResult = result;
typeSignature += "b";
case "cpp.Float32":
if (useCPPTypes) {
typeResult = (macro :cpp.Float32).toType ();
} else {
typeResult = result;
}
typeSignature += "f";
case "Float", "cpp.Float64":
typeResult = result;
typeSignature += "d";
case "String":
typeResult = result;
typeSignature += "s";
case "cpp.ConstCharStar":
typeResult = result;
typeSignature += "c";
case "Void", "cpp.Void":
if (useCPPTypes) {
typeResult = (macro :cpp.Void).toType ();
} else {
typeResult = result;
}
typeSignature += "v";
default:
if (useCPPTypes) {
typeResult = (macro :cpp.Object).toType ();
} else {
typeResult = (macro :Dynamic).toType ();
}
typeSignature += "o";
}
var typeString = "";
if (typeArgs.length == 0) {
typeString = "Void->";
} else {
for (arg in typeArgs) {
typeString += arg.t.toString () + "->";
}
}
typeString += typeResult.toString ();
return { args: typeArgs, result: typeResult, string: typeString, signature: typeSignature };
}
}
#end