2
0

Setup.ScriptRunner.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. unit Setup.ScriptRunner;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2024 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Script runner
  8. }
  9. interface
  10. {$IFNDEF NOCODE}
  11. uses
  12. uPSRuntime, uPSDebugger, uPSUtils;
  13. {$ENDIF}
  14. type
  15. TScriptRunnerOnLog = procedure(const S: String);
  16. TScriptRunnerOnLogFmt = procedure(const S: String; const Args: array of const);
  17. TScriptRunnerOnDllImport = procedure(var DllName: String; var ForceDelayLoad: Boolean);
  18. TScriptRunnerOnDebug = function(const Position: LongInt; var ContinueStepOver: Boolean): Boolean;
  19. TScriptRunnerOnDebugIntermediate = function(const Position: LongInt; var ContinueStepOver: Boolean): Boolean;
  20. TScriptRunnerOnException = procedure(const Exception: AnsiString; const Position: LongInt);
  21. TBreakCondition = (bcNone, bcTrue, bcFalse, bcNonZero, bcNonEmpty);
  22. TScriptRunner = class
  23. private
  24. FNamingAttribute: String;
  25. {$IFNDEF NOCODE}
  26. FPSExec: TPSDebugExec;
  27. FClassImporter: TPSRuntimeClassImporter;
  28. {$ENDIF}
  29. FOnLog: TScriptRunnerOnLog;
  30. FOnLogFmt: TScriptRunnerOnLogFmt;
  31. FOnDllImport: TScriptRunnerOnDllImport;
  32. FOnDebug: TScriptRunnerOnDebug;
  33. FOnDebugIntermediate: TScriptRunnerOnDebugIntermediate;
  34. FOnException: TScriptRunnerOnException;
  35. {$IFNDEF NOCODE}
  36. function GetProcNos(const Name: AnsiString; const CheckNamingAttribute: Boolean; const ProcNos: TPSList): Integer;
  37. procedure InternalRunProcedure(const Name: AnsiString; const Parameters: array of Const; const CheckNamingAttribute, MustExist: Boolean);
  38. function InternalRunBooleanFunction(const Name: AnsiString; const Parameters: array of Const; const CheckNamingAttribute: Boolean; const BreakCondition: TBreakCondition; const MustExist, Default: Boolean): Boolean;
  39. function InternalRunIntegerFunction(const Name: AnsiString; const Parameters: array of Const; const CheckNamingAttribute: Boolean; const BreakCondition: TBreakCondition; const MustExist: Boolean; const Default: Integer): Integer;
  40. function InternalRunStringFunction(const Name: AnsiString; const Parameters: array of Const; const CheckNamingAttribute: Boolean; const BreakCondition: TBreakCondition; const MustExist: Boolean; const Default: String): String;
  41. procedure Log(const S: String);
  42. procedure LogFmt(const S: String; const Args: array of const);
  43. procedure RaisePSExecException;
  44. procedure SetPSExecParameters(const Parameters: array of Const; Params: TPSList);
  45. procedure SetPSExecReturnValue(Params: TPSList; BaseType: TPSBaseType; var Res: PPSVariant);
  46. {$ENDIF}
  47. public
  48. {$IFNDEF NOCODE}
  49. constructor Create;
  50. destructor Destroy; override;
  51. {$ENDIF}
  52. procedure LoadScript(const CompiledScriptText, CompiledScriptDebugInfo: AnsiString);
  53. function FunctionExists(const Name: AnsiString; const CheckNamingAttribute: Boolean): Boolean;
  54. procedure RunProcedure(const Name: AnsiString; const Parameters: array of Const; const MustExist: Boolean);
  55. procedure RunProcedures(const Name: AnsiString; const Parameters: array of Const; const MustExist: Boolean);
  56. function RunBooleanFunction(const Name: AnsiString; const Parameters: array of Const; const MustExist, Default: Boolean): Boolean;
  57. function RunBooleanFunctions(const Name: AnsiString; const Parameters: array of Const; const BreakCondition: TBreakCondition; const MustExist, Default: Boolean): Boolean;
  58. function RunIntegerFunction(const Name: AnsiString; const Parameters: array of Const; const MustExist: Boolean; const Default: Integer): Integer;
  59. function RunIntegerFunctions(const Name: AnsiString; const Parameters: array of Const; const BreakCondition: TBreakCondition; const MustExist: Boolean; Default: Integer): Integer;
  60. function RunStringFunction(const Name: AnsiString; const Parameters: array of Const; const MustExist: Boolean; const Default: String): String;
  61. function RunStringFunctions(const Name: AnsiString; const Parameters: array of Const; const BreakCondition: TBreakCondition; const MustExist: Boolean; Default: String): String;
  62. function EvaluateUsedVariable(const Param1, Param2, Param3: LongInt; const Param4: AnsiString): String;
  63. function GetCallStack(var CallStackCount: Cardinal): String;
  64. property NamingAttribute: String write FNamingAttribute;
  65. property OnLog: TScriptRunnerOnLog read FOnLog write FOnLog;
  66. property OnLogFmt: TScriptRunnerOnLogFmt read FOnLogFmt write FOnLogFmt;
  67. property OnDllImport: TScriptRunnerOnDllImport read FOnDllImport write FOnDllImport;
  68. property OnDebug: TScriptRunnerOnDebug read FOnDebug write FOnDebug;
  69. property OnDebugIntermediate: TScriptRunnerOnDebugIntermediate read FOnDebugIntermediate write FOnDebugIntermediate;
  70. property OnException: TScriptRunnerOnException read FOnException write FOnException;
  71. end;
  72. implementation
  73. uses
  74. {$IFNDEF NOCODE}
  75. Windows,
  76. Forms, SysUtils,
  77. uPSR_dll,
  78. Setup.ScriptClasses, Setup.ScriptFunc;
  79. {$ELSE}
  80. SysUtils;
  81. {$ENDIF}
  82. {$IFNDEF NOCODE}
  83. {---}
  84. { Note: Originally this unit used String() casts to avoid "Implicit string
  85. cast" warnings on Delphi 2009, but the casts were found to cause non-Unicode
  86. Setup to crash during tooltip variable evaluation due to some kind of code
  87. generation bug in Delphi 2. Removed all casts, and added the following to
  88. simply disable the warning. }
  89. {$IFDEF UNICODE}
  90. {$WARN IMPLICIT_STRING_CAST OFF}
  91. {$ENDIF}
  92. procedure TScriptRunner.Log(const S: String);
  93. begin
  94. if Assigned(FOnLog) then
  95. FOnLog(S);
  96. end;
  97. procedure TScriptRunner.LogFmt(const S: String; const Args: array of const);
  98. begin
  99. if Assigned(FOnLogFmt) then
  100. FOnLogFmt(S, Args);
  101. end;
  102. {$ENDIF}
  103. procedure ShowError(const Error: String);
  104. begin
  105. raise Exception.Create(Error);
  106. end;
  107. {$IFNDEF NOCODE}
  108. procedure ShowPSExecError(const Error: TPSError);
  109. begin
  110. ShowError('Script error: ' + PSErrorToString(Error, ''));
  111. end;
  112. procedure TScriptRunner.RaisePSExecException;
  113. var
  114. E: TObject;
  115. begin
  116. try
  117. FPSExec.RaiseCurrentException;
  118. except
  119. { Note: Don't use 'on E: Exception do' since that will also match
  120. 'Exception' objects raised from other modules (which we mustn't modify) }
  121. E := ExceptObject;
  122. if E is Exception then begin
  123. Exception(E).Message := Format('Runtime error (at %d:%d):'#13#10#13#10,
  124. [FPSExec.ExceptionProcNo, FPSExec.ExceptionPos]) + Exception(E).Message;
  125. raise;
  126. end
  127. else begin
  128. { If we don't see it as an Exception, it was likely raised by another
  129. module }
  130. raise Exception.CreateFmt('Runtime error (at %d:%d):'#13#10#13#10 +
  131. 'Exception "%s" at address %p',
  132. [FPSExec.ExceptionProcNo, FPSExec.ExceptionPos, E.ClassName, ExceptAddr]);
  133. end;
  134. end;
  135. end;
  136. procedure TScriptRunner.SetPSExecParameters(const Parameters: array of Const; Params: TPSList);
  137. var
  138. Param: PPSVariant;
  139. I: Integer;
  140. begin
  141. for I := High(Parameters) downto Low(Parameters) do begin
  142. case Parameters[I].vType of
  143. vtAnsiString:
  144. begin
  145. Param := CreateHeapVariant(FPSExec.FindType2(btString));
  146. PPSVariantAString(Param).Data := AnsiString(Parameters[I].vAnsiString);
  147. end;
  148. vtWideString:
  149. begin
  150. Param := CreateHeapVariant(FPSExec.FindType2(btWideString));
  151. PPSVariantWString(Param).Data := WideString(Parameters[I].VWideString);
  152. end;
  153. vtUnicodeString:
  154. begin
  155. Param := CreateHeapVariant(FPSExec.FindType2(btUnicodeString));
  156. PPSVariantUString(Param).Data := UnicodeString(Parameters[I].VUnicodeString);
  157. end;
  158. vtInteger:
  159. begin
  160. Param := CreateHeapVariant(FPSExec.FindType2(btS32));
  161. PPSVariantS32(Param).Data := Parameters[I].vInteger;
  162. end;
  163. vtBoolean:
  164. begin
  165. Param := CreateHeapVariant(FPSExec.FindType2(btU8));
  166. PPSVariantU8(Param).Data := Byte(Parameters[I].vBoolean);
  167. end;
  168. vtPointer:
  169. begin
  170. { Pointers are assumed to be pointers to Booleans }
  171. Param := CreateHeapVariant(FPSExec.FindType2(btU8));
  172. PPSVariantU8(Param).Data := Byte(Boolean(Parameters[I].VPointer^));
  173. end;
  174. else
  175. raise Exception.Create('TScriptRunner.SetPSExecParameters: Invalid type');
  176. end;
  177. Params.Add(Param);
  178. end;
  179. end;
  180. procedure TScriptRunner.SetPSExecReturnValue(Params: TPSList; BaseType: TPSBaseType; var Res: PPSVariant);
  181. begin
  182. Res := CreateHeapVariant(FPSExec.FindType2(BaseType));
  183. Params.Add(Res);
  184. end;
  185. {---}
  186. function EncodeDLLFilenameForROPS(const Filename: String): AnsiString;
  187. begin
  188. Result := '';
  189. if Filename <> '' then
  190. Result := AnsiString('<utf8>') + UTF8Encode(Filename);
  191. end;
  192. function NewUnloadDLLProc(Caller: TPSExec; Proc: TPSExternalProcRec; Global, Stack: TPSStack): Boolean;
  193. begin
  194. UnloadDLL(Caller, EncodeDLLFilenameForROPS(Stack.GetString(-1)));
  195. Result := True;
  196. end;
  197. function PSExecOnSpecialProcImport(Sender: TPSExec; p: TPSExternalProcRec; Tag: Pointer): Boolean;
  198. const
  199. SYesNo: array[Boolean] of String = ('No', 'Yes');
  200. var
  201. ScriptRunner: TScriptRunner;
  202. S, DllName, FunctionName: AnsiString;
  203. UnicodeDllName: String;
  204. I: Integer;
  205. ForceDelayLoad: Boolean;
  206. ErrorCode: LongInt;
  207. begin
  208. ScriptRunner := Sender.ID;
  209. ForceDelayLoad := False;
  210. ScriptRunner.Log('-- DLL function import --');
  211. S := p.Decl;
  212. I := Pos(AnsiString('dll:'), S);
  213. if I <> 1 then begin
  214. Result := False;
  215. Exit;
  216. end;
  217. Delete(S, 1, Length('dll:'));
  218. I := Pos(AnsiString(#0), S);
  219. if I = 0 then begin
  220. Result := False;
  221. Exit;
  222. end;
  223. DllName := Copy(S, 1, I-1);
  224. Delete(S, 1, I);
  225. I := Pos(AnsiString(#0), S);
  226. if I = 0 then begin
  227. Result := False;
  228. Exit;
  229. end;
  230. FunctionName := Copy(S, 1, I-1);
  231. UnicodeDllName := UTF8ToString(DllName);
  232. ScriptRunner.LogFmt('Function and DLL name: %s@%s', [FunctionName, UnicodeDllName]);
  233. if Assigned(ScriptRunner.FOnDllImport) then begin
  234. ScriptRunner.FOnDllImport(UnicodeDllName, ForceDelayLoad);
  235. DllName := EncodeDLLFilenameForROPS(UnicodeDllName);
  236. p.Decl := AnsiString('dll:') + DllName + Copy(p.Decl, Pos(AnsiString(#0), p.Decl), MaxInt);
  237. end;
  238. if DllName <> '' then
  239. ScriptRunner.LogFmt('Importing the DLL function. Dest DLL name: %s', [UnicodeDllName])
  240. else
  241. ScriptRunner.Log('Skipping.'); { We're actually still going to call ProcessDllImport but this doesn't matter to the user. }
  242. var DelayLoaded: Boolean;
  243. Result := ProcessDllImportEx2(Sender, p, ForceDelayLoad, DelayLoaded, ErrorCode);
  244. if DllName <> '' then begin
  245. if Result then
  246. ScriptRunner.LogFmt('Successfully imported the DLL function. Delay loaded? %s', [SYesNo[DelayLoaded]])
  247. else
  248. ScriptRunner.LogFmt('Failed to import the DLL function (%d).', [ErrorCode]);
  249. end;
  250. end;
  251. procedure PSExecOnSourceLine(Sender: TPSDebugExec; const Name: AnsiString; Position, Row, Col: Cardinal);
  252. var
  253. ScriptRunner: TScriptRunner;
  254. ContinueStepOver, NeedToResume: Boolean;
  255. begin
  256. ScriptRunner := Sender.ID;
  257. ContinueStepOver := False;
  258. if Sender.DebugMode = dmPaused then begin
  259. if Assigned(ScriptRunner.FOnDebug) then
  260. ScriptRunner.FOnDebug(Position, ContinueStepOver);
  261. NeedToResume := True;
  262. end else begin
  263. { Normally the debugger does not pause when it receives an 'intermediate'
  264. notification. However, it can happen if the user clicks Step Over and
  265. then Pause before the function call being stepped over has returned. }
  266. NeedToResume := False;
  267. if Assigned(ScriptRunner.FOnDebugIntermediate) then
  268. NeedToResume := ScriptRunner.FOnDebugIntermediate(Position, ContinueStepOver);
  269. end;
  270. if NeedToResume then begin
  271. if ContinueStepOver then
  272. Sender.StepOver()
  273. else
  274. Sender.StepInto();
  275. end;
  276. end;
  277. procedure PSExecOnException(Sender: TPSExec; ExError: TPSError; const ExParam: AnsiString; ExObject: TObject; ProcNo, Position: Cardinal);
  278. var
  279. ScriptRunner: TScriptRunner;
  280. begin
  281. ScriptRunner := Sender.ID;
  282. if Assigned(ScriptRunner.FOnException) then
  283. ScriptRunner.FOnException(PSErrorToString(ExError, ExParam), ScriptRunner.FPSExec.TranslatePosition(ProcNo, Position));
  284. { Clear any previous 'step over' state after an exception. Like Delphi,
  285. when F8 is pressed after an exception it should go to the first line of
  286. the nearest 'except' handler, not to the next line of some higher-level
  287. function that the user was stepping over prior to the exception. }
  288. ScriptRunner.FPSExec.StepInto();
  289. end;
  290. {---}
  291. constructor TScriptRunner.Create();
  292. begin
  293. FPSExec := TPSDebugExec.Create();
  294. FPSExec.ID := Self;
  295. FPSExec.AddSpecialProcImport('dll', PSExecOnSpecialProcImport, nil);
  296. FPSExec.OnSourceLine := PSExecOnSourceLine;
  297. FPSExec.OnException := PSExecOnException;
  298. RegisterDLLRuntimeEx(FPSExec, False, False);
  299. FPSExec.RegisterFunctionName('UNLOADDLL', NewUnloadDLLProc, nil, nil);
  300. FClassImporter := ScriptClassesLibraryRegister_R(FPSExec);
  301. ScriptFuncLibraryRegister_R(FPSExec);
  302. end;
  303. destructor TScriptRunner.Destroy;
  304. begin
  305. FPSExec.Free();
  306. FClassImporter.Free();
  307. end;
  308. {$ENDIF}
  309. procedure TScriptRunner.LoadScript(const CompiledScriptText, CompiledScriptDebugInfo: AnsiString);
  310. begin
  311. {$IFNDEF NOCODE}
  312. if FPSExec.LoadData(CompiledScriptText) then begin
  313. FPSExec.DebugEnabled := CompiledScriptDebugInfo <> '';
  314. if FPSExec.DebugEnabled then
  315. FPSExec.LoadDebugData(CompiledScriptDebugInfo);
  316. FPSExec.StepInto();
  317. end else begin
  318. RaisePSExecException;
  319. { In the case the above for some reason doesn't raise an exception, raise
  320. our own: }
  321. raise Exception.Create('TScriptRunner.LoadScript failed');
  322. end;
  323. {$ENDIF}
  324. end;
  325. {$IFNDEF NOCODE}
  326. function TScriptRunner.GetProcNos(const Name: AnsiString; const CheckNamingAttribute: Boolean; const ProcNos: TPSList): Integer;
  327. var
  328. MainProcNo, ProcNo: Cardinal;
  329. Proc: PIFProcRec;
  330. Attr: TPSRuntimeAttribute;
  331. begin
  332. Result := 0;
  333. { Locate main implementation. Will add later. }
  334. MainProcNo := FPSExec.GetProc(Name);
  335. { Locate other implementations using attributes. }
  336. if CheckNamingAttribute and (FNamingAttribute <> '') then begin
  337. for ProcNo := 0 to FPSExec.GetProcCount-1 do begin
  338. if ProcNo <> MainProcNo then begin
  339. Proc := FPSExec.GetProcNo(ProcNo);
  340. if Proc.Attributes.Count > 0 then begin
  341. Attr := Proc.Attributes.FindAttribute(AnsiString(FNamingAttribute));
  342. if (Attr <> nil) and (Attr.ValueCount = 1) and
  343. (((Attr.Value[0].FType.BaseType = btUnicodeString) and (CompareText(PPSVariantUString(Attr.Value[0]).Data, Name) = 0)) or
  344. ((Attr.Value[0].FType.BaseType = btString) and (CompareText(PPSVariantAString(Attr.Value[0]).Data, Name) = 0))) then begin
  345. if ProcNos <> nil then
  346. ProcNos.Add(Pointer(ProcNo));
  347. Inc(Result);
  348. end;
  349. end;
  350. end;
  351. end;
  352. end;
  353. { Add main implementation. Doing this last so it will be called last always. }
  354. if MainProcNo <> Cardinal(-1) then begin
  355. if ProcNos <> nil then
  356. ProcNos.Add(Pointer(MainProcNo));
  357. Inc(Result);
  358. end;
  359. end;
  360. {$ENDIF}
  361. function TScriptRunner.FunctionExists(const Name: AnsiString; const CheckNamingAttribute: Boolean): Boolean;
  362. begin
  363. {$IFNDEF NOCODE}
  364. Result := GetProcNos(Name, CheckNamingAttribute, nil) <> 0;
  365. {$ELSE}
  366. Result := False;
  367. {$ENDIF}
  368. end;
  369. {$IFNDEF NOCODE}
  370. procedure WriteBackParameters(const Parameters: array of Const; const Params: TPSList);
  371. var
  372. I: Integer;
  373. begin
  374. { Write back new Boolean values to vtPointer-type parameters }
  375. for I := 0 to High(Parameters) do
  376. if Parameters[I].vType = vtPointer then
  377. Boolean(Parameters[I].VPointer^) := (PPSVariantU8(Params[High(Parameters)-I]).Data = 1);
  378. end;
  379. procedure TScriptRunner.InternalRunProcedure(const Name: AnsiString; const Parameters: array of Const; const CheckNamingAttribute, MustExist: Boolean);
  380. var
  381. ProcNos, Params: TPSList;
  382. I: Integer;
  383. begin
  384. ProcNos := TPSList.Create;
  385. try
  386. if GetProcNos(Name, CheckNamingAttribute, ProcNos) <> 0 then begin
  387. ScriptClassesLibraryUpdateVars(FPSExec);
  388. for I := 0 to ProcNos.Count-1 do begin
  389. Params := TPSList.Create();
  390. try
  391. SetPSExecParameters(Parameters, Params);
  392. FPSExec.RunProc(Params, Cardinal(ProcNos[I]));
  393. WriteBackParameters(Parameters, Params);
  394. RaisePSExecException;
  395. finally
  396. FreePSVariantList(Params);
  397. end;
  398. end;
  399. end else begin
  400. if MustExist then
  401. ShowPSExecError(erCouldNotCallProc);
  402. end;
  403. finally
  404. ProcNos.Free;
  405. end;
  406. end;
  407. {$ELSE}
  408. procedure CheckMustExist(const MustExist: Boolean);
  409. begin
  410. if MustExist then
  411. ShowError('Unexpected MustExist value');
  412. end;
  413. {$ENDIF}
  414. procedure TScriptRunner.RunProcedure(const Name: AnsiString; const Parameters: array of Const; const MustExist: Boolean);
  415. begin
  416. {$IFNDEF NOCODE}
  417. InternalRunProcedure(Name, Parameters, False, MustExist);
  418. {$ELSE}
  419. CheckMustExist(MustExist);
  420. {$ENDIF}
  421. end;
  422. procedure TScriptRunner.RunProcedures(const Name: AnsiString; const Parameters: array of Const; const MustExist: Boolean);
  423. begin
  424. {$IFNDEF NOCODE}
  425. InternalRunProcedure(Name, Parameters, True, MustExist);
  426. {$ELSE}
  427. CheckMustExist(MustExist);
  428. {$ENDIF}
  429. end;
  430. {$IFNDEF NOCODE}
  431. function TScriptRunner.InternalRunBooleanFunction(const Name: AnsiString; const Parameters: array of Const; const CheckNamingAttribute: Boolean; const BreakCondition: TBreakCondition; const MustExist, Default: Boolean): Boolean;
  432. var
  433. ProcNos, Params: TPSList;
  434. Res: PPSVariant;
  435. I: Integer;
  436. begin
  437. ProcNos := TPSList.Create;
  438. try
  439. if GetProcNos(Name, CheckNamingAttribute, ProcNos) <> 0 then begin
  440. if not (BreakCondition in [bcNone, bcTrue, bcFalse]) or
  441. ((BreakCondition = bcNone) and (ProcNos.Count > 1)) then
  442. ShowError('Internal error: InternalRunBooleanFunction: invalid BreakCondition');
  443. Result := True; { Silence compiler }
  444. ScriptClassesLibraryUpdateVars(FPSExec);
  445. for I := 0 to ProcNos.Count-1 do begin
  446. Params := TPSList.Create();
  447. try
  448. SetPSExecParameters(Parameters, Params);
  449. SetPSExecReturnValue(Params, btU8, Res);
  450. FPSExec.RunProc(Params, Cardinal(ProcNos[I]));
  451. WriteBackParameters(Parameters, Params);
  452. RaisePSExecException;
  453. Result := PPSVariantU8(Res).Data = 1;
  454. if (Result and (BreakCondition = bcTrue)) or
  455. (not Result and (BreakCondition = bcFalse)) then
  456. Exit;
  457. finally
  458. FreePSVariantList(Params);
  459. end;
  460. end;
  461. end else begin
  462. if MustExist then
  463. ShowPSExecError(erCouldNotCallProc);
  464. Result := Default;
  465. end;
  466. finally
  467. ProcNos.Free;
  468. end;
  469. end;
  470. {$ENDIF}
  471. function TScriptRunner.RunBooleanFunction(const Name: AnsiString; const Parameters: array of Const; const MustExist, Default: Boolean): Boolean;
  472. begin
  473. {$IFNDEF NOCODE}
  474. Result := InternalRunBooleanFunction(Name, Parameters, False, bcNone, MustExist, Default);
  475. {$ELSE}
  476. CheckMustExist(MustExist);
  477. Result := Default;
  478. {$ENDIF}
  479. end;
  480. function TScriptRunner.RunBooleanFunctions(const Name: AnsiString; const Parameters: array of Const; const BreakCondition: TBreakCondition; const MustExist, Default: Boolean): Boolean;
  481. begin
  482. {$IFNDEF NOCODE}
  483. Result := InternalRunBooleanFunction(Name, Parameters, True, BreakCondition, MustExist, Default);
  484. {$ELSE}
  485. CheckMustExist(MustExist);
  486. Result := Default;
  487. {$ENDIF}
  488. end;
  489. {$IFNDEF NOCODE}
  490. function TScriptRunner.InternalRunIntegerFunction(const Name: AnsiString; const Parameters: array of Const; const CheckNamingAttribute: Boolean; const BreakCondition: TBreakCondition; const MustExist: Boolean; const Default: Integer): Integer;
  491. var
  492. ProcNos, Params: TPSList;
  493. Res: PPSVariant;
  494. I: Integer;
  495. begin
  496. ProcNos := TPSList.Create;
  497. try
  498. if GetProcNos(Name, CheckNamingAttribute, ProcNos) <> 0 then begin
  499. if not (BreakCondition in [bcNone, bcNonZero]) or
  500. ((BreakCondition = bcNone) and (ProcNos.Count > 1)) then
  501. ShowError('Internal error: InternalRunIntegerFunction: invalid BreakCondition');
  502. Result := 0; { Silence compiler }
  503. ScriptClassesLibraryUpdateVars(FPSExec);
  504. for I := 0 to ProcNos.Count-1 do begin
  505. Params := TPSList.Create();
  506. try
  507. SetPSExecParameters(Parameters, Params);
  508. SetPSExecReturnValue(Params, btS32, Res);
  509. FPSExec.RunProc(Params, Cardinal(ProcNos[I]));
  510. WriteBackParameters(Parameters, Params);
  511. RaisePSExecException;
  512. Result := PPSVariantS32(Res).Data;
  513. if (Result <> 0) and (BreakCondition = bcNonZero) then
  514. Exit;
  515. finally
  516. FreePSVariantList(Params);
  517. end;
  518. end;
  519. end else begin
  520. if MustExist then
  521. ShowPSExecError(erCouldNotCallProc);
  522. Result := Default;
  523. end;
  524. finally
  525. ProcNos.Free;
  526. end;
  527. end;
  528. {$ENDIF}
  529. function TScriptRunner.RunIntegerFunction(const Name: AnsiString; const Parameters: array of Const; const MustExist: Boolean; const Default: Integer): Integer;
  530. begin
  531. {$IFNDEF NOCODE}
  532. Result := InternalRunIntegerFunction(Name, Parameters, False, bcNone, MustExist, Default);
  533. {$ELSE}
  534. CheckMustExist(MustExist);
  535. Result := Default;
  536. {$ENDIF}
  537. end;
  538. function TScriptRunner.RunIntegerFunctions(const Name: AnsiString; const Parameters: array of Const; const BreakCondition: TBreakCondition; const MustExist: Boolean; Default: Integer): Integer;
  539. begin
  540. {$IFNDEF NOCODE}
  541. Result := InternalRunIntegerFunction(Name, Parameters, True, BreakCondition, MustExist, Default);
  542. {$ELSE}
  543. CheckMustExist(MustExist);
  544. Result := Default;
  545. {$ENDIF}
  546. end;
  547. {$IFNDEF NOCODE}
  548. function TScriptRunner.InternalRunStringFunction(const Name: AnsiString; const Parameters: array of Const; const CheckNamingAttribute: Boolean; const BreakCondition: TBreakCondition; const MustExist: Boolean; const Default: String): String;
  549. var
  550. ProcNos, Params: TPSList;
  551. Res: PPSVariant;
  552. I: Integer;
  553. begin
  554. ProcNos := TPSList.Create;
  555. try
  556. if GetProcNos(Name, CheckNamingAttribute, ProcNos) <> 0 then begin
  557. if not (BreakCondition in [bcNone, bcNonEmpty]) or
  558. ((BreakCondition = bcNone) and (ProcNos.Count > 1)) then
  559. ShowError('Internal error: InternalRunStringFunction: invalid BreakCondition');
  560. Result := ''; { Silence compiler }
  561. ScriptClassesLibraryUpdateVars(FPSExec);
  562. for I := 0 to ProcNos.Count-1 do begin
  563. Params := TPSList.Create();
  564. try
  565. SetPSExecParameters(Parameters, Params);
  566. SetPSExecReturnValue(Params, btUnicodeString, Res);
  567. FPSExec.RunProc(Params, Cardinal(ProcNos[I]));
  568. WriteBackParameters(Parameters, Params);
  569. RaisePSExecException;
  570. Result := PPSVariantUString(Res).Data;
  571. if (Result <> '') and (BreakCondition = bcNonEmpty) then
  572. Exit;
  573. finally
  574. FreePSVariantList(Params);
  575. end;
  576. end;
  577. end else begin
  578. if MustExist then
  579. ShowPSExecError(erCouldNotCallProc);
  580. Result := Default;
  581. end;
  582. finally
  583. ProcNos.Free;
  584. end;
  585. end;
  586. {$ENDIF}
  587. function TScriptRunner.RunStringFunction(const Name: AnsiString; const Parameters: array of Const; const MustExist: Boolean; const Default: String): String;
  588. begin
  589. {$IFNDEF NOCODE}
  590. Result := InternalRunStringFunction(Name, Parameters, False, bcNone, MustExist, Default);
  591. {$ELSE}
  592. CheckMustExist(MustExist);
  593. Result := Default;
  594. {$ENDIF}
  595. end;
  596. function TScriptRunner.RunStringFunctions(const Name: AnsiString; const Parameters: array of Const; const BreakCondition: TBreakCondition; const MustExist: Boolean; Default: String): String;
  597. begin
  598. {$IFNDEF NOCODE}
  599. Result := InternalRunStringFunction(Name, Parameters, True, BreakCondition, MustExist, Default);
  600. {$ELSE}
  601. CheckMustExist(MustExist);
  602. Result := Default;
  603. {$ENDIF}
  604. end;
  605. function TScriptRunner.EvaluateUsedVariable(const Param1, Param2, Param3: LongInt; const Param4: AnsiString): String;
  606. {$IFNDEF NOCODE}
  607. function VariantToString(const p: TPSVariantIFC; const ClassProperties: AnsiString): String;
  608. begin
  609. //PSVariantToString isn't Unicode enabled, handle strings ourselves
  610. //doesn't handle more complex types as records, arrays and objects
  611. if p.Dta <> nil then begin
  612. case p.aType.BaseType of
  613. btWideChar: Result := '''' + tbtWideChar(p.Dta^) + '''';
  614. btWideString: Result := '''' + tbtWideString(p.Dta^) + '''';
  615. btUnicodeString: Result := '''' + tbtUnicodeString(p.Dta^) + '''';
  616. else
  617. Result := PSVariantToString(p, ClassProperties);
  618. end;
  619. end else
  620. Result := PSVariantToString(p, ClassProperties);
  621. end;
  622. {$ENDIF}
  623. begin
  624. {$IFNDEF NOCODE}
  625. case TPSVariableType(Param1) of
  626. ivtGlobal:
  627. begin
  628. Result := FPSExec.GlobalVarNames[Param3];
  629. if Param4 <> '' then
  630. Result := Result + '.' + Param4;
  631. Result := Result + ' = ' + VariantToString(NewTPSVariantIFC(FPSExec.GetGlobalVar(Param3), False), Param4);
  632. end;
  633. ivtParam:
  634. begin
  635. if Param2 = LongInt(FPSExec.GetCurrentProcNo) then begin
  636. Result := FPSExec.CurrentProcParams[Param3];
  637. if Param4 <> '' then
  638. Result := Result + '.' + Param4;
  639. Result := Result + ' = ' + VariantToString(NewTPSVariantIFC(FPSExec.GetProcParam(Param3), False), Param4);
  640. end else
  641. Result := '';
  642. end;
  643. ivtVariable:
  644. begin
  645. if Param2 = LongInt(FPSExec.GetCurrentProcNo) then begin
  646. Result := FPSExec.CurrentProcVars[Param3];
  647. if Param4 <> '' then
  648. Result := Result + '.' + Param4;
  649. Result := Result + ' = ' + VariantToString(NewTPSVariantIFC(FPSExec.GetProcVar(Param3), False), Param4);
  650. end else
  651. Result := '';
  652. end;
  653. end;
  654. {$ELSE}
  655. Result := '';
  656. {$ENDIF}
  657. end;
  658. function TScriptRunner.GetCallStack(var CallStackCount: Cardinal): String;
  659. begin
  660. {$IFNDEF NOCODE}
  661. Result := FPSExec.GetCallStack(CallStackCount);
  662. {$ELSE}
  663. Result := '';
  664. {$ENDIF}
  665. end;
  666. end.