123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- unit Compiler.ScriptCompiler;
- {
- Inno Setup
- Copyright (C) 1997-2024 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Script (=[Code]) compiler
- }
- interface
- uses
- Classes, Generics.Collections, uPSUtils;
- type
- TScriptCompilerOnLineToLineInfo = procedure(const Line: LongInt; var Filename: String; var FileLine: LongInt) of object;
- TScriptCompilerOnUsedLine = procedure(const Filename: String; const Line, Position: LongInt; const IsProcExit: Boolean) of object;
- TScriptCompilerOnUsedVariable = procedure(const Filename: String; const Line, Col, Param1, Param2, Param3: LongInt; const Param4: AnsiString) of object;
- TScriptCompilerOnError = procedure(const Msg: String; const ErrorFilename: String; const ErrorLine: LongInt) of object;
- TScriptCompilerOnWarning = procedure(const Msg: String) of object;
- TScriptCompiler = class
- private
- FNamingAttribute: String;
- FObsoleteFunctionWarnings: TDictionary<String, String>;
- FExports, FUsedLines: TList;
- FFunctionsFound: TStringList;
- FScriptText: AnsiString;
- FOnLineToLineInfo: TScriptCompilerOnLineToLineInfo;
- FOnUsedLine: TScriptCompilerOnUsedLine;
- FOnUsedVariable: TScriptCompilerOnUsedVariable;
- FOnError: TScriptCompilerOnError;
- FOnWarning: TScriptCompilerOnWarning;
- function FindExport(const Name, Decl: String; const IgnoreIndex: Integer): Integer;
- function GetExportCount: Integer;
- procedure PSPositionToLineCol(Position: LongInt; var Line, Col: LongInt);
- procedure TriggerWarning(const Position: LongInt; const WarningType, WarningMessage: String);
- public
- constructor Create;
- destructor Destroy; override;
- procedure AddExport(const Name, Decl: String; const AllowNamingAttribute, Required: Boolean; const RequiredFilename: String; const RequiredLine: LongInt);
- function CheckExports: Boolean;
- function Compile(const ScriptText: String; var CompiledScriptText, CompiledScriptDebugInfo: tbtString): Boolean;
- property ExportCount: Integer read GetExportCount;
- function ExportFound(const Name: String): Boolean;
- function FunctionFound(const Name: String): Boolean;
- function IsObsoleteFunction(const Name: String): String;
- property NamingAttribute: String write FNamingAttribute;
- property OnLineToLineInfo: TScriptCompilerOnLineToLineInfo write FOnLineToLineInfo;
- property OnUsedLine: TScriptCompilerOnUsedLine write FOnUsedLine;
- property OnUsedVariable: TScriptCompilerOnUsedVariable write FOnUsedVariable;
- property OnError: TScriptCompilerOnError write FOnError;
- property OnWarning: TScriptCompilerOnWarning write FOnWarning;
- end;
- implementation
- uses
- SysUtils, Generics.Defaults,
- uPSCompiler, uPSC_dll,
- Compiler.ScriptClasses, Compiler.ScriptFunc;
- type
- TScriptExport = class
- Name, Decl: String;
- AllowNamingAttribute: Boolean;
- Required: Boolean;
- RequiredFilename: String;
- RequiredLine: LongInt;
- Exported: Boolean;
- end;
- {---}
- function PSPascalCompilerOnExternalProc(Sender: TPSPascalCompiler; Decl: TPSParametersDecl; const Name, FExternal: tbtstring): TPSRegProc;
- var
- S: String;
- P: Integer;
- begin
- S := String(FExternal) + ' ';
- P := Pos(' setuponly ', S);
- if P > 0 then begin
- Delete(S, P+1, Length('setuponly '));
- Insert('setup:', S, Pos('@', S)+1);
- end
- else begin
- P := Pos(' uninstallonly ', S);
- if P > 0 then begin
- Delete(S, P+1, Length('uninstallonly '));
- Insert('uninstall:', S, Pos('@', S)+1);
- end;
- end;
- if Pos('@uninstall:files:', S) <> 0 then begin
- Sender.MakeError('', ecCustomError, '"uninstallonly" cannot be used with "files:"');
- Result := nil;
- Exit;
- end;
- Result := DllExternalProc(Sender, Decl, Name, tbtstring(TrimRight(S)));
- end;
- function PSPascalCompilerOnApplyAttributeToProc(Sender: TPSPascalCompiler; aProc: TPSProcedure; Attr: TPSAttribute): Boolean;
- var
- ScriptCompiler: TScriptCompiler;
- AttrValue: String;
- ScriptExport: TScriptExport;
- B: Boolean;
- I: Integer;
- begin
- ScriptCompiler := TScriptCompiler(Sender.ID);
- if CompareText(String(Attr.AType.Name), ScriptCompiler.FNamingAttribute) = 0 then begin
- if aProc.ClassType <> TPSInternalProcedure then begin
- with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute cannot be used on external function or procedure', [ScriptCompiler.FNamingAttribute]))) do
- SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
- Result := False;
- end else if Attr.Count <> 1 then begin
- with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value not found', [ScriptCompiler.FNamingAttribute]))) do
- SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
- Result := False;
- end else begin
- if ScriptCompiler.FindExport(String(TPSInternalProcedure(aProc).Name), '', -1) <> -1 then begin
- { Don't allow attributes on functions already matching an export (by their name) so that we don't have to deal with this later. }
- with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute not allowed for function or procedure "%s"', [ScriptCompiler.FNamingAttribute, String(TPSInternalProcedure(aProc).Name)]))) do
- SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
- Result := False;
- end else begin
- AttrValue := String(GetString(Attr.Values[0], B));
- I := ScriptCompiler.FindExport(AttrValue, String(Sender.MakeDecl(TPSInternalProcedure(aProc).Decl)), -1);
- if I <> -1 then begin
- { The name from the attribute and the function prototype are both ok. }
- ScriptExport := ScriptCompiler.FExports[I];
- if not ScriptExport.AllowNamingAttribute then begin
- with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value "%s" not allowed', [ScriptCompiler.FNamingAttribute, AttrValue]))) do
- SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
- Result := False;
- end else begin
- ScriptExport.Exported := True;
- Result := True;
- end;
- end else if ScriptCompiler.FindExport(AttrValue, '', -1) <> -1 then begin
- { The name from the attribute is ok but the function prototype is not. }
- with Sender.MakeError('', ecCustomError, tbtstring(Format('Invalid function or procedure prototype for attribute value "%s"', [AttrValue]))) do
- SetCustomPos(TPSInternalProcedure(aProc).DeclarePos, TPSInternalProcedure(aProc).DeclareRow, TPSInternalProcedure(aProc).DeclareCol);
- Result := False;
- end else begin
- { The name from the attribute is not ok. }
- with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value "%s" invalid', [ScriptCompiler.FNamingAttribute, AttrValue]))) do
- SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
- Result := False;
- end;
- end;
- end;
- end else
- Result := True;
- end;
- function PSPascalCompilerOnApplyAttributeToType(Sender: TPSPascalCompiler; aType: TPSType; Attr: TPSAttribute): Boolean;
- var
- NamingAttribute: String;
- begin
- NamingAttribute := TScriptCompiler(Sender.ID).FNamingAttribute;
- if CompareText(String(Attr.AType.Name), NamingAttribute) = 0 then begin
- with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute cannot be used on types', [NamingAttribute]))) do
- SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
- Result := False;
- end else
- Result := True;
- end;
- function PSPascalCompilerOnUses(Sender: TPSPascalCompiler; const Name: tbtstring): Boolean;
- var
- NamingAttribute: String;
- begin
- if Name = 'SYSTEM' then begin
- RegisterDll_Compiletime(Sender);
- Sender.OnExternalProc := PSPascalCompilerOnExternalProc;
- ScriptClassesLibraryRegister_C(Sender);
- ScriptFuncLibraryRegister_C(Sender, TScriptCompiler(Sender.ID).FObsoleteFunctionWarnings);
- NamingAttribute := TScriptCompiler(Sender.ID).FNamingAttribute;
- if NamingAttribute <> '' then begin
- with Sender.AddAttributeType do begin
- OrgName := tbtstring(NamingAttribute);
- with AddField do begin
- FieldOrgName := 'Name';
- FieldType := Sender.FindType('String');
- end;
- OnApplyAttributeToProc := PSPascalCompilerOnApplyAttributeToProc;
- OnApplyAttributeToType := PSPascalCompilerOnApplyAttributeToType;
- end;
- end;
- Result := True;
- end else begin
- Sender.MakeError('', ecUnknownIdentifier, '');
- Result := False;
- end;
- end;
- function PSPascalCompilerOnExportCheck(Sender: TPSPascalCompiler; Proc: TPSInternalProcedure; const ProcDecl: tbtstring): Boolean;
- var
- ScriptCompiler: TScriptCompiler;
- ScriptExport: TScriptExport;
- I: Integer;
- begin
- ScriptCompiler := TScriptCompiler(Sender.ID);
- ScriptCompiler.FFunctionsFound.Add(String(Proc.Name));
- { Try and see if the function name matches an export and if so,
- see if one of the prototypes for that name matches. }
- I := ScriptCompiler.FindExport(String(Proc.Name), String(Procdecl), -1);
- if I <> -1 then begin
- { The function name is a match and the function prototype is ok. }
- ScriptExport := ScriptCompiler.FExports[I];
- ScriptExport.Exported := True;
- Result := True;
- end else if ScriptCompiler.FindExport(String(Proc.Name), '', -1) <> -1 then begin
- { The function name is a match but the function prototype is not. }
- with Sender.MakeError('', ecCustomError, tbtstring(Format('Invalid prototype for ''%s''', [Proc.OriginalName]))) do
- SetCustomPos(Proc.DeclarePos, Proc.DeclareRow, Proc.DeclareCol);
- Result := False;
- end else
- Result := True; { The function name is not a match - this is a user function. }
- end;
- function PSPascalCompilerOnBeforeOutput(Sender: TPSPascalCompiler): Boolean;
- var
- ScriptCompiler: TScriptCompiler;
- ScriptExport: TScriptExport;
- I: Integer;
- Decl: TPSParametersDecl;
- Msg: String;
- begin
- ScriptCompiler := Sender.ID;
- Result := True;
- { Try and see if required but non found exports match any built in function
- names and if so, see if the prototypes also match }
- for I := 0 to ScriptCompiler.FExports.Count-1 do begin
- ScriptExport := ScriptCompiler.FExports[I];
- if ScriptExport.Required and not ScriptExport.Exported then begin
- Decl := Sender.UseExternalProc(tbtstring(ScriptExport.Name));
- if Decl <> nil then begin
- if CompareText(ScriptExport.Decl, String(Sender.MakeDecl(Decl))) = 0 then
- ScriptExport.Exported := True
- else begin
- if Assigned(ScriptCompiler.FOnError) then begin
- Msg := Format('Function or procedure ''%s'' prototype is incompatible', [ScriptExport.Name]);
- ScriptCompiler.FOnError(Msg, ScriptExport.RequiredFilename, ScriptExport.RequiredLine);
- end;
- Result := False;
- end;
- end;
- end;
- end;
- end;
- function PSPascalCompilerOnWriteLine2(Sender: TPSPascalCompiler; Position: Cardinal; IsProcExit: Boolean): Boolean;
- var
- ScriptCompiler: TScriptCompiler;
- Filename: String;
- Line, Col: LongInt;
- begin
- ScriptCompiler := Sender.ID;
- if Assigned(ScriptCompiler.FOnUsedLine) then begin
- ScriptCompiler.PSPositionToLineCol(Position, Line, Col);
- if ScriptCompiler.FUsedLines.IndexOf(Pointer(Line)) = -1 then begin
- ScriptCompiler.FUsedLines.Add(Pointer(Line));
- Filename := '';
- if Assigned(ScriptCompiler.FOnLineToLineInfo) then
- ScriptCompiler.FOnLineToLineInfo(Line, Filename, Line);
- ScriptCompiler.FOnUsedLine(Filename, Line, Position, IsProcExit);
- Result := True;
- end else
- Result := False;
- end else
- Result := True;
- end;
- procedure PSPascalCompilerOnUseVariable(Sender: TPSPascalCompiler; VarType: TPSVariableType; VarNo: Longint; ProcNo, Position: Cardinal; const PropData: tbtstring);
- var
- ScriptCompiler: TScriptCompiler;
- Filename: String;
- Line, Col: LongInt;
- begin
- ScriptCompiler := Sender.ID;
- if Assigned(ScriptCompiler.FOnUsedVariable) then begin
- ScriptCompiler.PSPositionToLineCol(Position, Line, Col);
- Filename := '';
- if Assigned(ScriptCompiler.FOnLineToLineInfo) then
- ScriptCompiler.FOnLineToLineInfo(Line, Filename, Line);
- ScriptCompiler.FOnUsedVariable(Filename, Line, Col, LongInt(VarType), ProcNo, VarNo, PropData);
- end;
- end;
- procedure PSPascalCompilerOnUseRegProc(Sender: TPSPascalCompiler; Position: Cardinal; const Name: tbtstring);
- var
- ScriptCompiler: TScriptCompiler;
- WarningMessage: String;
- begin
- ScriptCompiler := Sender.ID;
- if Assigned(ScriptCompiler.FOnWarning) then begin
- WarningMessage := ScriptCompiler.IsObsoleteFunction(String(Name));
- if WarningMessage <> '' then
- ScriptCompiler.TriggerWarning(Position, 'Hint', WarningMessage);
- end;
- end;
- {---}
- constructor TScriptCompiler.Create;
- begin
- FObsoleteFunctionWarnings := TDictionary<String, String>.Create(TIStringComparer.Ordinal);
- FExports := TList.Create();
- FUsedLines := TList.Create();
- FFunctionsFound := TStringList.Create();
- end;
- destructor TScriptCompiler.Destroy;
- var
- I: Integer;
- begin
- FFunctionsFound.Free();
- FUsedLines.Free();
- for I := 0 to FExports.Count-1 do
- TScriptExport(FExports[I]).Free();
- FExports.Free();
- FObsoleteFunctionWarnings.Free();
- end;
- procedure TScriptCompiler.PSPositionToLineCol(Position: LongInt; var Line, Col: LongInt);
- function FindNewLine(const S: AnsiString; const Start: Integer): Integer;
- var
- I: Integer;
- begin
- for I := Start to Length(S) do
- if S[I] = #10 then begin
- Result := I - Start + 1;
- Exit;
- end;
- Result := 0;
- end;
- var
- LineStartPosition, LineLength: LongInt;
- begin
- Inc(Position);
- Line := 1;
- LineStartPosition := 1;
- LineLength := FindNewLine(FScriptText, LineStartPosition);
- while (LineLength <> 0) and (Position > LineLength) do begin
- Inc(Line);
- Inc(LineStartPosition, LineLength);
- Dec(Position, LineLength);
- LineLength := FindNewLine(FScriptText, LineStartPosition);
- end;
- { Convert Position from the UTF8 encoded ANSI string index to a UTF-16 string index }
- Position := Length(UTF8ToString(Copy(FScriptText, LineStartPosition, Position - 1))) + 1;
- Col := Position;
- end;
- procedure TScriptCompiler.TriggerWarning(const Position: LongInt; const WarningType, WarningMessage: String);
- var
- Line, Col: LongInt;
- Filename, Msg: String;
- begin
- PSPositionToLineCol(Position, Line, Col);
- Filename := '';
- if Assigned(FOnLineToLineInfo) then
- FOnLineToLineInfo(Line, Filename, Line);
- Msg := '';
- if Filename <> '' then
- Msg := Msg + Filename + ', ';
- Msg := Msg + Format('Line %d, Column %d: [%s] %s', [Line, Col, WarningType, WarningMessage]);
- FOnWarning(Msg);
- end;
- procedure TScriptCompiler.AddExport(const Name, Decl: String; const AllowNamingAttribute, Required: Boolean; const RequiredFilename: String; const RequiredLine: LongInt);
- var
- ScriptExport: TScriptExport;
- I: Integer;
- begin
- I := FindExport(Name, Decl, -1);
- if I <> -1 then begin
- ScriptExport := FExports[I];
- if Required and not ScriptExport.Required then begin
- ScriptExport.Required := True;
- ScriptExport.RequiredFilename := RequiredFilename;
- ScriptExport.RequiredLine := RequiredLine;
- end;
- ScriptExport.AllowNamingAttribute := ScriptExport.AllowNamingAttribute and AllowNamingAttribute;
- Exit;
- end;
- ScriptExport := TScriptExport.Create();
- ScriptExport.Name := Name;
- ScriptExport.Decl := Decl;
- ScriptExport.AllowNamingAttribute := AllowNamingAttribute;
- ScriptExport.Required := Required;
- if Required then begin
- ScriptExport.RequiredFilename := RequiredFilename;
- ScriptExport.RequiredLine := RequiredLine;
- end;
- FExports.Add(ScriptExport);
- end;
- function TScriptCompiler.FindExport(const Name, Decl: String; const IgnoreIndex: Integer): Integer;
- var
- ScriptExport: TScriptExport;
- I: Integer;
- begin
- for I := 0 to FExports.Count-1 do begin
- ScriptExport := FExports[I];
- if ((Name = '') or (CompareText(ScriptExport.Name, Name) = 0)) and
- ((Decl = '') or (CompareText(ScriptExport.Decl, Decl) = 0)) and
- ((IgnoreIndex = -1) or (I <> IgnoreIndex)) then begin
- Result := I;
- Exit;
- end;
- end;
- Result := -1;
- end;
- function TScriptCompiler.CheckExports: Boolean;
- var
- ScriptExport: TScriptExport;
- I: Integer;
- Msg: String;
- begin
- Result := True;
- for I := 0 to FExports.Count-1 do begin
- ScriptExport := FExports[I];
- if ScriptExport.Required and not ScriptExport.Exported then begin
- if Assigned(FOnError) then begin
- { Either the function wasn't present or it was present but matched another export }
- if FindExport(ScriptExport.Name, '', I) <> -1 then
- Msg := Format('Required function or procedure ''%s'' found but not with a compatible prototype', [ScriptExport.Name])
- else
- Msg := Format('Required function or procedure ''%s'' not found', [ScriptExport.Name]);
- FOnError(Msg, ScriptExport.RequiredFilename, ScriptExport.RequiredLine);
- end;
- Result := False;
- Exit;
- end;
- end;
- end;
- function TScriptCompiler.Compile(const ScriptText: String; var CompiledScriptText, CompiledScriptDebugInfo: tbtString): Boolean;
- var
- PSPascalCompiler: TPSPascalCompiler;
- L, Line, Col: LongInt;
- Filename, Msg: String;
- I: Integer;
- begin
- Result := False;
- FScriptText := Utf8Encode(ScriptText);
- for I := 0 to FExports.Count-1 do
- TScriptExport(FExports[I]).Exported := False;
- FFunctionsFound.Clear;
- PSPascalCompiler := TPSPascalCompiler.Create();
- try
- PSPascalCompiler.ID := Self;
- PSPascalCompiler.AllowNoBegin := True;
- PSPascalCompiler.AllowNoEnd := True;
- PSPascalCompiler.BooleanShortCircuit := True;
- PSPascalCompiler.AllowDuplicateRegister := False;
- PSPascalCompiler.UTF8Decode := True;
- PSPascalCompiler.AttributesOpenTokenID := CSTI_Less;
- PSPascalCompiler.AttributesCloseTokenID := CSTI_Greater;
- PSPascalCompiler.OnUses := PSPascalCompilerOnUses;
- PSPascalCompiler.OnExportCheck := PSPascalCompilerOnExportCheck;
- PSPascalCompiler.OnBeforeOutput := PSPascalCompilerOnBeforeOutput;
- DefaultCC := ClStdCall;
- FUsedLines.Clear();
- PSPascalCompiler.OnWriteLine2 := PSPascalCompilerOnWriteLine2;
- PSPascalCompiler.OnUseVariable := PSPascalCompilerOnUseVariable;
- PSPascalCompiler.OnUseRegProc := PSPascalCompilerOnUseRegProc;
- if not PSPascalCompiler.Compile(FScriptText) then begin
- if Assigned(FOnError) then begin
- for L := 0 to PSPascalCompiler.MsgCount-1 do begin
- if PSPascalCompiler.Msg[L] is TPSPascalCompilerError then begin
- PSPositionToLineCol(PSPascalCompiler.Msg[L].Pos, Line, Col);
- Filename := '';
- if Assigned(FOnLineToLineInfo) then
- FOnLineToLineInfo(Line, Filename, Line);
- Msg := Format('Column %d:'#13#10'%s', [Col, PSPascalCompiler.Msg[L].ShortMessageToString]);
- FOnError(Msg, Filename, Line);
- Break;
- end;
- end;
- end;
- Exit;
- end else begin
- if not CheckExports() then
- Exit;
- if not PSPascalCompiler.GetOutput(CompiledScriptText) then begin
- if Assigned(FOnError) then begin
- Msg := 'GetOutput failed';
- FOnError(Msg, '', 0);
- end;
- Exit;
- end;
- if not PSPascalCompiler.GetDebugOutput(CompiledScriptDebugInfo) then begin
- if Assigned(FOnError) then begin
- Msg := 'GetDebugOutput failed';
- FOnError(Msg, '', 0);
- end;
- Exit;
- end;
- if Assigned(FOnWarning) then
- for L := 0 to PSPascalCompiler.MsgCount-1 do
- TriggerWarning(PSPascalCompiler.Msg[L].Pos,
- String(PSPascalCompiler.Msg[L].ErrorType),
- String(PSPascalCompiler.Msg[L].ShortMessageToString));
- end;
- Result := True;
- finally
- PSPascalCompiler.Free();
- end;
- end;
- function TScriptCompiler.ExportFound(const Name: String): Boolean;
- var
- ScriptExport: TScriptExport;
- I: Integer;
- begin
- for I := 0 to FExports.Count-1 do begin
- ScriptExport := FExports[I];
- if CompareText(ScriptExport.Name, Name) = 0 then begin
- Result := ScriptExport.Exported;
- Exit;
- end;
- end;
- Result := False;
- end;
- function TScriptCompiler.FunctionFound(const Name: String): Boolean;
- var
- I: Integer;
- begin
- Result := False;
- for I := 0 to FFunctionsFound.Count-1 do begin
- if CompareText(FFunctionsFound[I], Name) = 0 then begin
- Result := True;
- Break;
- end;
- end;
- end;
- function TScriptCompiler.GetExportCount: Integer;
- begin
- Result := FExports.Count;
- end;
- function TScriptCompiler.IsObsoleteFunction(const Name: String): String;
- begin
- FObsoleteFunctionWarnings.TryGetValue(Name, Result);
- end;
- end.
|