|
@@ -25,9 +25,17 @@ unit Pas2jsFileCache;
|
|
|
interface
|
|
|
|
|
|
uses
|
|
|
- Classes, SysUtils, AVL_Tree,
|
|
|
- PScanner, PasResolver, FPPJsSrcMap,
|
|
|
- Pas2jsLogger, Pas2jsFileUtils, Pas2JsFiler;
|
|
|
+ {$IFDEF Pas2js}
|
|
|
+ {$IFDEF NodeJS}
|
|
|
+ NodeJSFS,
|
|
|
+ {$ENDIF}
|
|
|
+ {$ENDIF}
|
|
|
+ Classes, SysUtils,
|
|
|
+ PScanner, PasUseAnalyzer, PasResolver, FPPJsSrcMap,
|
|
|
+ {$IFDEF HasPas2jsFiler}
|
|
|
+ Pas2JsFiler,
|
|
|
+ {$ENDIF}
|
|
|
+ Pas2jsLogger, Pas2jsFileUtils;
|
|
|
|
|
|
const // Messages
|
|
|
nIncludeSearch = 201; sIncludeSearch = 'Include file search: %s';
|
|
@@ -43,7 +51,7 @@ type
|
|
|
type
|
|
|
TPas2jsFileAgeTime = longint;
|
|
|
TPas2jsFileAttr = longint;
|
|
|
- TPas2jsFileSize = int64;
|
|
|
+ TPas2jsFileSize = TMaxPrecInt;
|
|
|
TPas2jsSearchFileCase = (
|
|
|
sfcDefault,
|
|
|
sfcCaseSensitive,
|
|
@@ -115,7 +123,7 @@ type
|
|
|
TPas2jsCachedDirectories = class
|
|
|
private
|
|
|
FChangeStamp: TChangeStamp;
|
|
|
- FDirectories: TAVLTree;// tree of TPas2jsCachedDirectory sorted by Directory
|
|
|
+ FDirectories: TPasAnalyzerKeySet;// set of TPas2jsCachedDirectory, key is Directory
|
|
|
FWorkingDirectory: string;
|
|
|
private
|
|
|
FOnReadDirectory: TReadDirectoryEvent;
|
|
@@ -202,7 +210,7 @@ type
|
|
|
FIsEOF: boolean;
|
|
|
FLineNumber: integer;
|
|
|
FSource: string;
|
|
|
- FSrcPos: PChar;
|
|
|
+ FSrcPos: integer;
|
|
|
public
|
|
|
constructor Create(const AFilename: string); override;
|
|
|
constructor Create(aFile: TPas2jsCachedFile); reintroduce;
|
|
@@ -211,7 +219,7 @@ type
|
|
|
property LineNumber: integer read FLineNumber;
|
|
|
property CachedFile: TPas2jsCachedFile read FCachedFile;
|
|
|
property Source: string read FSource;
|
|
|
- property SrcPos: PChar read FSrcPos;
|
|
|
+ property SrcPos: integer read FSrcPos;
|
|
|
end;
|
|
|
|
|
|
{ TPas2jsCachedFile }
|
|
@@ -263,7 +271,7 @@ type
|
|
|
FBaseDirectory: string;
|
|
|
FDefaultOutputPath: string;
|
|
|
FDirectoryCache: TPas2jsCachedDirectories;
|
|
|
- FFiles: TAVLTree; // tree of TPas2jsCachedFile sorted for Filename
|
|
|
+ FFiles: TPasAnalyzerKeySet; // set of TPas2jsCachedFile, key is Filename
|
|
|
FForeignUnitPaths: TStringList;
|
|
|
FForeignUnitPathsFromCmdLine: integer;
|
|
|
FIncludePaths: TStringList;
|
|
@@ -279,7 +287,9 @@ type
|
|
|
FOnReadFile: TPas2jsReadFileEvent;
|
|
|
FOnWriteFile: TPas2jsWriteFileEvent;
|
|
|
FOptions: TP2jsFileCacheOptions;
|
|
|
+ {$IFDEF HasPas2jsFiler}
|
|
|
FPrecompileFormat: TPas2JSPrecompileFormat;
|
|
|
+ {$ENDIF}
|
|
|
FReadLineCounter: SizeInt;
|
|
|
FResetStamp: TChangeStamp;
|
|
|
FSrcMapBaseDir: string;
|
|
@@ -329,7 +339,9 @@ type
|
|
|
procedure GetListing(const aDirectory: string; var Files: TStrings;
|
|
|
FullPaths: boolean = true);
|
|
|
procedure RaiseDuplicateFile(aFilename: string);
|
|
|
+ {$IFDEF HasStreams}
|
|
|
procedure SaveToFile(ms: TMemoryStream; Filename: string);
|
|
|
+ {$ENDIF}
|
|
|
function ExpandDirectory(const Filename, BaseDir: string): string;
|
|
|
public
|
|
|
property AllJSIntoMainJS: Boolean read GetAllJSIntoMainJS write SetAllJSIntoMainJS;
|
|
@@ -348,7 +360,9 @@ type
|
|
|
property Namespaces: TStringList read FNamespaces;
|
|
|
property NamespacesFromCmdLine: integer read FNamespacesFromCmdLine;
|
|
|
property Options: TP2jsFileCacheOptions read FOptions write SetOptions default DefaultPas2jsFileCacheOptions;
|
|
|
+ {$IFDEF HasPas2jsFiler}
|
|
|
property PrecompileFormat: TPas2JSPrecompileFormat read FPrecompileFormat write FPrecompileFormat;
|
|
|
+ {$ENDIF}
|
|
|
property ReadLineCounter: SizeInt read FReadLineCounter write FReadLineCounter;
|
|
|
property ResetStamp: TChangeStamp read FResetStamp;
|
|
|
property SearchLikeFPC: boolean read GetSearchLikeFPC write SetSearchLikeFPC;
|
|
@@ -362,21 +376,59 @@ type
|
|
|
property OnWriteFile: TPas2jsWriteFileEvent read FOnWriteFile write FOnWriteFile;
|
|
|
end;
|
|
|
|
|
|
+{$IFDEF Pas2js}
|
|
|
+function PtrStrToStr(StrAsPtr: Pointer): string;
|
|
|
+function PtrFilenameToKeyName(FilenameAsPtr: Pointer): string;
|
|
|
+function Pas2jsCachedFileToKeyName(Item: Pointer): string;
|
|
|
+function Pas2jsCacheDirToKeyName(Item: Pointer): string;
|
|
|
+{$ELSE}
|
|
|
function CompareFilenameWithCachedFile(Filename, CachedFile: Pointer): integer;
|
|
|
function CompareCachedFiles(File1, File2: Pointer): integer;
|
|
|
function ComparePas2jsCacheDirectories(Dir1, Dir2: Pointer): integer;
|
|
|
function CompareAnsiStringWithDirectoryCache(Path, DirCache: Pointer): integer;
|
|
|
-function ComparePas2jsDirectoryEntries(Entry1, Entry2: Pointer): integer;
|
|
|
+{$ENDIF}
|
|
|
+function ComparePas2jsDirectoryEntries(Entry1, Entry2: {$IFDEF Pas2js}jsvalue{$ELSE}Pointer{$ENDIF}): integer;
|
|
|
function CompareFirstCaseInsThenSensitive(const s, h: string): integer;
|
|
|
|
|
|
+{$IFDEF FPC_HAS_CPSTRING}
|
|
|
// UTF-8 helper functions
|
|
|
function ConvertTextToUTF8(const Src: string; var SrcEncoding: string): string;
|
|
|
function GuessEncoding(const Src: string): string;
|
|
|
function HasUTF8BOM(const s: string): boolean;
|
|
|
function RemoveUTFBOM(const s: string): string;
|
|
|
+{$ENDIF}
|
|
|
|
|
|
implementation
|
|
|
|
|
|
+{$IFDEF pas2js}
|
|
|
+function PtrStrToStr(StrAsPtr: Pointer): string;
|
|
|
+var
|
|
|
+ S: String absolute StrAsPtr;
|
|
|
+begin
|
|
|
+ Result:=S;
|
|
|
+end;
|
|
|
+
|
|
|
+function PtrFilenameToKeyName(FilenameAsPtr: Pointer): string;
|
|
|
+var
|
|
|
+ Filename: String absolute FilenameAsPtr;
|
|
|
+begin
|
|
|
+ Result:=FilenameToKey(Filename);
|
|
|
+end;
|
|
|
+
|
|
|
+function Pas2jsCachedFileToKeyName(Item: Pointer): string;
|
|
|
+var
|
|
|
+ aFile: TPas2jsCachedFile absolute Item;
|
|
|
+begin
|
|
|
+ Result:=FilenameToKey(aFile.Filename);
|
|
|
+end;
|
|
|
+
|
|
|
+function Pas2jsCacheDirToKeyName(Item: Pointer): string;
|
|
|
+var
|
|
|
+ Dir: TPas2jsCachedDirectory absolute Item;
|
|
|
+begin
|
|
|
+ Result:=FilenameToKey(Dir.Path);
|
|
|
+end;
|
|
|
+{$ELSE}
|
|
|
function CompareFilenameWithCachedFile(Filename, CachedFile: Pointer): integer;
|
|
|
var
|
|
|
Cache: TPas2jsCachedFile absolute CachedFile;
|
|
@@ -406,8 +458,9 @@ var
|
|
|
begin
|
|
|
Result:=CompareFilenames(AnsiString(Path),Directory.Path);
|
|
|
end;
|
|
|
+{$ENDIF}
|
|
|
|
|
|
-function ComparePas2jsDirectoryEntries(Entry1, Entry2: Pointer): integer;
|
|
|
+function ComparePas2jsDirectoryEntries(Entry1, Entry2: {$IFDEF Pas2js}jsvalue{$ELSE}Pointer{$ENDIF}): integer;
|
|
|
var
|
|
|
E1: TPas2jsCachedDirectoryEntry absolute Entry1;
|
|
|
E2: TPas2jsCachedDirectoryEntry absolute Entry2;
|
|
@@ -422,6 +475,7 @@ begin
|
|
|
Result:=CompareStr(s,h);
|
|
|
end;
|
|
|
|
|
|
+{$IFDEF FPC_HAS_CPSTRING}
|
|
|
function ConvertTextToUTF8(const Src: string; var SrcEncoding: string): string;
|
|
|
var
|
|
|
p: PChar;
|
|
@@ -512,6 +566,7 @@ begin
|
|
|
if not HasUTF8BOM(Result) then exit;
|
|
|
Delete(Result,1,3);
|
|
|
end;
|
|
|
+{$ENDIF}
|
|
|
|
|
|
{ TPas2jsCachedDirectory }
|
|
|
|
|
@@ -548,12 +603,18 @@ begin
|
|
|
end;
|
|
|
|
|
|
procedure TPas2jsCachedDirectory.DoReadDir;
|
|
|
+{$IFDEF Pas2js}
|
|
|
+{$ELSE}
|
|
|
var
|
|
|
Info: TUnicodeSearchRec;
|
|
|
+{$ENDIF}
|
|
|
begin
|
|
|
if Assigned(Pool.OnReadDirectory) then
|
|
|
if Pool.OnReadDirectory(Self) then exit;
|
|
|
|
|
|
+ {$IFDEF Pas2js}
|
|
|
+ raise Exception.Create('TPas2jsCachedDirectory.DoReadDir TODO');
|
|
|
+ {$ELSE}
|
|
|
// Note: do not add a 'if not DirectoryExists then exit'.
|
|
|
// This will not work on automounted directories. You must use FindFirst.
|
|
|
if FindFirst(UnicodeString(Path+AllFilesMask),faAnyFile,Info)=0 then
|
|
@@ -568,6 +629,7 @@ begin
|
|
|
until FindNext(Info)<>0;
|
|
|
end;
|
|
|
FindClose(Info);
|
|
|
+ {$ENDIF}
|
|
|
end;
|
|
|
|
|
|
constructor TPas2jsCachedDirectory.Create(aPath: string;
|
|
@@ -596,7 +658,7 @@ var
|
|
|
i: Integer;
|
|
|
begin
|
|
|
for i:=0 to FEntries.Count-1 do
|
|
|
- TObject(FEntries[i]).Free;
|
|
|
+ TObject(FEntries[i]).{$IFDEF Pas2js}Destroy{$ELSE}Free{$ENDIF};
|
|
|
FEntries.Clear;
|
|
|
FSorted:=true;
|
|
|
end;
|
|
@@ -881,7 +943,12 @@ end;
|
|
|
constructor TPas2jsCachedDirectories.Create;
|
|
|
begin
|
|
|
IncreaseChangeStamp(FChangeStamp);
|
|
|
- FDirectories:=TAVLTree.Create(@ComparePas2jsCacheDirectories);
|
|
|
+ FDirectories:=TPasAnalyzerKeySet.Create(
|
|
|
+ {$IFDEF pas2js}
|
|
|
+ @Pas2jsCacheDirToKeyName,@PtrFilenameToKeyName
|
|
|
+ {$ELSE}
|
|
|
+ @ComparePas2jsCacheDirectories,@CompareAnsiStringWithDirectoryCache
|
|
|
+ {$ENDIF});
|
|
|
end;
|
|
|
|
|
|
destructor TPas2jsCachedDirectories.Destroy;
|
|
@@ -898,17 +965,21 @@ end;
|
|
|
|
|
|
procedure TPas2jsCachedDirectories.Clear;
|
|
|
var
|
|
|
- Node: TAVLTreeNode;
|
|
|
Dir: TPas2jsCachedDirectory;
|
|
|
+ List: TFPList;
|
|
|
+ i: Integer;
|
|
|
begin
|
|
|
- Node:=FDirectories.FindLowest;
|
|
|
- while Node<>nil do begin
|
|
|
- Dir:=TPas2jsCachedDirectory(Node.Data);
|
|
|
- if Dir.FRefCount<>1 then
|
|
|
- raise Exception.Create('TPas2jsCachedDirectories.Clear [20180126090807] "'+Dir.Path+'" '+IntToStr(Dir.FRefCount));
|
|
|
- Dir.Release;
|
|
|
- Node.Data:=nil;
|
|
|
- Node:=FDirectories.FindSuccessor(Node);
|
|
|
+ List:=FDirectories.GetList;
|
|
|
+ try
|
|
|
+ for i:=0 to List.Count-1 do
|
|
|
+ begin
|
|
|
+ Dir:=TPas2jsCachedDirectory(List[i]);
|
|
|
+ if Dir.FRefCount<>1 then
|
|
|
+ raise Exception.Create('TPas2jsCachedDirectories.Clear [20180126090807] "'+Dir.Path+'" '+IntToStr(Dir.FRefCount));
|
|
|
+ Dir.Release;
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ List.Free;
|
|
|
end;
|
|
|
FDirectories.Clear;
|
|
|
end;
|
|
@@ -1032,16 +1103,14 @@ function TPas2jsCachedDirectories.GetDirectory(const Directory: string;
|
|
|
CreateIfNotExists: boolean; DoReference: boolean): TPas2jsCachedDirectory;
|
|
|
var
|
|
|
Dir: String;
|
|
|
- Node: TAVLTreeNode;
|
|
|
begin
|
|
|
Dir:=ResolveDots(Directory);
|
|
|
if not FilenameIsAbsolute(Dir) then
|
|
|
Dir:=WorkingDirectory+Dir;
|
|
|
Dir:=IncludeTrailingPathDelimiter(Dir);
|
|
|
- Node:=FDirectories.FindKey(Pointer(Dir),@CompareAnsiStringWithDirectoryCache);
|
|
|
- if Node<>nil then
|
|
|
+ Result:=TPas2jsCachedDirectory(FDirectories.FindKey(Pointer(Dir)));
|
|
|
+ if Result<>nil then
|
|
|
begin
|
|
|
- Result:=TPas2jsCachedDirectory(Node.Data);
|
|
|
if DoReference then
|
|
|
Result.Reference;
|
|
|
Result.Update;
|
|
@@ -1071,7 +1140,7 @@ begin
|
|
|
inherited Create(aFile.Filename);
|
|
|
FCachedFile:=aFile;
|
|
|
FSource:=aFile.Source;
|
|
|
- FSrcPos:=PChar(FSource);
|
|
|
+ FSrcPos:=1;
|
|
|
FIsEOF:=FSource='';
|
|
|
end;
|
|
|
|
|
@@ -1082,16 +1151,15 @@ end;
|
|
|
|
|
|
function TPas2jsFileLineReader.ReadLine: string;
|
|
|
var
|
|
|
- p: PChar;
|
|
|
+ S: string;
|
|
|
+ p, SrcLen: integer;
|
|
|
|
|
|
procedure GetLine;
|
|
|
var
|
|
|
l: SizeInt;
|
|
|
begin
|
|
|
l:=p-FSrcPos;
|
|
|
- SetLength(Result,l);
|
|
|
- if l>0 then
|
|
|
- Move(FSrcPos^,Result[1],l);
|
|
|
+ Result:=copy(S,FSrcPos,l);
|
|
|
FSrcPos:=p;
|
|
|
inc(FLineNumber);
|
|
|
if (CachedFile<>nil) and (CachedFile.Cache<>nil) then
|
|
@@ -1099,35 +1167,29 @@ var
|
|
|
//writeln('GetLine "',Result,'"');
|
|
|
end;
|
|
|
|
|
|
-var
|
|
|
- c: Char;
|
|
|
begin
|
|
|
if FIsEOF then exit('');
|
|
|
+ S:=Source;
|
|
|
+ SrcLen:=length(S);
|
|
|
p:=FSrcPos;
|
|
|
- repeat
|
|
|
- c:=p^;
|
|
|
- case c of
|
|
|
- #0:
|
|
|
- if p-PChar(FSource)=length(FSource) then
|
|
|
- begin
|
|
|
- FIsEOF:=true;
|
|
|
- GetLine;
|
|
|
- exit;
|
|
|
- end;
|
|
|
+ while p<=SrcLen do
|
|
|
+ case S[p] of
|
|
|
#10,#13:
|
|
|
begin
|
|
|
GetLine;
|
|
|
inc(p);
|
|
|
- if (p^ in [#10,#13]) and (p^<>c) then inc(p);
|
|
|
- if (p^=#0) and (p-PChar(FSource)=length(FSource)) then
|
|
|
+ if (p<=SrcLen) and (S[p] in [#10,#13]) and (S[p]<>S[p-1]) then
|
|
|
+ inc(p);
|
|
|
+ if p>SrcLen then
|
|
|
FIsEOF:=true;
|
|
|
FSrcPos:=p;
|
|
|
exit;
|
|
|
end;
|
|
|
+ else
|
|
|
+ inc(p);
|
|
|
end;
|
|
|
- inc(p);
|
|
|
- until false;
|
|
|
- Result:='';
|
|
|
+ FIsEOF:=true;
|
|
|
+ GetLine;
|
|
|
end;
|
|
|
|
|
|
{ TPas2jsCachedFile }
|
|
@@ -1817,7 +1879,12 @@ begin
|
|
|
FForeignUnitPaths:=TStringList.Create;
|
|
|
FNamespaces:=TStringList.Create;
|
|
|
FUnitPaths:=TStringList.Create;
|
|
|
- FFiles:=TAVLTree.Create(@CompareCachedFiles);
|
|
|
+ FFiles:=TPasAnalyzerKeySet.Create(
|
|
|
+ {$IFDEF Pas2js}
|
|
|
+ @Pas2jsCachedFileToKeyName,@PtrFilenameToKeyName
|
|
|
+ {$ELSE}
|
|
|
+ @CompareCachedFiles,@CompareFilenameWithCachedFile
|
|
|
+ {$ENDIF});
|
|
|
FDirectoryCache:=TPas2jsCachedDirectories.Create;
|
|
|
RegisterMessages;
|
|
|
end;
|
|
@@ -1825,7 +1892,7 @@ end;
|
|
|
destructor TPas2jsFilesCache.Destroy;
|
|
|
begin
|
|
|
FLog:=nil;
|
|
|
- FFiles.FreeAndClear;
|
|
|
+ FFiles.FreeItems;
|
|
|
FreeAndNil(FDirectoryCache);
|
|
|
FreeAndNil(FFiles);
|
|
|
FreeAndNil(FInsertFilenames);
|
|
@@ -1840,7 +1907,7 @@ procedure TPas2jsFilesCache.Reset;
|
|
|
begin
|
|
|
IncreaseChangeStamp(FResetStamp);
|
|
|
FDirectoryCache.Invalidate;
|
|
|
- // FFiles: TAVLTree; keep data, files are checked against LoadedFileAge
|
|
|
+ // FFiles: keep data, files are checked against LoadedFileAge
|
|
|
FOptions:=DefaultPas2jsFileCacheOptions;
|
|
|
FMainJSFile:='';
|
|
|
FMainJSFileResolved:='';
|
|
@@ -1859,7 +1926,9 @@ begin
|
|
|
FStates:=FStates-[cfsMainJSFileResolved];
|
|
|
FNamespaces.Clear;
|
|
|
FNamespacesFromCmdLine:=0;
|
|
|
+ {$IFDEF HasPas2jsFiler}
|
|
|
FPrecompileFormat:=nil;
|
|
|
+ {$ENDIF}
|
|
|
FSrcMapBaseDir:='';
|
|
|
// FOnReadFile: TPas2jsReadFileEvent; keep
|
|
|
// FOnWriteFile: TPas2jsWriteFileEvent; keep
|
|
@@ -1958,30 +2027,21 @@ begin
|
|
|
end;
|
|
|
|
|
|
function TPas2jsFilesCache.FindFile(Filename: string): TPas2jsCachedFile;
|
|
|
-var
|
|
|
- Node: TAVLTreeNode;
|
|
|
begin
|
|
|
Filename:=NormalizeFilename(Filename,true);
|
|
|
- Node:=FFiles.FindKey(Pointer(Filename),@CompareFilenameWithCachedFile);
|
|
|
- if Node=nil then
|
|
|
- exit(nil);
|
|
|
- Result:=TPas2jsCachedFile(Node.Data);
|
|
|
+ Result:=TPas2jsCachedFile(FFiles.FindKey(Pointer(Filename)));
|
|
|
end;
|
|
|
|
|
|
function TPas2jsFilesCache.LoadFile(Filename: string; Binary: boolean
|
|
|
): TPas2jsCachedFile;
|
|
|
-var
|
|
|
- Node: TAVLTreeNode;
|
|
|
begin
|
|
|
Filename:=NormalizeFilename(Filename,true);
|
|
|
- Node:=FFiles.FindKey(Pointer(Filename),@CompareFilenameWithCachedFile);
|
|
|
- if Node=nil then
|
|
|
+ Result:=TPas2jsCachedFile(FFiles.FindKey(Pointer(Filename)));
|
|
|
+ if Result=nil then
|
|
|
begin
|
|
|
// new file
|
|
|
Result:=TPas2jsCachedFile.Create(Self,Filename);
|
|
|
FFiles.Add(Result);
|
|
|
- end else begin
|
|
|
- Result:=TPas2jsCachedFile(Node.Data);
|
|
|
end;
|
|
|
Result.Load(true,Binary);
|
|
|
end;
|
|
@@ -2086,6 +2146,7 @@ begin
|
|
|
end;
|
|
|
end;
|
|
|
|
|
|
+{$IFDEF HasStreams}
|
|
|
procedure TPas2jsFilesCache.SaveToFile(ms: TMemoryStream; Filename: string);
|
|
|
var
|
|
|
s: string;
|
|
@@ -2122,6 +2183,7 @@ begin
|
|
|
end;
|
|
|
end;
|
|
|
end;
|
|
|
+{$ENDIF}
|
|
|
|
|
|
function TPas2jsFilesCache.ExpandDirectory(const Filename, BaseDir: string
|
|
|
): string;
|