'use strict'; const op = {}; const ffi = require('ffi-napi'); const ref = require('ref-napi'); const JSBI = require('jsbi'); const Null = require('./null.js'); exports.op = op; const ctypes = { void: ref.types.void, cpointer: ref.refType(ref.types.void), utf8str: ref.types.CString, }; const shortInts = {int: true, short: true, char: true, ushort: true, uchar: true}; const longInts = {uint: true, longlong: true}; const floats = {double: true, float: true}; for (const type of Object.keys(floats).concat(Object.keys(shortInts), Object.keys(longInts))) { ctypes[type] = ref.types[type]; } function mapType(typeHash) { const nativeCallType = typeHash.content.get('type').$$getStr(); if (nativeCallType in ctypes) { return ctypes[nativeCallType]; } else { throw 'type not supported: ' + nativeCallType; } } op.buildnativecall = function(ctx, target, libname, symbol, convention, args, returns) { try { const symbols = {}; symbols[symbol] = [mapType(returns), args.array.map(mapType)]; target.$$lib = ffi.Library(libname === '' ? null : libname, symbols); // eslint-disable-line new-cap target.$$symbol = symbol; target.$$ret = returns.content.get('type').$$getStr(); target.$$args = args.array.map(arg => arg.content.get('type').$$getStr()); } catch (e) { throw new NQPException('native call exception:' + e); } }; op.nativecall = function(returns, callObject, args) { const mangled = []; for (let i = 0; i < args.array.length; i++) { const type = callObject.$$args[i]; // HACK for cpointers not being done properly yet const arg = type === 'cpointer' ? args.array[i] : args.array[i].$$decont(null); if (type in shortInts) { mangled[i] = arg.$$getInt(); } else if (type in longInts) { if (arg.$$getBignum) { mangled[i] = arg.$$getBignum().toString(); } else { mangled[i] = arg.$$getInt(); } } else if (type in floats) { mangled[i] = args.array[i].$$decont(null).$$getNum(); } else if (type === 'utf8str') { mangled[i] = args.array[i].$$decont(null).$$getStr(); } else if (type === 'cpointer') { mangled[i] = args.array[i].$$decont(null).$$getPointer(); } else { throw 'can\'t mangle: ' + callObject.$$args[i]; } } const ret = callObject.$$lib[callObject.$$symbol].apply(callObject.$$lib, mangled); if (callObject.$$ret === 'void') { return Null; } else if (callObject.$$ret === 'utf8str') { if (ret === null) { return returns; } else { const boxed = returns.$$STable.REPR.allocate(returns.$$STable); boxed.$$setStr(ret); return boxed; } } else if (callObject.$$ret === 'cpointer') { const boxed = returns.$$STable.REPR.allocate(returns.$$STable); boxed.$$setPointer(ret); return boxed; } else if (callObject.$$ret in shortInts) { const boxed = returns.$$STable.REPR.allocate(returns.$$STable); boxed.$$setInt(ret); return boxed; } else if (callObject.$$ret in longInts) { const boxed = returns.$$STable.REPR.allocate(returns.$$STable); boxed.$$setBignum(JSBI.BigInt(ret)); return boxed; } else if (callObject.$$ret in floats) { const boxed = returns.$$STable.REPR.allocate(returns.$$STable); boxed.$$setNum(ret); return boxed; } else { console.log('returning ', typeof callObject.$$ret, callObject.$$ret, shortInts, callObject.$$ret in shortInts); return ret === null ? Null : ret; } }; op.nativecallsizeof = function(obj) { if (obj.$$STable.REPR.nativeCallSize) { return obj.$$STable.REPR.nativeCallSize(); } else { throw new NQPException('can\'t do nativecallsizeof'); } }; op.initnativecall = function() { return 1; };