ffi.manager.pp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2017 by the Free Pascal development team
  4. RTTI Function Call Manager using Foreign Function Call (libffi) library.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit ffi.manager;
  12. {$mode objfpc}{$H+}
  13. interface
  14. implementation
  15. uses
  16. TypInfo, Rtti, ffi;
  17. type
  18. Tpffi_typeArray = array of pffi_type;
  19. procedure FreeFFIType(t: pffi_type);
  20. var
  21. elements: Tpffi_typeArray;
  22. i: LongInt;
  23. begin
  24. if t^._type <> _FFI_TYPE_STRUCT then
  25. Exit;
  26. elements := Tpffi_typeArray(t^.elements);
  27. for i := Low(elements) to High(elements) do
  28. FreeFFIType(elements[i]);
  29. { with this the array will be freed }
  30. elements := Nil;
  31. Dispose(t);
  32. end;
  33. function TypeInfoToFFIType(aTypeInfo: PTypeInfo; aFlags: TParamFlags): pffi_type; forward;
  34. function RecordOrObjectToFFIType(aTypeInfo: PTypeInfo): pffi_type;
  35. var
  36. curindex: SizeInt;
  37. elements: Tpffi_typeArray;
  38. procedure AddElement(t: pffi_type);
  39. begin
  40. if curindex = Length(elements) then begin
  41. SetLength(elements, Length(elements) * 2);
  42. end;
  43. elements[curindex] := t;
  44. Inc(curindex);
  45. end;
  46. var
  47. td, fieldtd: PTypeData;
  48. i, j, curoffset, remoffset: SizeInt;
  49. field: PManagedField;
  50. ffitype: pffi_type;
  51. begin
  52. td := GetTypeData(aTypeInfo);
  53. if td^.TotalFieldCount = 0 then
  54. { uhm... }
  55. Exit(Nil);
  56. New(Result);
  57. FillChar(Result^, SizeOf(Result), 0);
  58. Result^._type := _FFI_TYPE_STRUCT;
  59. Result^.elements := Nil;
  60. curoffset := 0;
  61. curindex := 0;
  62. field := PManagedField(PByte(@td^.TotalFieldCount) + SizeOf(td^.TotalFieldCount));
  63. { assume first that there are no paddings }
  64. SetLength(elements, td^.TotalFieldCount);
  65. for i := 0 to td^.TotalFieldCount - 1 do begin
  66. { ToDo: what about fields that are larger that what we have currently? }
  67. if field^.FldOffset < curoffset then begin
  68. Inc(field);
  69. Continue;
  70. end;
  71. remoffset := field^.FldOffset - curoffset;
  72. { insert padding elements }
  73. while remoffset >= SizeOf(QWord) do begin
  74. AddElement(@ffi_type_uint64);
  75. Dec(remoffset, SizeOf(QWord));
  76. end;
  77. while remoffset >= SizeOf(LongWord) do begin
  78. AddElement(@ffi_type_uint32);
  79. Dec(remoffset, SizeOf(LongWord));
  80. end;
  81. while remoffset >= SizeOf(Word) do begin
  82. AddElement(@ffi_type_uint16);
  83. Dec(remoffset, SizeOf(Word));
  84. end;
  85. while remoffset >= SizeOf(Byte) do begin
  86. AddElement(@ffi_type_uint8);
  87. Dec(remoffset, SizeOf(Byte))
  88. end;
  89. { now add the real field type (Note: some are handled differently from
  90. being passed as arguments, so we handle those here) }
  91. if field^.TypeRef^.Kind = tkObject then
  92. AddElement(RecordOrObjectToFFIType(field^.TypeRef))
  93. else if field^.TypeRef^.Kind = tkSString then begin
  94. fieldtd := GetTypeData(field^.TypeRef);
  95. for j := 0 to fieldtd^.MaxLength + 1 do
  96. AddElement(@ffi_type_uint8);
  97. end else if field^.TypeRef^.Kind = tkArray then begin
  98. fieldtd := GetTypeData(field^.TypeRef);
  99. ffitype := TypeInfoToFFIType(fieldtd^.ArrayData.ElType, []);
  100. for j := 0 to fieldtd^.ArrayData.ElCount - 1 do
  101. AddElement(ffitype);
  102. end else
  103. AddElement(TypeInfoToFFIType(field^.TypeRef, []));
  104. Inc(field);
  105. curoffset := field^.FldOffset;
  106. end;
  107. { add a final Nil element }
  108. AddElement(Nil);
  109. { reduce array to final size }
  110. SetLength(elements, curindex);
  111. { this is a bit cheeky, but it works }
  112. Tpffi_typeArray(Result^.elements) := elements;
  113. end;
  114. function SetToFFIType(aSize: SizeInt): pffi_type;
  115. var
  116. elements: Tpffi_typeArray;
  117. curindex: SizeInt;
  118. procedure AddElement(t: pffi_type);
  119. begin
  120. if curindex = Length(elements) then begin
  121. SetLength(elements, Length(elements) * 2);
  122. end;
  123. elements[curindex] := t;
  124. Inc(curindex);
  125. end;
  126. begin
  127. if aSize = 0 then
  128. Exit(Nil);
  129. New(Result);
  130. Result^._type := _FFI_TYPE_STRUCT;
  131. Result^.elements := Nil;
  132. curindex := 0;
  133. SetLength(elements, aSize);
  134. while aSize >= SizeOf(QWord) do begin
  135. AddElement(@ffi_type_uint64);
  136. Dec(aSize, SizeOf(QWord));
  137. end;
  138. while aSize >= SizeOf(LongWord) do begin
  139. AddElement(@ffi_type_uint32);
  140. Dec(aSize, SizeOf(LongWord));
  141. end;
  142. while aSize >= SizeOf(Word) do begin
  143. AddElement(@ffi_type_uint16);
  144. Dec(aSize, SizeOf(Word));
  145. end;
  146. while aSize >= SizeOf(Byte) do begin
  147. AddElement(@ffi_type_uint8);
  148. Dec(aSize, SizeOf(Byte));
  149. end;
  150. AddElement(Nil);
  151. SetLength(elements, curindex);
  152. Tpffi_typeArray(Result^.elements) := elements;
  153. end;
  154. function TypeInfoToFFIType(aTypeInfo: PTypeInfo; aFlags: TParamFlags): pffi_type;
  155. function TypeKindName: String;
  156. begin
  157. Result := '';
  158. WriteStr(Result, aTypeInfo^.Kind);
  159. end;
  160. var
  161. td: PTypeData;
  162. begin
  163. Result := @ffi_type_void;
  164. if Assigned(aTypeInfo) then begin
  165. td := GetTypeData(aTypeInfo);
  166. if aFlags * [pfArray, pfOut, pfVar, pfConstRef] <> [] then
  167. Result := @ffi_type_pointer
  168. else
  169. case aTypeInfo^.Kind of
  170. tkInteger,
  171. tkEnumeration,
  172. tkBool,
  173. tkInt64,
  174. tkQWord:
  175. case td^.OrdType of
  176. otSByte:
  177. Result := @ffi_type_sint8;
  178. otUByte:
  179. Result := @ffi_type_uint8;
  180. otSWord:
  181. Result := @ffi_type_sint16;
  182. otUWord:
  183. Result := @ffi_type_uint16;
  184. otSLong:
  185. Result := @ffi_type_sint32;
  186. otULong:
  187. Result := @ffi_type_uint32;
  188. otSQWord:
  189. Result := @ffi_type_sint64;
  190. otUQWord:
  191. Result := @ffi_type_uint64;
  192. end;
  193. tkChar:
  194. Result := @ffi_type_uint8;
  195. tkFloat:
  196. case td^.FloatType of
  197. ftSingle:
  198. Result := @ffi_type_float;
  199. ftDouble:
  200. Result := @ffi_type_double;
  201. ftExtended:
  202. Result := @ffi_type_longdouble;
  203. { Comp and Currency are passed as Int64 (ToDo: on all platforms?) }
  204. ftComp:
  205. Result := @ffi_type_sint64;
  206. ftCurr:
  207. Result := @ffi_type_sint64;
  208. end;
  209. tkSet:
  210. case td^.OrdType of
  211. otUByte: begin
  212. if td^.SetSize = 1 then
  213. Result := @ffi_type_uint8
  214. else begin
  215. { ugh... build a of suitable record }
  216. Result := SetToFFIType(td^.SetSize);
  217. end;
  218. end;
  219. otUWord:
  220. Result := @ffi_type_uint16;
  221. otULong:
  222. Result := @ffi_type_uint32;
  223. end;
  224. tkWChar,
  225. tkUChar:
  226. Result := @ffi_type_uint16;
  227. tkInterface,
  228. tkAString,
  229. tkUString,
  230. tkWString,
  231. tkInterfaceRaw,
  232. tkProcVar,
  233. tkDynArray,
  234. tkClass,
  235. tkClassRef,
  236. tkPointer:
  237. Result := @ffi_type_pointer;
  238. tkMethod:
  239. Result := RecordOrObjectToFFIType(TypeInfo(TMethod));
  240. tkSString:
  241. { since shortstrings are rather large they're passed as references }
  242. Result := @ffi_type_pointer;
  243. tkObject:
  244. { passed around as pointer as well }
  245. Result := @ffi_type_pointer;
  246. tkArray:
  247. { arrays are passed as pointers to be compatible to C }
  248. Result := @ffi_type_pointer;
  249. tkRecord:
  250. Result := RecordOrObjectToFFIType(aTypeInfo);
  251. tkVariant:
  252. Result := RecordOrObjectToFFIType(TypeInfo(tvardata));
  253. //tkLString: ;
  254. //tkHelper: ;
  255. //tkFile: ;
  256. else
  257. raise EInvocationError.CreateFmt(SErrTypeKindNotSupported, [TypeKindName]);
  258. end;
  259. end else if aFlags * [pfOut, pfVar, pfConst, pfConstRef] <> [] then
  260. Result := @ffi_type_pointer;
  261. end;
  262. function ArgIsIndirect(aKind: TTypeKind; aFlags: TParamFlags; aIsResult: Boolean): Boolean;
  263. const
  264. ResultTypeNeedsIndirection = [
  265. tkAString,
  266. tkWString,
  267. tkUString,
  268. tkInterface,
  269. tkDynArray
  270. ];
  271. begin
  272. Result := False;
  273. if (aKind = tkSString) or
  274. (aIsResult and (aKind in ResultTypeNeedsIndirection)) or
  275. (aFlags * [pfArray, pfOut, pfVar, pfConstRef] <> []) or
  276. ((aKind = tkUnknown) and (pfConst in aFlags)) then
  277. Result := True;
  278. end;
  279. procedure FFIValueToValue(Source, Dest: Pointer; TypeInfo: PTypeInfo);
  280. var
  281. size: SizeInt;
  282. td: PTypeData;
  283. begin
  284. td := GetTypeData(TypeInfo);
  285. size := 0;
  286. case TypeInfo^.Kind of
  287. tkChar,
  288. tkWChar,
  289. tkUChar,
  290. tkEnumeration,
  291. tkBool,
  292. tkInteger,
  293. tkInt64,
  294. tkQWord:
  295. case td^.OrdType of
  296. otSByte,
  297. otUByte:
  298. size := 1;
  299. otSWord,
  300. otUWord:
  301. size := 2;
  302. otSLong,
  303. otULong:
  304. size := 4;
  305. otSQWord,
  306. otUQWord:
  307. size := 8;
  308. end;
  309. tkSet:
  310. size := td^.SetSize;
  311. tkFloat:
  312. case td^.FloatType of
  313. ftSingle:
  314. size := SizeOf(Single);
  315. ftDouble:
  316. size := SizeOf(Double);
  317. ftExtended:
  318. size := SizeOf(Extended);
  319. ftComp:
  320. size := SizeOf(Comp);
  321. ftCurr:
  322. size := SizeOf(Currency);
  323. end;
  324. tkMethod:
  325. size := SizeOf(TMethod);
  326. tkSString:
  327. size := td^.MaxLength + 1;
  328. tkDynArray,
  329. tkLString,
  330. tkAString,
  331. tkUString,
  332. tkWString,
  333. tkClass,
  334. tkPointer,
  335. tkClassRef,
  336. tkInterfaceRaw:
  337. size := SizeOf(Pointer);
  338. tkVariant:
  339. size := SizeOf(tvardata);
  340. tkArray:
  341. size := td^.ArrayData.Size;
  342. tkRecord:
  343. size := td^.RecSize;
  344. tkProcVar:
  345. size := SizeOf(CodePointer);
  346. tkObject: ;
  347. tkHelper: ;
  348. tkFile: ;
  349. end;
  350. if size > 0 then
  351. Move(Source^, Dest^, size);
  352. end;
  353. { move this to type info? }
  354. function RetInParam(aCallConv: TCallConv; aTypeInfo: PTypeInfo): Boolean;
  355. begin
  356. Result := False;
  357. if not Assigned(aTypeInfo) then
  358. Exit;
  359. case aTypeInfo^.Kind of
  360. tkSString,
  361. tkAString,
  362. tkWString,
  363. tkUString,
  364. tkInterface,
  365. tkDynArray:
  366. Result := True;
  367. end;
  368. end;
  369. { on X86 platforms Currency and Comp results are passed by the X87 if the
  370. Extended type is available }
  371. {$if (defined(CPUI8086) or defined(CPUI386) or defined(CPUX86_64)) and defined(FPC_HAS_TYPE_EXTENDED) and (not defined(FPC_COMP_IS_INT64) or not defined(FPC_CURRENCY_IS_INT64))}
  372. {$define USE_EXTENDED_AS_COMP_CURRENCY_RES}
  373. {$endif}
  374. type
  375. TFFIData = record
  376. Types: array of pffi_type;
  377. Values: array of Pointer;
  378. Indirect: array of Boolean;
  379. ResultType: pffi_type;
  380. ResultValue: Pointer;
  381. ResultIndex: SizeInt;
  382. {$ifdef USE_EXTENDED_AS_COMP_CURRENCY_RES}
  383. ResultTypeData: PTypeData;
  384. ResultExtended: Extended;
  385. {$endif}
  386. { put this at the end just in case we messed up the size }
  387. CIF: ffi_cif;
  388. end;
  389. procedure CreateCIF(constref aArgInfos: array of TFunctionCallParameterInfo; constref aArgValues: array of Pointer; aCallConv: TCallConv; aResultType: PTypeInfo; aResultValue: Pointer; aFlags: TFunctionCallFlags; out aData: TFFIData);
  390. function CallConvName: String; inline;
  391. begin
  392. WriteStr(Result, aCallConv);
  393. end;
  394. var
  395. abi: ffi_abi;
  396. i, arglen, argoffset, argstart: LongInt;
  397. usevalues, retparam: Boolean;
  398. kind: TTypeKind;
  399. types: ppffi_type;
  400. begin
  401. if not (fcfStatic in aFlags) and (Length(aArgInfos) = 0) then
  402. raise EInvocationError.Create(SErrMissingSelfParam);
  403. Assert((Length(aArgInfos) = Length(aArgValues)) or (Length(aArgValues) = 0), 'Amount of arguments does not match needed arguments');
  404. case aCallConv of
  405. {$if defined(CPUI386)}
  406. ccReg:
  407. abi := FFI_REGISTER;
  408. ccCdecl:
  409. {$ifdef WIN32}
  410. abi := FFI_MS_CDECL;
  411. {$else}
  412. abi := FFI_STDCALL;
  413. {$endif}
  414. ccPascal:
  415. abi := FFI_PASCAL;
  416. ccStdCall:
  417. abi := FFI_STDCALL;
  418. ccCppdecl:
  419. abi := FFI_THISCALL;
  420. {$else}
  421. {$ifndef CPUM68K}
  422. { M68k has a custom register calling convention implementation }
  423. ccReg,
  424. {$endif}
  425. ccCdecl,
  426. ccPascal,
  427. ccStdCall,
  428. ccCppdecl:
  429. abi := FFI_DEFAULT_ABI;
  430. {$endif}
  431. else
  432. raise EInvocationError.CreateFmt(SErrCallConvNotSupported, [CallConvName]);
  433. end;
  434. { if no values are provided we are called to prepare a callback, otherwise
  435. we are asked to prepare a invokation }
  436. usevalues := (Length(aArgInfos) > 0) and (Length(aArgValues) > 0);
  437. retparam := RetInParam(aCallConv, aResultType);
  438. arglen := Length(aArgInfos);
  439. if retparam then begin
  440. Inc(arglen);
  441. usevalues := True;
  442. argoffset := 1;
  443. aData.ResultIndex := 0;
  444. end else begin
  445. argoffset := 0;
  446. aData.ResultIndex := -1;
  447. end;
  448. SetLength(aData.Types, arglen);
  449. SetLength(aData.Indirect, arglen);
  450. if usevalues then
  451. SetLength(aData.Values, arglen);
  452. { the order is Self/Vmt (if any), Result param (if any), other params }
  453. if not (fcfStatic in aFlags) and retparam then begin
  454. aData.Types[0] := TypeInfoToFFIType(aArgInfos[0].ParamType, aArgInfos[0].ParamFlags);
  455. if Assigned(aArgInfos[0].ParamType) then
  456. kind := aArgInfos[0].ParamType^.Kind
  457. else
  458. kind := tkUnknown;
  459. aData.Indirect[0] := ArgIsIndirect(kind, aArgInfos[0].ParamFlags, False);
  460. if usevalues then
  461. if aData.Indirect[0] then
  462. aData.Values[0] := @aArgValues[0]
  463. else
  464. aData.Values[0] := aArgValues[0];
  465. if retparam then
  466. Inc(aData.ResultIndex);
  467. argstart := 1;
  468. end else
  469. argstart := 0;
  470. for i := argstart to High(aArgInfos) do begin
  471. aData.Types[i + argoffset] := TypeInfoToFFIType(aArgInfos[i].ParamType, aArgInfos[i].ParamFlags);
  472. if (pfResult in aArgInfos[i].ParamFlags) and not retparam then
  473. aData.ResultIndex := i + argoffset;
  474. if Assigned(aArgInfos[i].ParamType) then
  475. kind := aArgInfos[i].ParamType^.Kind
  476. else
  477. kind := tkUnknown;
  478. aData.Indirect[i + argoffset] := ArgIsIndirect(kind, aArgInfos[i].ParamFlags, False);
  479. if usevalues then
  480. if aData.Indirect[i + argoffset] then
  481. aData.Values[i + argoffset] := @aArgValues[i]
  482. else
  483. aData.Values[i + argoffset] := aArgValues[i];
  484. end;
  485. if retparam then begin
  486. aData.Types[aData.ResultIndex] := TypeInfoToFFIType(aResultType, []);
  487. aData.Indirect[aData.ResultIndex] := ArgIsIndirect(aResultType^.Kind, [], True);
  488. if usevalues then
  489. if aData.Indirect[aData.ResultIndex] then
  490. aData.Values[aData.ResultIndex] := @aResultValue
  491. else
  492. aData.Values[aData.ResultIndex] := aResultValue;
  493. aData.ResultType := @ffi_type_void;
  494. aData.ResultValue := Nil;
  495. {$ifdef USE_EXTENDED_AS_COMP_CURRENCY_RES}
  496. aData.ResultTypeData := Nil;
  497. {$endif}
  498. end else begin
  499. aData.ResultValue := Nil;
  500. {$ifdef USE_EXTENDED_AS_COMP_CURRENCY_RES}
  501. { special case for Comp/Currency as such arguments are passed as Int64,
  502. but the result is handled through the X87 }
  503. if Assigned(aResultType) and (aResultType^.Kind = tkFloat) then begin
  504. aData.ResultTypeData := GetTypeData(aResultType);
  505. case aData.ResultTypeData^.FloatType of
  506. {$ifndef FPC_CURRENCY_IS_INT64}
  507. ftCurr: begin
  508. aData.ResultType := @ffi_type_longdouble;
  509. aData.ResultValue := @aData.ResultExtended;
  510. end;
  511. {$endif}
  512. {$ifndef FPC_COMP_IS_INT64}
  513. ftComp: begin
  514. aData.ResultType := @ffi_type_longdouble;
  515. aData.ResultValue := @aData.ResultExtended;
  516. end;
  517. {$endif}
  518. end;
  519. end else
  520. aData.ResultTypeData := Nil;
  521. {$endif}
  522. if not Assigned(aData.ResultValue) then begin
  523. aData.ResultType := TypeInfoToFFIType(aResultType, []);
  524. if Assigned(aResultType) then
  525. aData.ResultValue := aResultValue
  526. else
  527. aData.ResultValue := Nil;
  528. end;
  529. end;
  530. if Assigned(aData.Types) then
  531. types := @aData.Types[0]
  532. else
  533. types := Nil;
  534. if ffi_prep_cif(@aData.CIF, abi, arglen, aData.ResultType, types) <> FFI_OK then
  535. raise EInvocationError.Create(SErrInvokeFailed);
  536. end;
  537. procedure FFIInvoke(aCodeAddress: Pointer; const aArgs: TFunctionCallParameterArray; aCallConv: TCallConv;
  538. aResultType: PTypeInfo; aResultValue: Pointer; aFlags: TFunctionCallFlags);
  539. var
  540. ffidata: TFFIData;
  541. i: SizeInt;
  542. arginfos: array of TFunctionCallParameterInfo;
  543. argvalues: array of Pointer;
  544. begin
  545. if Assigned(aResultType) and not Assigned(aResultValue) then
  546. raise EInvocationError.Create(SErrInvokeResultTypeNoValue);
  547. SetLength(arginfos, Length(aArgs));
  548. SetLength(argvalues, Length(aArgs));
  549. for i := 0 to High(aArgs) do begin
  550. arginfos[i] := aArgs[i].Info;
  551. argvalues[i] := aArgs[i].ValueRef;
  552. end;
  553. CreateCIF(arginfos, argvalues, aCallConv, aResultType, aResultValue, aFlags, ffidata);
  554. arginfos := Nil;
  555. argvalues := Nil;
  556. ffi_call(@ffidata.CIF, ffi_fn(aCodeAddress), ffidata.ResultValue, @ffidata.Values[0]);
  557. {$ifdef USE_EXTENDED_AS_COMP_CURRENCY_RES}
  558. if Assigned(ffidata.ResultTypeData) then begin
  559. case ffidata.ResultTypeData^.FloatType of
  560. {$ifndef FPC_CURRENCY_IS_INT64}
  561. ftCurr:
  562. PCurrency(aResultValue)^ := Currency(ffidata.ResultExtended / 10000);
  563. {$endif}
  564. {$ifndef FPC_COMP_IS_INT64}
  565. ftComp:
  566. PComp(aResultValue)^ := Comp(ffidata.ResultExtended);
  567. {$endif}
  568. end;
  569. end;
  570. {$endif}
  571. end;
  572. type
  573. TFFIFunctionCallback = class(TFunctionCallCallback)
  574. private
  575. fFFIData: TFFIData;
  576. fData: Pointer;
  577. fCode: CodePointer;
  578. fContext: Pointer;
  579. private
  580. class procedure ClosureFunc(aCIF: pffi_cif; aRet: Pointer; aArgs: PPointer; aUserData: Pointer); cdecl; static;
  581. procedure PassToHandler(aRet: Pointer; aArgs: PPointer);
  582. protected
  583. function GetCodeAddress: CodePointer; override;
  584. procedure CallHandler(constref aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer); virtual; abstract;
  585. public
  586. constructor Create(aContext: Pointer; aCallConv: TCallConv; constref aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags);
  587. destructor Destroy; override;
  588. end;
  589. TFFIFunctionCallbackMethod = class(TFFIFunctionCallback)
  590. private
  591. fHandler: TFunctionCallMethod;
  592. protected
  593. procedure CallHandler(constref aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer); override;
  594. public
  595. constructor Create(aHandler: TFunctionCallMethod; aContext: Pointer; aCallConv: TCallConv; constref aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags);
  596. end;
  597. TFFIFunctionCallbackProc = class(TFFIFunctionCallback)
  598. private
  599. fHandler: TFunctionCallProc;
  600. protected
  601. procedure CallHandler(constref aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer); override;
  602. public
  603. constructor Create(aHandler: TFunctionCallProc; aContext: Pointer; aCallConv: TCallConv; constref aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags);
  604. end;
  605. class procedure TFFIFunctionCallback.ClosureFunc(aCIF: pffi_cif; aRet: Pointer; aArgs: PPointer; aUserData: Pointer); cdecl;
  606. var
  607. this: TFFIFunctionCallback absolute aUserData;
  608. begin
  609. this.PassToHandler(aRet, aArgs);
  610. end;
  611. procedure TFFIFunctionCallback.PassToHandler(aRet: Pointer; aArgs: PPointer);
  612. var
  613. args: array of Pointer;
  614. i, arglen, argidx: SizeInt;
  615. resptr: Pointer;
  616. {$ifdef USE_EXTENDED_AS_COMP_CURRENCY_RES}
  617. {$ifndef FPC_COMP_IS_INT64}
  618. rescomp: Comp;
  619. {$endif}
  620. {$ifndef FPC_CURR_IS_INT64}
  621. rescurr: Currency;
  622. {$endif}
  623. {$endif}
  624. begin
  625. arglen := Length(fFFIData.Types);
  626. if fFFIData.ResultIndex >= 0 then
  627. Dec(arglen);
  628. SetLength(args, arglen);
  629. argidx := 0;
  630. for i := 0 to High(fFFIData.Types) do begin
  631. if i = fFFIData.ResultIndex then
  632. Continue;
  633. args[argidx] := aArgs[i];
  634. if fFFIData.Indirect[i] then
  635. args[argidx] := PPointer(aArgs[i])^
  636. else
  637. args[argidx] := aArgs[i];
  638. Inc(argidx);
  639. end;
  640. if fFFIData.ResultIndex >= 0 then begin
  641. if fFFIData.Indirect[fFFIData.ResultIndex] then
  642. resptr := PPointer(aArgs[fFFIData.ResultIndex])^
  643. else
  644. resptr := aArgs[fFFIData.ResultIndex];
  645. end else begin
  646. {$ifdef USE_EXTENDED_AS_COMP_CURRENCY_RES}
  647. resptr := Nil;
  648. if Assigned(fFFIData.ResultTypeData) then begin
  649. case fFFIData.ResultTypeData^.FloatType of
  650. {$ifndef FPC_COMP_IS_INT64}
  651. ftComp:
  652. resptr := @rescomp;
  653. {$endif}
  654. {$ifndef FPC_CURR_IS_INT64}
  655. ftCurr:
  656. resptr := @rescurr;
  657. {$endif}
  658. end;
  659. end;
  660. if not Assigned(resptr) then
  661. {$endif}
  662. resptr := aRet;
  663. end;
  664. CallHandler(args, resptr, fContext);
  665. {$ifdef USE_EXTENDED_AS_COMP_CURRENCY_RES}
  666. if Assigned(fFFIData.ResultTypeData) then begin
  667. case fFFIData.ResultTypeData^.FloatType of
  668. {$ifndef FPC_COMP_IS_INT64}
  669. ftComp:
  670. PExtended(aRet)^ := rescomp;
  671. {$endif}
  672. {$ifndef FPC_CURR_IS_INT64}
  673. ftCurr:
  674. PExtended(aRet) ^ := rescurr * 10000;
  675. {$endif}
  676. end;
  677. end;
  678. {$endif}
  679. end;
  680. function TFFIFunctionCallback.GetCodeAddress: CodePointer;
  681. begin
  682. Result := fData;
  683. end;
  684. constructor TFFIFunctionCallback.Create(aContext: Pointer; aCallConv: TCallConv; constref aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags);
  685. var
  686. res: ffi_status;
  687. begin
  688. fContext := aContext;
  689. CreateCIF(aArgs, [], aCallConv, aResultType, Nil, aFlags, fFFIData);
  690. fData := ffi_closure_alloc(SizeOf(ffi_closure), @fCode);
  691. if not Assigned(fData) or not Assigned(fCode) then
  692. raise ERTTI.Create(SErrMethodImplCreateFailed);
  693. res := ffi_prep_closure_loc(pffi_closure(fData), @fFFIData.CIF, @ClosureFunc, Self, fCode);
  694. if res <> FFI_OK then
  695. raise ERTTI.Create(SErrMethodImplCreateFailed);
  696. end;
  697. destructor TFFIFunctionCallback.Destroy;
  698. begin
  699. if Assigned(fData) then
  700. ffi_closure_free(fData);
  701. end;
  702. constructor TFFIFunctionCallbackProc.Create(aHandler: TFunctionCallProc; aContext: Pointer; aCallConv: TCallConv; constref aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags);
  703. begin
  704. inherited Create(aContext, aCallConv, aArgs, aResultType, aFlags);
  705. fHandler := aHandler;
  706. end;
  707. procedure TFFIFunctionCallbackProc.CallHandler(constref aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer);
  708. begin
  709. fHandler(aArgs, aResult, aContext);
  710. end;
  711. constructor TFFIFunctionCallbackMethod.Create(aHandler: TFunctionCallMethod; aContext: Pointer; aCallConv: TCallConv; constref aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags);
  712. begin
  713. inherited Create(aContext, aCallConv, aArgs, aResultType, aFlags);
  714. fHandler := aHandler;
  715. end;
  716. procedure TFFIFunctionCallbackMethod.CallHandler(constref aArgs: specialize TArray<Pointer>; aResult: Pointer; aContext: Pointer);
  717. begin
  718. fHandler(aArgs, aResult, aContext);
  719. end;
  720. function FFICreateCallbackProc(aHandler: TFunctionCallProc; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
  721. begin
  722. Result := TFFIFunctionCallbackProc.Create(aHandler, aContext, aCallConv, aArgs, aResultType, aFlags);
  723. end;
  724. function FFICreateCallbackMethod(aHandler: TFunctionCallMethod; aCallConv: TCallConv; aArgs: array of TFunctionCallParameterInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
  725. begin
  726. Result := TFFIFunctionCallbackMethod.Create(aHandler, aContext, aCallConv, aArgs, aResultType, aFlags);
  727. end;
  728. const
  729. FFIManager: TFunctionCallManager = (
  730. Invoke: @FFIInvoke;
  731. CreateCallbackProc: @FFICreateCallbackProc;
  732. CreateCallbackMethod: @FFICreateCallbackMethod;
  733. );
  734. var
  735. OldManagers: TFunctionCallManagerArray;
  736. const
  737. SupportedCallConvs = [ccReg, ccCdecl, ccStdCall, {ccCppdecl,} ccPascal];
  738. procedure InitFuncCallManager;
  739. begin
  740. SetFunctionCallManager(SupportedCallConvs, FFIManager, OldManagers);
  741. end;
  742. procedure DoneFuncCallManager;
  743. begin
  744. SetFunctionCallManagers(SupportedCallConvs, OldManagers);
  745. end;
  746. initialization
  747. InitFuncCallManager;
  748. finalization
  749. DoneFuncCallManager;
  750. end.