ffi.manager.pp 25 KB

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