ScriptCompiler.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. unit ScriptCompiler;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2018 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Script compiler
  8. }
  9. interface
  10. uses
  11. Classes, Generics.Collections, uPSUtils;
  12. type
  13. TScriptCompilerOnLineToLineInfo = procedure(const Line: LongInt; var Filename: String; var FileLine: LongInt) of object;
  14. TScriptCompilerOnUsedLine = procedure(const Filename: String; const Line, Position: LongInt; const IsProcExit: Boolean) of object;
  15. TScriptCompilerOnUsedVariable = procedure(const Filename: String; const Line, Col, Param1, Param2, Param3: LongInt; const Param4: AnsiString) of object;
  16. TScriptCompilerOnError = procedure(const Msg: String; const ErrorFilename: String; const ErrorLine: LongInt) of object;
  17. TScriptCompilerOnWarning = procedure(const Msg: String) of object;
  18. TScriptCompiler = class
  19. private
  20. FNamingAttribute: String;
  21. FObsoleteFunctionWarnings: TDictionary<String, String>;
  22. FExports, FUsedLines: TList;
  23. FFunctionsFound: TStringList;
  24. FScriptText: AnsiString;
  25. FOnLineToLineInfo: TScriptCompilerOnLineToLineInfo;
  26. FOnUsedLine: TScriptCompilerOnUsedLine;
  27. FOnUsedVariable: TScriptCompilerOnUsedVariable;
  28. FOnError: TScriptCompilerOnError;
  29. FOnWarning: TScriptCompilerOnWarning;
  30. function FindExport(const Name, Decl: String; const IgnoreIndex: Integer): Integer;
  31. function GetExportCount: Integer;
  32. procedure PSPositionToLineCol(Position: LongInt; var Line, Col: LongInt);
  33. procedure TriggerWarning(const Position: LongInt; const WarningType, WarningMessage: String);
  34. public
  35. constructor Create;
  36. destructor Destroy; override;
  37. procedure AddExport(const Name, Decl: String; const AllowNamingAttribute, Required: Boolean; const RequiredFilename: String; const RequiredLine: LongInt);
  38. function CheckExports: Boolean;
  39. function Compile(const ScriptText: String; var CompiledScriptText, CompiledScriptDebugInfo: tbtString): Boolean;
  40. property ExportCount: Integer read GetExportCount;
  41. function ExportFound(const Name: String): Boolean;
  42. function FunctionFound(const Name: String): Boolean;
  43. function IsObsoleteFunction(const Name: String): String;
  44. property NamingAttribute: String write FNamingAttribute;
  45. property OnLineToLineInfo: TScriptCompilerOnLineToLineInfo write FOnLineToLineInfo;
  46. property OnUsedLine: TScriptCompilerOnUsedLine write FOnUsedLine;
  47. property OnUsedVariable: TScriptCompilerOnUsedVariable write FOnUsedVariable;
  48. property OnError: TScriptCompilerOnError write FOnError;
  49. property OnWarning: TScriptCompilerOnWarning write FOnWarning;
  50. end;
  51. implementation
  52. uses
  53. SysUtils, Generics.Defaults,
  54. uPSCompiler, uPSC_dll,
  55. ScriptClasses_C, ScriptFunc_C;
  56. type
  57. TScriptExport = class
  58. Name, Decl: String;
  59. AllowNamingAttribute: Boolean;
  60. Required: Boolean;
  61. RequiredFilename: String;
  62. RequiredLine: LongInt;
  63. Exported: Boolean;
  64. end;
  65. {---}
  66. function PSPascalCompilerOnExternalProc(Sender: TPSPascalCompiler; Decl: TPSParametersDecl; const Name, FExternal: tbtstring): TPSRegProc;
  67. var
  68. S: String;
  69. P: Integer;
  70. begin
  71. S := String(FExternal) + ' ';
  72. P := Pos(' setuponly ', S);
  73. if P > 0 then begin
  74. Delete(S, P+1, Length('setuponly '));
  75. Insert('setup:', S, Pos('@', S)+1);
  76. end
  77. else begin
  78. P := Pos(' uninstallonly ', S);
  79. if P > 0 then begin
  80. Delete(S, P+1, Length('uninstallonly '));
  81. Insert('uninstall:', S, Pos('@', S)+1);
  82. end;
  83. end;
  84. if Pos('@uninstall:files:', S) <> 0 then begin
  85. Sender.MakeError('', ecCustomError, '"uninstallonly" cannot be used with "files:"');
  86. Result := nil;
  87. Exit;
  88. end;
  89. Result := DllExternalProc(Sender, Decl, Name, tbtstring(TrimRight(S)));
  90. end;
  91. function PSPascalCompilerOnApplyAttributeToProc(Sender: TPSPascalCompiler; aProc: TPSProcedure; Attr: TPSAttribute): Boolean;
  92. var
  93. ScriptCompiler: TScriptCompiler;
  94. AttrValue: String;
  95. ScriptExport: TScriptExport;
  96. B: Boolean;
  97. I: Integer;
  98. begin
  99. ScriptCompiler := TScriptCompiler(Sender.ID);
  100. if CompareText(String(Attr.AType.Name), ScriptCompiler.FNamingAttribute) = 0 then begin
  101. if aProc.ClassType <> TPSInternalProcedure then begin
  102. with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute cannot be used on external function or procedure', [ScriptCompiler.FNamingAttribute]))) do
  103. SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
  104. Result := False;
  105. end else if Attr.Count <> 1 then begin
  106. with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value not found', [ScriptCompiler.FNamingAttribute]))) do
  107. SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
  108. Result := False;
  109. end else begin
  110. if ScriptCompiler.FindExport(String(TPSInternalProcedure(aProc).Name), '', -1) <> -1 then begin
  111. { Don't allow attributes on functions already matching an export (by their name) so that we don't have to deal with this later. }
  112. with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute not allowed for function or procedure "%s"', [ScriptCompiler.FNamingAttribute, String(TPSInternalProcedure(aProc).Name)]))) do
  113. SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
  114. Result := False;
  115. end else begin
  116. AttrValue := String(GetString(Attr.Values[0], B));
  117. I := ScriptCompiler.FindExport(AttrValue, String(Sender.MakeDecl(TPSInternalProcedure(aProc).Decl)), -1);
  118. if I <> -1 then begin
  119. { The name from the attribute and the function prototype are both ok. }
  120. ScriptExport := ScriptCompiler.FExports[I];
  121. if not ScriptExport.AllowNamingAttribute then begin
  122. with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value "%s" not allowed', [ScriptCompiler.FNamingAttribute, AttrValue]))) do
  123. SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
  124. Result := False;
  125. end else begin
  126. ScriptExport.Exported := True;
  127. Result := True;
  128. end;
  129. end else if ScriptCompiler.FindExport(AttrValue, '', -1) <> -1 then begin
  130. { The name from the attribute is ok but the function prototype is not. }
  131. with Sender.MakeError('', ecCustomError, tbtstring(Format('Invalid function or procedure prototype for attribute value "%s"', [AttrValue]))) do
  132. SetCustomPos(TPSInternalProcedure(aProc).DeclarePos, TPSInternalProcedure(aProc).DeclareRow, TPSInternalProcedure(aProc).DeclareCol);
  133. Result := False;
  134. end else begin
  135. { The name from the attribute is not ok. }
  136. with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value "%s" invalid', [ScriptCompiler.FNamingAttribute, AttrValue]))) do
  137. SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
  138. Result := False;
  139. end;
  140. end;
  141. end;
  142. end else
  143. Result := True;
  144. end;
  145. function PSPascalCompilerOnApplyAttributeToType(Sender: TPSPascalCompiler; aType: TPSType; Attr: TPSAttribute): Boolean;
  146. var
  147. NamingAttribute: String;
  148. begin
  149. NamingAttribute := TScriptCompiler(Sender.ID).FNamingAttribute;
  150. if CompareText(String(Attr.AType.Name), NamingAttribute) = 0 then begin
  151. with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute cannot be used on types', [NamingAttribute]))) do
  152. SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
  153. Result := False;
  154. end else
  155. Result := True;
  156. end;
  157. function PSPascalCompilerOnUses(Sender: TPSPascalCompiler; const Name: tbtstring): Boolean;
  158. var
  159. NamingAttribute: String;
  160. begin
  161. if Name = 'SYSTEM' then begin
  162. RegisterDll_Compiletime(Sender);
  163. Sender.OnExternalProc := PSPascalCompilerOnExternalProc;
  164. ScriptClassesLibraryRegister_C(Sender);
  165. ScriptFuncLibraryRegister_C(Sender, TScriptCompiler(Sender.ID).FObsoleteFunctionWarnings);
  166. NamingAttribute := TScriptCompiler(Sender.ID).FNamingAttribute;
  167. if NamingAttribute <> '' then begin
  168. with Sender.AddAttributeType do begin
  169. OrgName := tbtstring(NamingAttribute);
  170. with AddField do begin
  171. FieldOrgName := 'Name';
  172. FieldType := Sender.FindType('String');
  173. end;
  174. OnApplyAttributeToProc := PSPascalCompilerOnApplyAttributeToProc;
  175. OnApplyAttributeToType := PSPascalCompilerOnApplyAttributeToType;
  176. end;
  177. end;
  178. Result := True;
  179. end else begin
  180. Sender.MakeError('', ecUnknownIdentifier, '');
  181. Result := False;
  182. end;
  183. end;
  184. function PSPascalCompilerOnExportCheck(Sender: TPSPascalCompiler; Proc: TPSInternalProcedure; const ProcDecl: tbtstring): Boolean;
  185. var
  186. ScriptCompiler: TScriptCompiler;
  187. ScriptExport: TScriptExport;
  188. I: Integer;
  189. begin
  190. ScriptCompiler := TScriptCompiler(Sender.ID);
  191. ScriptCompiler.FFunctionsFound.Add(String(Proc.Name));
  192. { Try and see if the function name matches an export and if so,
  193. see if one of the prototypes for that name matches. }
  194. I := ScriptCompiler.FindExport(String(Proc.Name), String(Procdecl), -1);
  195. if I <> -1 then begin
  196. { The function name is a match and the function prototype is ok. }
  197. ScriptExport := ScriptCompiler.FExports[I];
  198. ScriptExport.Exported := True;
  199. Result := True;
  200. end else if ScriptCompiler.FindExport(String(Proc.Name), '', -1) <> -1 then begin
  201. { The function name is a match but the function prototype is not. }
  202. with Sender.MakeError('', ecCustomError, tbtstring(Format('Invalid prototype for ''%s''', [Proc.OriginalName]))) do
  203. SetCustomPos(Proc.DeclarePos, Proc.DeclareRow, Proc.DeclareCol);
  204. Result := False;
  205. end else
  206. Result := True; { The function name is not a match - this is a user function. }
  207. end;
  208. function PSPascalCompilerOnBeforeOutput(Sender: TPSPascalCompiler): Boolean;
  209. var
  210. ScriptCompiler: TScriptCompiler;
  211. ScriptExport: TScriptExport;
  212. I: Integer;
  213. Decl: TPSParametersDecl;
  214. Msg: String;
  215. begin
  216. ScriptCompiler := Sender.ID;
  217. Result := True;
  218. { Try and see if required but non found exports match any built in function
  219. names and if so, see if the prototypes also match }
  220. for I := 0 to ScriptCompiler.FExports.Count-1 do begin
  221. ScriptExport := ScriptCompiler.FExports[I];
  222. if ScriptExport.Required and not ScriptExport.Exported then begin
  223. Decl := Sender.UseExternalProc(tbtstring(ScriptExport.Name));
  224. if Decl <> nil then begin
  225. if CompareText(ScriptExport.Decl, String(Sender.MakeDecl(Decl))) = 0 then
  226. ScriptExport.Exported := True
  227. else begin
  228. if Assigned(ScriptCompiler.FOnError) then begin
  229. Msg := Format('Function or procedure ''%s'' prototype is incompatible', [ScriptExport.Name]);
  230. ScriptCompiler.FOnError(Msg, ScriptExport.RequiredFilename, ScriptExport.RequiredLine);
  231. end;
  232. Result := False;
  233. end;
  234. end;
  235. end;
  236. end;
  237. end;
  238. function PSPascalCompilerOnWriteLine2(Sender: TPSPascalCompiler; Position: Cardinal; IsProcExit: Boolean): Boolean;
  239. var
  240. ScriptCompiler: TScriptCompiler;
  241. Filename: String;
  242. Line, Col: LongInt;
  243. begin
  244. ScriptCompiler := Sender.ID;
  245. if Assigned(ScriptCompiler.FOnUsedLine) then begin
  246. ScriptCompiler.PSPositionToLineCol(Position, Line, Col);
  247. if ScriptCompiler.FUsedLines.IndexOf(Pointer(Line)) = -1 then begin
  248. ScriptCompiler.FUsedLines.Add(Pointer(Line));
  249. Filename := '';
  250. if Assigned(ScriptCompiler.FOnLineToLineInfo) then
  251. ScriptCompiler.FOnLineToLineInfo(Line, Filename, Line);
  252. ScriptCompiler.FOnUsedLine(Filename, Line, Position, IsProcExit);
  253. Result := True;
  254. end else
  255. Result := False;
  256. end else
  257. Result := True;
  258. end;
  259. procedure PSPascalCompilerOnUseVariable(Sender: TPSPascalCompiler; VarType: TPSVariableType; VarNo: Longint; ProcNo, Position: Cardinal; const PropData: tbtstring);
  260. var
  261. ScriptCompiler: TScriptCompiler;
  262. Filename: String;
  263. Line, Col: LongInt;
  264. begin
  265. ScriptCompiler := Sender.ID;
  266. if Assigned(ScriptCompiler.FOnUsedVariable) then begin
  267. ScriptCompiler.PSPositionToLineCol(Position, Line, Col);
  268. Filename := '';
  269. if Assigned(ScriptCompiler.FOnLineToLineInfo) then
  270. ScriptCompiler.FOnLineToLineInfo(Line, Filename, Line);
  271. ScriptCompiler.FOnUsedVariable(Filename, Line, Col, LongInt(VarType), ProcNo, VarNo, PropData);
  272. end;
  273. end;
  274. procedure PSPascalCompilerOnUseRegProc(Sender: TPSPascalCompiler; Position: Cardinal; const Name: tbtstring);
  275. var
  276. ScriptCompiler: TScriptCompiler;
  277. WarningMessage: String;
  278. begin
  279. ScriptCompiler := Sender.ID;
  280. if Assigned(ScriptCompiler.FOnWarning) then begin
  281. WarningMessage := ScriptCompiler.IsObsoleteFunction(String(Name));
  282. if WarningMessage <> '' then
  283. ScriptCompiler.TriggerWarning(Position, 'Hint', WarningMessage);
  284. end;
  285. end;
  286. {---}
  287. constructor TScriptCompiler.Create;
  288. begin
  289. FObsoleteFunctionWarnings := TDictionary<String, String>.Create(TIStringComparer.Ordinal);
  290. FExports := TList.Create();
  291. FUsedLines := TList.Create();
  292. FFunctionsFound := TStringList.Create();
  293. end;
  294. destructor TScriptCompiler.Destroy;
  295. var
  296. I: Integer;
  297. begin
  298. FFunctionsFound.Free();
  299. FUsedLines.Free();
  300. for I := 0 to FExports.Count-1 do
  301. TScriptExport(FExports[I]).Free();
  302. FExports.Free();
  303. FObsoleteFunctionWarnings.Free();
  304. end;
  305. procedure TScriptCompiler.PSPositionToLineCol(Position: LongInt; var Line, Col: LongInt);
  306. function FindNewLine(const S: AnsiString; const Start: Integer): Integer;
  307. var
  308. I: Integer;
  309. begin
  310. for I := Start to Length(S) do
  311. if S[I] = #10 then begin
  312. Result := I - Start + 1;
  313. Exit;
  314. end;
  315. Result := 0;
  316. end;
  317. var
  318. LineStartPosition, LineLength: LongInt;
  319. begin
  320. Inc(Position);
  321. Line := 1;
  322. LineStartPosition := 1;
  323. LineLength := FindNewLine(FScriptText, LineStartPosition);
  324. while (LineLength <> 0) and (Position > LineLength) do begin
  325. Inc(Line);
  326. Inc(LineStartPosition, LineLength);
  327. Dec(Position, LineLength);
  328. LineLength := FindNewLine(FScriptText, LineStartPosition);
  329. end;
  330. {$IFDEF UNICODE}
  331. { Convert Position from the UTF8 encoded ANSI string index to a UTF-16 string index }
  332. Position := Length(UTF8ToString(Copy(FScriptText, LineStartPosition, Position - 1))) + 1;
  333. {$ENDIF}
  334. Col := Position;
  335. end;
  336. procedure TScriptCompiler.TriggerWarning(const Position: LongInt; const WarningType, WarningMessage: String);
  337. var
  338. Line, Col: LongInt;
  339. Filename, Msg: String;
  340. begin
  341. PSPositionToLineCol(Position, Line, Col);
  342. Filename := '';
  343. if Assigned(FOnLineToLineInfo) then
  344. FOnLineToLineInfo(Line, Filename, Line);
  345. Msg := '';
  346. if Filename <> '' then
  347. Msg := Msg + Filename + ', ';
  348. Msg := Msg + Format('Line %d, Column %d: [%s] %s', [Line, Col, WarningType, WarningMessage]);
  349. FOnWarning(Msg);
  350. end;
  351. procedure TScriptCompiler.AddExport(const Name, Decl: String; const AllowNamingAttribute, Required: Boolean; const RequiredFilename: String; const RequiredLine: LongInt);
  352. var
  353. ScriptExport: TScriptExport;
  354. I: Integer;
  355. begin
  356. I := FindExport(Name, Decl, -1);
  357. if I <> -1 then begin
  358. ScriptExport := FExports[I];
  359. if Required and not ScriptExport.Required then begin
  360. ScriptExport.Required := True;
  361. ScriptExport.RequiredFilename := RequiredFilename;
  362. ScriptExport.RequiredLine := RequiredLine;
  363. end;
  364. ScriptExport.AllowNamingAttribute := ScriptExport.AllowNamingAttribute and AllowNamingAttribute;
  365. Exit;
  366. end;
  367. ScriptExport := TScriptExport.Create();
  368. ScriptExport.Name := Name;
  369. ScriptExport.Decl := Decl;
  370. ScriptExport.AllowNamingAttribute := AllowNamingAttribute;
  371. ScriptExport.Required := Required;
  372. if Required then begin
  373. ScriptExport.RequiredFilename := RequiredFilename;
  374. ScriptExport.RequiredLine := RequiredLine;
  375. end;
  376. FExports.Add(ScriptExport);
  377. end;
  378. function TScriptCompiler.FindExport(const Name, Decl: String; const IgnoreIndex: Integer): Integer;
  379. var
  380. ScriptExport: TScriptExport;
  381. I: Integer;
  382. begin
  383. for I := 0 to FExports.Count-1 do begin
  384. ScriptExport := FExports[I];
  385. if ((Name = '') or (CompareText(ScriptExport.Name, Name) = 0)) and
  386. ((Decl = '') or (CompareText(ScriptExport.Decl, Decl) = 0)) and
  387. ((IgnoreIndex = -1) or (I <> IgnoreIndex)) then begin
  388. Result := I;
  389. Exit;
  390. end;
  391. end;
  392. Result := -1;
  393. end;
  394. function TScriptCompiler.CheckExports: Boolean;
  395. var
  396. ScriptExport: TScriptExport;
  397. I: Integer;
  398. Msg: String;
  399. begin
  400. Result := True;
  401. for I := 0 to FExports.Count-1 do begin
  402. ScriptExport := FExports[I];
  403. if ScriptExport.Required and not ScriptExport.Exported then begin
  404. if Assigned(FOnError) then begin
  405. { Either the function wasn't present or it was present but matched another export }
  406. if FindExport(ScriptExport.Name, '', I) <> -1 then
  407. Msg := Format('Required function or procedure ''%s'' found but not with a compatible prototype', [ScriptExport.Name])
  408. else
  409. Msg := Format('Required function or procedure ''%s'' not found', [ScriptExport.Name]);
  410. FOnError(Msg, ScriptExport.RequiredFilename, ScriptExport.RequiredLine);
  411. end;
  412. Result := False;
  413. Exit;
  414. end;
  415. end;
  416. end;
  417. function TScriptCompiler.Compile(const ScriptText: String; var CompiledScriptText, CompiledScriptDebugInfo: tbtString): Boolean;
  418. var
  419. PSPascalCompiler: TPSPascalCompiler;
  420. L, Line, Col: LongInt;
  421. Filename, Msg: String;
  422. I: Integer;
  423. begin
  424. Result := False;
  425. {$IFDEF UNICODE}
  426. FScriptText := UTF8Encode(ScriptText);
  427. {$ELSE}
  428. FScriptText := ScriptText;
  429. {$ENDIF}
  430. for I := 0 to FExports.Count-1 do
  431. TScriptExport(FExports[I]).Exported := False;
  432. FFunctionsFound.Clear;
  433. PSPascalCompiler := TPSPascalCompiler.Create();
  434. try
  435. PSPascalCompiler.ID := Self;
  436. PSPascalCompiler.AllowNoBegin := True;
  437. PSPascalCompiler.AllowNoEnd := True;
  438. PSPascalCompiler.BooleanShortCircuit := True;
  439. {$IFDEF UNICODE}
  440. PSPascalCompiler.AllowDuplicateRegister := False;
  441. PSPascalCompiler.UTF8Decode := True;
  442. {$ENDIF}
  443. PSPascalCompiler.AttributesOpenTokenID := CSTI_Less;
  444. PSPascalCompiler.AttributesCloseTokenID := CSTI_Greater;
  445. PSPascalCompiler.OnUses := PSPascalCompilerOnUses;
  446. PSPascalCompiler.OnExportCheck := PSPascalCompilerOnExportCheck;
  447. PSPascalCompiler.OnBeforeOutput := PSPascalCompilerOnBeforeOutput;
  448. DefaultCC := ClStdCall;
  449. FUsedLines.Clear();
  450. PSPascalCompiler.OnWriteLine2 := PSPascalCompilerOnWriteLine2;
  451. PSPascalCompiler.OnUseVariable := PSPascalCompilerOnUseVariable;
  452. PSPascalCompiler.OnUseRegProc := PSPascalCompilerOnUseRegProc;
  453. if not PSPascalCompiler.Compile(FScriptText) then begin
  454. if Assigned(FOnError) then begin
  455. for L := 0 to PSPascalCompiler.MsgCount-1 do begin
  456. if PSPascalCompiler.Msg[L] is TPSPascalCompilerError then begin
  457. PSPositionToLineCol(PSPascalCompiler.Msg[L].Pos, Line, Col);
  458. Filename := '';
  459. if Assigned(FOnLineToLineInfo) then
  460. FOnLineToLineInfo(Line, Filename, Line);
  461. Msg := Format('Column %d:'#13#10'%s', [Col, PSPascalCompiler.Msg[L].ShortMessageToString]);
  462. FOnError(Msg, Filename, Line);
  463. Break;
  464. end;
  465. end;
  466. end;
  467. Exit;
  468. end else begin
  469. if not CheckExports() then
  470. Exit;
  471. if not PSPascalCompiler.GetOutput(CompiledScriptText) then begin
  472. if Assigned(FOnError) then begin
  473. Msg := 'GetOutput failed';
  474. FOnError(Msg, '', 0);
  475. end;
  476. Exit;
  477. end;
  478. if not PSPascalCompiler.GetDebugOutput(CompiledScriptDebugInfo) then begin
  479. if Assigned(FOnError) then begin
  480. Msg := 'GetDebugOutput failed';
  481. FOnError(Msg, '', 0);
  482. end;
  483. Exit;
  484. end;
  485. if Assigned(FOnWarning) then
  486. for L := 0 to PSPascalCompiler.MsgCount-1 do
  487. TriggerWarning(PSPascalCompiler.Msg[L].Pos,
  488. String(PSPascalCompiler.Msg[L].ErrorType),
  489. String(PSPascalCompiler.Msg[L].ShortMessageToString));
  490. end;
  491. Result := True;
  492. finally
  493. PSPascalCompiler.Free();
  494. end;
  495. end;
  496. function TScriptCompiler.ExportFound(const Name: String): Boolean;
  497. var
  498. ScriptExport: TScriptExport;
  499. I: Integer;
  500. begin
  501. for I := 0 to FExports.Count-1 do begin
  502. ScriptExport := FExports[I];
  503. if CompareText(ScriptExport.Name, Name) = 0 then begin
  504. Result := ScriptExport.Exported;
  505. Exit;
  506. end;
  507. end;
  508. Result := False;
  509. end;
  510. function TScriptCompiler.FunctionFound(const Name: String): Boolean;
  511. var
  512. I: Integer;
  513. begin
  514. Result := False;
  515. for I := 0 to FFunctionsFound.Count-1 do begin
  516. if CompareText(FFunctionsFound[I], Name) = 0 then begin
  517. Result := True;
  518. Break;
  519. end;
  520. end;
  521. end;
  522. function TScriptCompiler.GetExportCount: Integer;
  523. begin
  524. Result := FExports.Count;
  525. end;
  526. function TScriptCompiler.IsObsoleteFunction(const Name: String): String;
  527. begin
  528. FObsoleteFunctionWarnings.TryGetValue(Name, Result);
  529. end;
  530. end.