pastojs: filer: test write/read multiple pcu

git-svn-id: trunk@38516 -
This commit is contained in:
Mattias Gaertner 2018-03-13 13:42:24 +00:00
parent 8e393548cf
commit fa0bf267e2
6 changed files with 361 additions and 174 deletions

View File

@ -253,6 +253,10 @@ type
out Count: integer);
procedure OnPasResolverLog(Sender: TObject; const Msg: String);
procedure OnParserLog(Sender: TObject; const Msg: String);
function OnPCUConverterIsElementUsed(Sender: TObject; El: TPasElement
): boolean;
function OnPCUConverterIsTypeInfoUsed(Sender: TObject; El: TPasElement
): boolean;
procedure OnScannerLog(Sender: TObject; const Msg: String);
procedure OnUseAnalyzerMessage(Sender: TObject; Msg: TPAMessage);
procedure HandleEParserError(E: EParserError);
@ -275,6 +279,7 @@ type
function GetInitialConverterOptions: TPasToJsConverterOptions;
procedure CreateScannerAndParser(aFileResolver: TPas2jsFileResolver);
procedure CreatePCUReader;
procedure CreateConverter;
function FindPCU(const UseUnitName: string; out aFormat: TPas2JSPrecompileFormat): string;
function OnResolverFindModule(const UseUnitName, InFilename: String; NameExpr,
InFileExpr: TPasExpr): TPasModule;
@ -868,6 +873,15 @@ begin
end;
end;
procedure TPas2jsCompilerFile.CreateConverter;
begin
if FConverter<>nil then exit;
FConverter:=TPasToJSConverter.Create;
FConverter.Options:=GetInitialConverterOptions;
FConverter.TargetPlatform:=Compiler.TargetPlatform;
FConverter.TargetProcessor:=Compiler.TargetProcessor;
end;
procedure TPas2jsCompilerFile.OnResolverCheckSrcName(const Element: TPasElement);
var
SrcName, ExpectedSrcName: String;
@ -968,6 +982,18 @@ begin
aScanner.CurFilename,aScanner.CurRow,aScanner.CurColumn);
end;
function TPas2jsCompilerFile.OnPCUConverterIsElementUsed(Sender: TObject;
El: TPasElement): boolean;
begin
Result:=UseAnalyzer.IsUsed(El);
end;
function TPas2jsCompilerFile.OnPCUConverterIsTypeInfoUsed(Sender: TObject;
El: TPasElement): boolean;
begin
Result:=UseAnalyzer.IsTypeInfoUsed(El);
end;
procedure TPas2jsCompilerFile.OnScannerLog(Sender: TObject; const Msg: String);
var
aScanner: TPascalScanner;
@ -1028,7 +1054,7 @@ end;
procedure TPas2jsCompilerFile.HandleUnknownException(E: Exception);
begin
if not (E is ECompilerTerminate) then
Log.Log(mtFatal,'bug: uncaught ECompilerTerminate'+': '+E.Message,0); // must use on E:ECompilerTerminate do raise;
Log.Log(mtFatal,'bug: uncaught '+E.ClassName+': '+E.Message,0); // must use on E:ECompilerTerminate do raise;
Log.Log(mtFatal,E.ClassName+': '+E.Message,0);
Compiler.Terminate(ExitCodeErrorInternal);
// Note: a "raise E" is not allowed by caught exceptions, try..except will free it
@ -1118,7 +1144,7 @@ begin
writeln('TPas2jsCompilerFile.ReaderFinished analyzing ',PasFilename,' ...');
{$ENDIF}
UseAnalyzer.AnalyzeModule(FPasModule);
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompilerFile.ReaderFinished analyzed ',PasFilename,' ScopeModule=',GetObjName(UseAnalyzer.ScopeModule));
{$ENDIF}
@ -1138,10 +1164,11 @@ var
Writer: TPCUWriter;
ms: TMemoryStream;
DestDir: String;
JS: TJSElement;
begin
if PasModule.ClassType<>TPasModule then
begin
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompilerFile.WritePCU not a unit: ',PasFilename,' skip');
{$ENDIF}
exit;
@ -1149,7 +1176,7 @@ begin
if (PCUFilename<>'') or (PCUReader<>nil) then
begin
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompilerFile.WritePCU already precompiled "',PCUFilename,'" Reader=',GetObjName(PCUReader));
{$ENDIF}
exit;
@ -1157,10 +1184,11 @@ begin
FPCUFilename:=Compiler.CreatePrecompileFilename(Self);
FPCUFormat:=Compiler.FileCache.PrecompileFormat;
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompilerFile.WritePCU precompiling ',PCUFilename);
{$ENDIF}
JS:=nil;
PF:=Compiler.FileCache.PrecompileFormat;
ms:=TMemoryStream.Create;
Writer:=PF.WriterClass.Create;
@ -1168,8 +1196,17 @@ begin
Writer.GUID:=Compiler.PrecompileGUID;
Writer.OnGetSrc:=@OnFilerGetSrc;
Writer.OnIsElementUsed:=@OnWriterIsElementUsed;
// create JavaScript for procs, initialization, finalization
CreateConverter;
Converter.Options:=Converter.Options+[coStoreImplJS];
Converter.OnIsElementUsed:=@OnPCUConverterIsElementUsed;
Converter.OnIsTypeInfoUsed:=@OnPCUConverterIsTypeInfoUsed;
JS:=Converter.ConvertPasElement(PasModule,PascalResolver);
Converter.Options:=Converter.Options-[coStoreImplJS];
Writer.WritePCU(PascalResolver,Converter,Compiler.PrecompileInitialFlags,ms,false);
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompilerFile.WritePCU precompiled ',PCUFilename);
{$ENDIF}
@ -1180,7 +1217,7 @@ begin
DestDir:=ChompPathDelim(ExtractFilePath(PCUFilename));
if (DestDir<>'') and not Compiler.DirectoryExists(DestDir) then
begin
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompilerFile.WritePCU output dir not found "',DestDir,'"');
{$ENDIF}
Log.LogMsg(nOutputDirectoryNotFound,[Compiler.FileCache.FormatPath(DestDir)]);
@ -1188,7 +1225,7 @@ begin
end;
if Compiler.DirectoryExists(PCUFilename) then
begin
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompilerFile.WritePCU file is folder "',DestDir,'"');
{$ENDIF}
Log.LogMsg(nFileIsFolder,[Compiler.FileCache.FormatPath(PCUFilename)]);
@ -1197,10 +1234,11 @@ begin
ms.Position:=0;
Compiler.FileCache.SaveToFile(ms,PCUFilename);
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompilerFile.WritePCU written ',PCUFilename);
{$ENDIF}
finally
JS.Free;
Writer.Free;
ms.Free;
end;
@ -1249,7 +1287,7 @@ begin
FReaderState:=prsWaitingForUsedUnits;
end;
{$IFDEF VerboseUnitQueue}
writeln('TPas2jsCompilerFile.ReadUnit ',PasFilename,' Finished=',Parser.CurModule=nil);
writeln('TPas2jsCompilerFile.ReadUnit ',PasFilename,' ReaderState=',ReaderState);
{$ENDIF}
except
on E: ECompilerTerminate do
@ -1283,7 +1321,7 @@ begin
Result:=Parser.CurModule=nil;
end;
{$IFDEF VerboseUnitQueue}
writeln('TPas2jsCompilerFile.ReadContinue ',PasFilename,' finished=',Parser.CurModule=nil);
writeln('TPas2jsCompilerFile.ReadContinue ',PasFilename,' finished=',Result);
{$ENDIF}
if Result then
ReaderFinished
@ -1322,12 +1360,9 @@ begin
UseAnalyzer.EmitModuleHints(PasModule);
// convert
FConverter:=TPasToJSConverter.Create;
FConverter.Options:=GetInitialConverterOptions;
FConverter.TargetPlatform:=Compiler.TargetPlatform;
FConverter.TargetProcessor:=Compiler.TargetProcessor;
FConverter.OnIsElementUsed:=@OnConverterIsElementUsed;
FConverter.OnIsTypeInfoUsed:=@OnConverterIsTypeInfoUsed;
CreateConverter;
Converter.OnIsElementUsed:=@OnConverterIsElementUsed;
Converter.OnIsTypeInfoUsed:=@OnConverterIsTypeInfoUsed;
FJSModule:=Converter.ConvertPasElement(PasModule,PascalResolver);
except
on E: ECompilerTerminate do
@ -1343,14 +1378,12 @@ var
begin
aModule:=GetCurPasModule;
if aModule=nil then exit;
if aModule.ClassType=TPasUnitModule then
Result:=TPasUnitModule(aModule).InterfaceSection
else if aModule.ClassType=TPasProgram then
if aModule.ClassType=TPasProgram then
Result:=TPasProgram(aModule).ProgramSection
else if aModule.ClassType=TPasLibrary then
Result:=TPasLibrary(aModule).LibrarySection
else
Result:=nil;
Result:=aModule.InterfaceSection;
end;
function TPas2jsCompilerFile.GetPasImplSection: TPasSection;
@ -1406,6 +1439,8 @@ function TPas2jsCompilerFile.GetCurPasModule: TPasModule;
begin
if PasModule<>nil then
Result:=PasModule
else if (PascalResolver<>nil) and (PascalResolver.RootElement<>nil) then
Result:=PascalResolver.RootElement
else if Parser<>nil then
Result:=Parser.CurModule
else
@ -1492,32 +1527,41 @@ end;
function TPas2jsCompilerFile.OnResolverFindModule(const UseUnitName,
InFilename: String; NameExpr, InFileExpr: TPasExpr): TPasModule;
var
FoundUnitName: string;
FoundIsForeign: Boolean;
FoundPasFilename, FoundPasUnitName, FoundPCUFilename, FoundPCUUnitName: string;
FoundPasIsForeign: Boolean;
FoundPCUFormat: TPas2JSPrecompileFormat;
function TryUnitName(const TestUnitName: string): string;
procedure TryUnitName(const TestUnitName: string);
var
aFile: TPas2jsCompilerFile;
begin
// search loaded units
aFile:=Compiler.FindUnitWithName(TestUnitName);
if aFile<>nil then
if FoundPasFilename='' then
begin
FoundUnitName:=TestUnitName;
exit(aFile.PasFilename);
// search loaded units
aFile:=Compiler.FindUnitWithName(TestUnitName);
if aFile<>nil then
begin
FoundPasFilename:=aFile.PasFilename;
FoundPasUnitName:=TestUnitName;
end else begin
// search pas in unit path
FoundPasFilename:=FileResolver.FindUnitFileName(TestUnitName,'',FoundPasIsForeign);
if FoundPasFilename<>'' then
FoundPasUnitName:=TestUnitName;
end;
end;
if FoundPCUFilename='' then
begin
FoundPCUFilename:=FindPCU(TestUnitName,FoundPCUFormat);
if FoundPCUFilename<>'' then
FoundPCUUnitName:=TestUnitName;
end;
// search pas in unit path
Result:=FileResolver.FindUnitFileName(TestUnitName,'',FoundIsForeign);
if Result<>'' then
FoundUnitName:=TestUnitName;
end;
var
aNameSpace, DefNameSpace, FoundPasFilename, FoundPCUFilename: String;
LastEl: TPasElement;
aNameSpace, DefNameSpace: String;
i: Integer;
aFile: TPas2jsCompilerFile;
FoundPCUFormat: TPas2JSPrecompileFormat;
begin
Result:=nil;
if CompareText(ExtractFilenameOnly(PasFilename),UseUnitname)=0 then
@ -1526,29 +1570,19 @@ begin
Parser.RaiseParserError(nUnitCycle,[UseUnitname]);
end;
LastEl:=PascalResolver.LastElement;
if (LastEl<>nil)
and ((LastEl is TPasSection) or (LastEl.ClassType=TPasUsesUnit)
or (LastEl.Parent is TPasSection)) then
// ok
else
RaiseInternalError(20170504161408,'internal error TPas2jsCompilerFile.FindModule PasTree.LastElement='+GetObjName(LastEl)+' '+GetObjName(LastEl.Parent));
FoundPasFilename:='';
FoundIsForeign:=false;
FoundUnitName:='';
FoundPasIsForeign:=false;
FoundPasUnitName:='';
FoundPCUFilename:='';
FoundPCUFormat:=nil;
FoundPCUUnitName:='';
if (InFilename='') and (Pos('.',UseUnitname)<1) then
begin
// generic unit -> search with namespaces
// first the default program namespace
DefNameSpace:=Compiler.GetDefaultNamespace;
if DefNameSpace<>'' then
begin
FoundPasFilename:=TryUnitName(DefNameSpace+'.'+UseUnitname);
FoundPCUFilename:=FindPCU(DefNameSpace+'.'+UseUnitname,FoundPCUFormat);
end;
TryUnitName(DefNameSpace+'.'+UseUnitname);
if (FoundPasFilename='') or (FoundPCUFilename='') then
begin
@ -1557,10 +1591,7 @@ begin
aNameSpace:=Compiler.FileCache.Namespaces[i];
if aNameSpace='' then continue;
if SameText(aNameSpace,DefNameSpace) then continue;
if FoundPasFilename='' then
FoundPasFilename:=TryUnitName(aNameSpace+'.'+UseUnitname);
if FoundPCUFilename='' then
FoundPCUFilename:=FindPCU(aNameSpace+'.'+UseUnitname,FoundPCUFormat);
TryUnitName(aNameSpace+'.'+UseUnitname);
end;
end;
end;
@ -1574,30 +1605,33 @@ begin
if aFile<>nil then
begin
FoundPasFilename:=aFile.PasFilename;
FoundUnitName:=UseUnitName;
FoundPasUnitName:=UseUnitName;
end;
end;
if FoundPasFilename='' then
begin
// search Pascal file
FoundPasFilename:=FileResolver.FindUnitFileName(UseUnitname,InFilename,FoundIsForeign);
FoundPasFilename:=FileResolver.FindUnitFileName(UseUnitname,InFilename,FoundPasIsForeign);
if FoundPasFilename<>'' then
begin
if InFilename<>'' then
FoundUnitName:=ExtractFilenameOnly(InFilename)
FoundPasUnitName:=ExtractFilenameOnly(InFilename)
else
FoundUnitName:=UseUnitName;
FoundPasUnitName:=UseUnitName;
end
else if InFilename<>'' then
exit; // an in-filename unit source is missing -> stop
end;
end;
if FoundPCUFilename='' then
begin
FoundPCUFilename:=FindPCU(UseUnitName,FoundPCUFormat);
FoundPCUUnitName:=UseUnitName;
end;
if (FoundPCUFilename<>'') and (FoundPasFilename='') then
begin
aFile:=LoadUsedUnit(FoundPCUFilename,UseUnitName,'',NameExpr,nil,false,FoundPCUFormat);
aFile:=LoadUsedUnit(FoundPCUFilename,FoundPCUUnitName,'',NameExpr,nil,false,FoundPCUFormat);
if aFile<>nil then
Result:=aFile.PasModule;
exit;
@ -1606,8 +1640,8 @@ begin
if FoundPasFilename<>'' then
begin
// load unit
aFile:=LoadUsedUnit(FoundPasFilename,FoundUnitName,InFilename,
NameExpr,InFileExpr,FoundIsForeign,nil);
aFile:=LoadUsedUnit(FoundPasFilename,FoundPasUnitName,InFilename,
NameExpr,InFileExpr,FoundPasIsForeign,nil);
if aFile<>nil then
Result:=aFile.PasModule;
end;
@ -1652,7 +1686,7 @@ var
Cycle: TFPList;
CyclePath: String;
begin
if Parser.CurModule.ImplementationSection=nil then
if PasModule.ImplementationSection=nil then
begin
// main uses section (e.g. interface or program, not implementation)
// -> check for cycles
@ -1910,6 +1944,7 @@ var
i: Integer;
aFile: TPas2jsCompilerFile;
Found: Boolean;
Section: TPasSection;
begin
// parse til exception or all modules have finished
repeat
@ -1934,6 +1969,13 @@ begin
writeln('TPas2jsCompiler.ProcessQueue aFile=',aFile.PasFilename);
{$ENDIF}
aFile.ReadContinue;
if aFile.ReaderState=prsCanContinue then
begin
{$IF defined(VerbosePasResolver) or defined(VerboseUnitQueue)}
writeln('TPas2jsCompiler.ProcessQueue aFile=',aFile.PasFilename,' ReadContinue buggy');
{$ENDIF}
RaiseInternalError(20180313130300,'File='+aFile.PasFilename+' ReadContinue buggy');
end;
break;
end;
until not Found;
@ -1945,13 +1987,20 @@ begin
for i:=0 to FReadingModules.Count-1 do
begin
aFile:=TPas2jsCompilerFile(FReadingModules[i]);
if aFile.Parser.CurModule<>nil then
if aFile.PascalResolver=nil then
RaiseInternalError(20180313124125,aFile.PasFilename);
if (aFile.PCUReader=nil) and (aFile.Parser.CurModule<>nil) then
begin
{$IF defined(VerbosePasResolver) or defined(VerboseUnitQueue)}
writeln('TPas2jsCompiler.ProcessQueue aFile=',aFile.PasFilename,' was not finished');
{$ENDIF}
RaiseInternalError(20180305185342,aFile.PasFilename);
end;
Section:=aFile.PascalResolver.GetLastSection;
if Section=nil then
RaiseInternalError(20180313124207,aFile.PasFilename);
if Section.PendingUsedIntf<>nil then
RaiseInternalError(20180313124226,aFile.PasFilename+' '+GetObjName(Section)+' PendingUsedIntf='+GetObjName(Section.PendingUsedIntf));
end;
end;
@ -1998,7 +2047,7 @@ begin
if FileCache.AllJSIntoMainJS and (WPOAnalyzer<>nil)
and not WPOAnalyzer.IsUsed(aFile.PasModule) then
begin
{$IF defined(VerboseUnitQueue) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TPas2jsCompiler.MarkNeedBuilding module not used by WPO: ',aFile.PasFilename);
{$ENDIF}
exit(false);

View File

@ -820,7 +820,8 @@ type
function ReadMemberHints(Obj: TJSONObject; El: TPasElement; const DefaultValue: TPasMemberHints): TPasMemberHints; virtual;
procedure ReadPasElement(Obj: TJSONObject; El: TPasElement; aContext: TPCUReaderContext); virtual;
procedure ReadExtRefs(Obj: TJSONObject; El: TPasElement); virtual;
procedure ReadUsedUnits(Obj: TJSONObject; Section: TPasSection; aContext: TPCUReaderContext); virtual;
procedure ReadUsedUnitsInit(Obj: TJSONObject; Section: TPasSection; aContext: TPCUReaderContext); virtual;
procedure ReadUsedUnitsFinish(Obj: TJSONObject; Section: TPasSection; aContext: TPCUReaderContext); virtual;
procedure ReadSectionScope(Obj: TJSONObject; Scope: TPasSectionScope; aContext: TPCUReaderContext); virtual;
procedure ReadSection(Obj: TJSONObject; Section: TPasSection; aContext: TPCUReaderContext); virtual;
procedure ReadDeclarations(Obj: TJSONObject; Section: TPasSection; aContext: TPCUReaderContext); virtual;
@ -1365,32 +1366,19 @@ end;
{ TPCUCustomReader }
function TPCUCustomReader.ReadCanContinue: boolean;
function SectionCanContinue(Section: TPasSection): boolean;
var
Scope: TPasSectionScope;
begin
Scope:=Section.CustomData as TPasSectionScope;
if Scope.Finished then exit(false);
Result:=Section.PendingUsedIntf=nil;
end;
var
Module: TPasModule;
Section: TPasSection;
Scope: TPasSectionScope;
begin
Result:=false;
Module:=Resolver.RootElement;
if Module is TPasProgram then
Result:=SectionCanContinue(TPasProgram(Module).ProgramSection)
else if Module is TPasLibrary then
Result:=SectionCanContinue(TPasLibrary(Module).LibrarySection)
else
begin
if Module.ImplementationSection<>nil then
Result:=SectionCanContinue(Module.ImplementationSection)
else
Result:=SectionCanContinue(Module.InterfaceSection);
end;
if Module=nil then exit(true); // not yet started
Section:=Resolver.GetLastSection;
if Section=nil then exit(true); // only header
Scope:=Section.CustomData as TPasSectionScope;
if Scope.Finished then exit(false); // finished
Result:=Section.PendingUsedIntf=nil;
end;
{ TPCUFilerElementRef }
@ -4626,20 +4614,19 @@ begin
end;
end;
procedure TPCUReader.ReadUsedUnits(Obj: TJSONObject; Section: TPasSection;
procedure TPCUReader.ReadUsedUnitsInit(Obj: TJSONObject; Section: TPasSection;
aContext: TPCUReaderContext);
// Note: can be called twice for each section if there are pending used interfaces
var
Arr: TJSONArray;
i, Id: Integer;
Data: TJSONData;
UsesObj, ModuleObj: TJSONObject;
UsesObj: TJSONObject;
Name, InFilename, ModuleName: string;
Use: TPasUsesUnit;
Module: TPasModule;
Scope, UsedScope: TPasSectionScope;
begin
if not ReadArray(Obj,'Uses',Arr,Section) then exit;
Scope:=Section.CustomData as TPasSectionScope;
SetLength(Section.UsesClause,Arr.Count);
for i:=0 to length(Section.UsesClause)-1 do
Section.UsesClause[i]:=nil;
@ -4668,16 +4655,50 @@ begin
if Module=nil then
RaiseMsg(20180307231247,Use);
Use.Module:=Module;
UsedScope:=Module.InterfaceSection.CustomData as TPasSectionScope;
Scope.UsesScopes.Add(UsedScope);
Module.AddRef;
if ReadInteger(UsesObj,'Id',Id,Use) then
AddElReference(Id,Use,Use);
end;
Resolver.CheckPendingUsedInterface(Section);
if aContext=nil then ;
end;
procedure TPCUReader.ReadUsedUnitsFinish(Obj: TJSONObject;
Section: TPasSection; aContext: TPCUReaderContext);
var
Arr: TJSONArray;
Scope, UsedScope: TPasSectionScope;
i: Integer;
Use: TPasUsesUnit;
Module: TPasModule;
Data: TJSONData;
UsesObj, ModuleObj: TJSONObject;
begin
if not ReadArray(Obj,'Uses',Arr,Section) then exit;
Scope:=Section.CustomData as TPasSectionScope;
if Scope.UsesFinished then
RaiseMsg(20180313133931,Section);
if Section.PendingUsedIntf<>nil then
RaiseMsg(20180313134142,Section,GetObjName(Section.PendingUsedIntf));
if Arr.Count<>length(Section.UsesClause) then
RaiseMsg(20180313134338,IntToStr(Arr.Count)+'<>'+IntToStr(length(Section.UsesClause)));
for i:=0 to Arr.Count-1 do
begin
Data:=Arr[i];
if not (Data is TJSONObject) then
RaiseMsg(20180313134409,Section,GetObjName(Data));
UsesObj:=TJSONObject(Data);
Use:=Section.UsesClause[i];
Module:=Use.Module as TPasModule;
UsedScope:=Module.InterfaceSection.CustomData as TPasSectionScope;
Scope.UsesScopes.Add(UsedScope);
// Refs
if ReadObject(UsesObj,'Module',ModuleObj,Use) then
ReadExtRefs(ModuleObj,Module);
end;
Resolver.CheckPendingUsedInterface(Section);
end;
Scope.UsesFinished:=true;
if aContext=nil then ;
end;
@ -4689,6 +4710,7 @@ end;
procedure TPCUReader.ReadSection(Obj: TJSONObject; Section: TPasSection;
aContext: TPCUReaderContext);
// Note: can be called twice for each section if there are pending used interfaces
var
Scope: TPasSectionScope;
begin
@ -4699,24 +4721,22 @@ begin
begin
ReadPasElement(Obj,Section,aContext);
Scope:=TPasSectionScope(Resolver.CreateScope(Section,TPasSectionScope));
ReadUsedUnits(Obj,Section,aContext);
ReadUsedUnitsInit(Obj,Section,aContext);
if Section.PendingUsedIntf<>nil then exit;
end
else
begin
Scope:=Section.CustomData as TPasSectionScope;
if Scope.Finished then
RaiseMsg(20180308160336,Section);
if Section.PendingUsedIntf<>nil then
RaiseMsg(20180308160639,Section,GetObjName(Section.PendingUsedIntf));
end;
if Scope.Finished then
RaiseMsg(20180308160336,Section);
if Scope.UsesFinished then
RaiseMsg(20180308160337,Section);
Scope.UsesFinished:=true;
// read external references
ReadUsedUnitsFinish(Obj,Section,aContext);
// read scope, needs external refs
ReadSectionScope(Obj,Scope,aContext);
// read declarations, needs external refs
ReadDeclarations(Obj,Section,aContext);
Scope.Finished:=true;
if Section is TInterfaceSection then

View File

@ -36,8 +36,8 @@ type
private
FAnalyzer: TPasAnalyzer;
FInitialFlags: TPCUInitialFlags;
FPJUReader: TPCUReader;
FPJUWriter: TPCUWriter;
FPCUReader: TPCUReader;
FPCUWriter: TPCUWriter;
FRestAnalyzer: TPasAnalyzer;
procedure OnFilerGetSrc(Sender: TObject; aFilename: string; out p: PChar;
out Count: integer);
@ -121,8 +121,8 @@ type
public
property Analyzer: TPasAnalyzer read FAnalyzer;
property RestAnalyzer: TPasAnalyzer read FRestAnalyzer;
property PJUWriter: TPCUWriter read FPJUWriter write FPJUWriter;
property PJUReader: TPCUReader read FPJUReader write FPJUReader;
property PCUWriter: TPCUWriter read FPCUWriter write FPCUWriter;
property PCUReader: TPCUReader read FPCUReader write FPCUReader;
property InitialFlags: TPCUInitialFlags read FInitialFlags;
end;
@ -220,7 +220,7 @@ function TCustomTestPrecompile.OnRestResolverFindUnit(const aUnitName: String
begin
CurEngine:=Resolvers[i];
CurUnitName:=ExtractFileUnitName(CurEngine.Filename);
{$IFDEF VerbosePJUFiler}
{$IFDEF VerbosePCUFiler}
//writeln('TCustomTestPrecompile.FindRestUnit Checking ',i,'/',ResolverCount,' ',CurEngine.Filename,' ',CurUnitName);
{$ENDIF}
if CompareText(Name,CurUnitName)=0 then
@ -228,12 +228,12 @@ function TCustomTestPrecompile.OnRestResolverFindUnit(const aUnitName: String
Result:=CurEngine.Module;
if Result<>nil then
begin
{$IFDEF VerbosePJUFiler}
{$IFDEF VerbosePCUFiler}
//writeln('TCustomTestPrecompile.FindRestUnit Found parsed module: ',Result.Filename);
{$ENDIF}
exit;
end;
{$IFDEF VerbosePJUFiler}
{$IFDEF VerbosePCUFiler}
writeln('TCustomTestPrecompile.FindRestUnit PARSING unit "',CurEngine.Filename,'"');
{$ENDIF}
Fail('not parsed');
@ -270,8 +270,8 @@ end;
procedure TCustomTestPrecompile.TearDown;
begin
FreeAndNil(FAnalyzer);
FreeAndNil(FPJUWriter);
FreeAndNil(FPJUReader);
FreeAndNil(FPCUWriter);
FreeAndNil(FPCUReader);
FreeAndNil(FInitialFlags);
inherited TearDown;
end;
@ -291,7 +291,7 @@ end;
procedure TCustomTestPrecompile.WriteReadUnit;
var
ms: TMemoryStream;
PJU, RestJSSrc, OrigJSSrc: string;
PCU, RestJSSrc, OrigJSSrc: string;
// restored classes:
RestResolver: TTestEnginePasResolver;
RestFileResolver: TFileResolver;
@ -302,8 +302,8 @@ var
begin
ConvertUnit;
FPJUWriter:=TPCUWriter.Create;
FPJUReader:=TPCUReader.Create;
FPCUWriter:=TPCUWriter.Create;
FPCUReader:=TPCUReader.Create;
ms:=TMemoryStream.Create;
RestParser:=nil;
RestScanner:=nil;
@ -313,9 +313,9 @@ begin
RestJSModule:=nil;
try
try
PJUWriter.OnGetSrc:=@OnFilerGetSrc;
PJUWriter.OnIsElementUsed:=@OnConverterIsElementUsed;
PJUWriter.WritePCU(Engine,Converter,InitialFlags,ms,false);
PCUWriter.OnGetSrc:=@OnFilerGetSrc;
PCUWriter.OnIsElementUsed:=@OnConverterIsElementUsed;
PCUWriter.WritePCU(Engine,Converter,InitialFlags,ms,false);
except
on E: Exception do
begin
@ -327,12 +327,12 @@ begin
end;
try
SetLength(PJU,ms.Size);
System.Move(ms.Memory^,PJU[1],length(PJU));
SetLength(PCU,ms.Size);
System.Move(ms.Memory^,PCU[1],length(PCU));
writeln('TCustomTestPrecompile.WriteReadUnit PJU START-----');
writeln(PJU);
writeln('TCustomTestPrecompile.WriteReadUnit PJU END-------');
writeln('TCustomTestPrecompile.WriteReadUnit PCU START-----');
writeln(PCU);
writeln('TCustomTestPrecompile.WriteReadUnit PCU END-------');
RestFileResolver:=TFileResolver.Create;
RestScanner:=TPascalScanner.Create(RestFileResolver);
@ -345,8 +345,8 @@ begin
RestParser.Options:=po_tcmodules;
RestResolver.CurrentParser:=RestParser;
ms.Position:=0;
PJUReader.ReadPCU(RestResolver,ms);
if not PJUReader.ReadContinue then
PCUReader.ReadPCU(RestResolver,ms);
if not PCUReader.ReadContinue then
Fail('ReadContinue=false, pending used interfaces');
except
on E: Exception do
@ -641,8 +641,8 @@ begin
Fail(Path+'.UsesScopes['+IntToStr(i)+'] Rest='+GetObjName(TObject(Rest.UsesScopes[i])));
RestUses:=TPasSectionScope(Rest.UsesScopes[i]);
if OrigUses.ClassType<>RestUses.ClassType then
Fail(Path+'.Uses['+IntToStr(i)+'] Orig='+GetObjName(OrigUses)+' Rest='+GetObjName(RestUses));
CheckRestoredReference(Path+'.Uses['+IntToStr(i)+']',OrigUses.Element,RestUses.Element);
Fail(Path+'.UsesScopes['+IntToStr(i)+'] Orig='+GetObjName(OrigUses)+' Rest='+GetObjName(RestUses));
CheckRestoredReference(Path+'.UsesScopes['+IntToStr(i)+']',OrigUses.Element,RestUses.Element);
end;
AssertEquals(Path+'.Finished',Orig.Finished,Rest.Finished);
CheckRestoredIdentifierScope(Path,Orig,Rest);

View File

@ -662,31 +662,40 @@ var
Result:=p;
end;
procedure SkipLineEnd(var p: PChar);
begin
if p^ in [#10,#13] then
begin
if (p[1] in [#10,#13]) and (p^<>p[1]) then
inc(p,2)
else
inc(p);
end;
end;
procedure DiffFound;
var
ActLineStartP, ActLineEndP, p, StartPos: PChar;
ExpLine, ActLine: String;
i: Integer;
i, LineNo, DiffLineNo: Integer;
begin
writeln('Diff found "',Msg,'". Lines:');
// write correct lines
p:=PChar(Expected);
LineNo:=0;
DiffLineNo:=0;
repeat
StartPos:=p;
while not (p^ in [#0,#10,#13]) do inc(p);
ExpLine:=copy(Expected,StartPos-PChar(Expected)+1,p-StartPos);
if p^ in [#10,#13] then
begin
if (p[1] in [#10,#13]) and (p^<>p[1]) then
inc(p,2)
else
inc(p);
end;
SkipLineEnd(p);
inc(LineNo);
if (p<=ExpectedP) and (p^<>#0) then
begin
writeln('= ',ExpLine);
end else begin
// diff line
if DiffLineNo=0 then DiffLineNo:=LineNo;
// write actual line
ActLineStartP:=FindLineStart(ActualP,PChar(Actual));
ActLineEndP:=FindLineEnd(ActualP);
@ -699,6 +708,15 @@ var
writeln('^');
Msg:='expected "'+ExpLine+'", but got "'+ActLine+'".';
CheckSrcDiff:=false;
// write up to three following actual lines to get some context
for i:=1 to 3 do begin
ActLineStartP:=ActLineEndP;
SkipLineEnd(ActLineStartP);
if ActLineStartP^=#0 then break;
ActLineEndP:=FindLineEnd(ActLineStartP);
ActLine:=copy(Actual,ActLineStartP-PChar(Actual)+1,ActLineEndP-ActLineStartP);
writeln('~ ',ActLine);
end;
exit;
end;
until p^=#0;

View File

@ -26,52 +26,133 @@ interface
uses
Classes, SysUtils,
fpcunit, testregistry,
tcunitsearch, tcmodules;
tcunitsearch, tcmodules, Pas2jsFileUtils;
type
{ TTestCLI_Precompile }
TTestCLI_Precompile = class(TCustomTestCLI)
public
protected
procedure CheckPrecompile(MainFile, UnitPaths: string;
SharedParams: TStringList = nil;
FirstRunParams: TStringList = nil;
SecondRunParams: TStringList = nil);
published
procedure TestPCU_EmptyUnit;
procedure TestPCU_ParamNS;
procedure TestPCU_UnitCycle;
end;
function LinesToList(const Lines: array of string): TStringList;
implementation
function LinesToList(const Lines: array of string): TStringList;
var
i: Integer;
begin
Result:=TStringList.Create;
for i:=Low(Lines) to High(Lines) do Result.Add(Lines[i]);
end;
{ TTestCLI_Precompile }
procedure TTestCLI_Precompile.TestPCU_EmptyUnit;
procedure TTestCLI_Precompile.CheckPrecompile(MainFile, UnitPaths: string;
SharedParams: TStringList; FirstRunParams: TStringList;
SecondRunParams: TStringList);
var
aFile, JSFile: TCLIFile;
OrigSrc, NewSrc, s: String;
UnitOutputDir, JSFilename, OrigSrc, NewSrc, s: String;
JSFile: TCLIFile;
begin
AddUnit('sub/system.pp',[''],['']);
try
UnitOutputDir:='units';
AddDir(UnitOutputDir);
// compile, create .pcu files
{$IFDEF VerbosePCUFiler}
writeln('TTestCLI_Precompile.CheckPrecompile create pcu files=========================');
{$ENDIF}
Params.Clear;
if SharedParams<>nil then
Params.Assign(SharedParams);
if FirstRunParams<>nil then
Params.AddStrings(FirstRunParams);
Compile([MainFile,'-Jc','-Fu'+UnitPaths,'-JUpcu','-FU'+UnitOutputDir]);
AssertFileExists('units/system.pcu');
JSFilename:=UnitOutputDir+PathDelim+ExtractFilenameOnly(MainFile)+'.js';
AssertFileExists(JSFilename);
JSFile:=FindFile(JSFilename);
OrigSrc:=JSFile.Source;
// compile, using .pcu files
{$IFDEF VerbosePCUFiler}
writeln('TTestCLI_Precompile.CheckPrecompile compile using pcu files==================');
{$ENDIF}
JSFile.Source:='';
Compiler.Reset;
Params.Clear;
if SharedParams<>nil then
Params.Assign(SharedParams);
if SecondRunParams<>nil then
Params.AddStrings(SecondRunParams);
Compile([MainFile,'-Jc','-FU'+UnitOutputDir]);
NewSrc:=JSFile.Source;
if not CheckSrcDiff(OrigSrc,NewSrc,s) then
begin
WriteSources;
Fail('test1.js: '+s);
end;
finally
SharedParams.Free;
FirstRunParams.Free;
SecondRunParams.Free;
end;
end;
procedure TTestCLI_Precompile.TestPCU_EmptyUnit;
begin
AddUnit('src/system.pp',[''],['']);
AddFile('test1.pas',[
'begin',
'end.']);
AddDir('units');
// compile, create .pcu files
{$IFDEF VerbosePJUFiler}
writeln('TTestCLI_Precompile.TestPCU_EmptyUnit create pcu files=========================');
{$ENDIF}
Compile(['test1.pas','-Jc','-Fusub','-JUpcu','-FUunits']);
aFile:=FindFile('units/system.pcu');
AssertNotNull('units/system.pcu',aFile);
JSFile:=FindFile('units/test1.js');
AssertNotNull('units/test1.js',JSFile);
OrigSrc:=JSFile.Source;
CheckPrecompile('test1.pas','src');
end;
// compile, using .pcu files
{$IFDEF VerbosePJUFiler}
writeln('TTestCLI_Precompile.TestPCU_EmptyUnit compile using pcu files==================');
{$ENDIF}
Compiler.Reset;
Compile(['test1.pas','-Jc','-Fuunits']);
NewSrc:=JSFile.Source;
if not CheckSrcDiff(OrigSrc,NewSrc,s) then
Fail('test1.js: '+s);
procedure TTestCLI_Precompile.TestPCU_ParamNS;
begin
AddUnit('src/system.pp',[''],['']);
AddUnit('src/foo.unit1.pp',['var i: longint;'],['']);
AddFile('test1.pas',[
'uses unit1;',
'begin',
' i:=3;',
'end.']);
CheckPrecompile('test1.pas','src',LinesToList(['-NSfoo']));
end;
procedure TTestCLI_Precompile.TestPCU_UnitCycle;
begin
AddUnit('src/system.pp',['type integer = longint;'],['']);
AddUnit('src/unit1.pp',
['var i: integer;',
'procedure Do1(j: integer);'],
['uses unit2;',
'procedure Do1(j: integer);',
'begin',
' Do2(j);',
'end;']);
AddUnit('src/unit2.pp',
['uses unit1;',
'procedure Do2(j: integer);'],
['procedure Do2(j: integer);',
'begin',
' unit1.i:=j;',
'end;']);
AddFile('test1.pas',[
'uses unit1;',
'begin',
' Do1(3);',
'end.']);
CheckPrecompile('test1.pas','src');
end;
Initialization

View File

@ -112,6 +112,7 @@ type
function AddFile(Filename: string; const SourceLines: array of string): TCLIFile;
function AddUnit(Filename: string; const Intf, Impl: array of string): TCLIFile;
function AddDir(Filename: string): TCLIFile;
procedure AssertFileExists(Filename: string);
property WorkDir: string read FWorkDir write SetWorkDir;
property DefaultFileAge: longint read FDefaultFileAge write FDefaultFileAge;
property ExitCode: integer read GetExitCode write SetExitCode;
@ -240,7 +241,7 @@ procedure TCustomTestCLI.DoLog(Sender: TObject; const Msg: String);
var
LogMsg: TCLILogMsg;
begin
{$IF defined(VerbosePasResolver) or defined(VerbosePJUFiler)}
{$IF defined(VerbosePasResolver) or defined(VerbosePCUFiler)}
writeln('TCustomTestCLI.DoLog ',Msg,' File=',Compiler.Log.LastMsgFile,' Line=',Compiler.Log.LastMsgLine);
{$ENDIF}
LogMsg:=TCLILogMsg.Create;
@ -296,7 +297,7 @@ begin
if aFile=nil then exit(false);
if (faDirectory and aFile.Attr)>0 then
begin
{$IF defined(VerbosePasResolver) or defined(VerbosePJUFiler)}
{$IF defined(VerbosePasResolver) or defined(VerbosePCUFiler)}
writeln('[20180224000557] TCustomTestCLI.OnReadFile ',aFilename);
{$ENDIF}
EPas2jsFileCache.Create('TCustomTestCLI.OnReadFile: reading directory '+aFilename);
@ -309,29 +310,40 @@ end;
procedure TCustomTestCLI.OnWriteFile(aFilename: string; Source: string);
var
aFile: TCLIFile;
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
//i: Integer;
{$ENDIF}
begin
aFile:=FindFile(aFilename);
{$IF defined(VerbosePasResolver) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('TCustomTestCLI.WriteFile ',aFilename,' Found=',aFile<>nil,' SrcLen=',length(Source));
{$ENDIF}
if aFile<>nil then
begin
if faDirectory and aFile.Attr>0 then
if (faDirectory and aFile.Attr)>0 then
begin
{$IF defined(VerbosePasResolver) or defined(VerbosePJUFiler)}
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
writeln('[20180223175616] TCustomTestCLI.OnWriteFile ',aFilename);
{$ENDIF}
raise EPas2jsFileCache.Create('unable to write file to directory "'+aFilename+'"');
end;
end else
begin
{$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
//writeln('TCustomTestCLI.OnWriteFile FFiles: ',FFiles.Count);
//for i:=0 to FFiles.Count-1 do
//begin
// aFile:=TCLIFile(FFiles[i]);
// writeln(' ',i,': Filename=',aFile.Filename,' ',CompareFilenames(aFile.Filename,aFilename),' Dir=',(aFile.Attr and faDirectory)>0,' Len=',length(aFile.Source));
//end;
{$ENDIF}
aFile:=TCLIFile.Create(aFilename,'',0,0);
FFiles.Add(aFile);
end;
aFile.Source:=Source;
aFile.Attr:=faNormal;
aFile.Age:=DateTimeToFileDate(CurDate);
//writeln('TCustomTestCLI.OnWriteFile ',aFile.Filename,' "',LeftStr(aFile.Source,50),'"');
writeln('TCustomTestCLI.OnWriteFile ',aFile.Filename,' Found=',FindFile(aFilename)<>nil,' "',LeftStr(aFile.Source,50),'" ');
end;
procedure TCustomTestCLI.WriteSources;
@ -394,7 +406,6 @@ var
i: Integer;
begin
AssertEquals('Initial System.ExitCode',0,system.ExitCode);
Params.Clear;
for i:=low(Args) to High(Args) do
Params.Add(Args[i]);
try
@ -404,13 +415,13 @@ begin
except
on E: ECompilerTerminate do
begin
{$IF defined(VerbosePasResolver) or defined(VerbosePJUFiler)}
{$IF defined(VerbosePasResolver) or defined(VerbosePCUFiler)}
writeln('TCustomTestCLI.Compile ',E.ClassName,':',E.Message);
{$ENDIF}
end;
on E: Exception do
begin
{$IF defined(VerbosePasResolver) or defined(VerbosePJUFiler)}
{$IF defined(VerbosePasResolver) or defined(VerbosePCUFiler)}
writeln('TCustomTestCLI.Compile ',E.ClassName,':',E.Message);
{$ENDIF}
Fail('TCustomTestCLI.Compile '+E.ClassName+':'+E.Message);
@ -454,7 +465,7 @@ end;
function TCustomTestCLI.AddFile(Filename, Source: string): TCLIFile;
begin
Filename:=ExpandFilename(Filename);
{$IFDEF VerbosePJUFiler}
{$IFDEF VerbosePCUFiler}
writeln('TCustomTestCLI.AddFile ',Filename);
{$ENDIF}
Result:=FindFile(Filename);
@ -504,14 +515,14 @@ begin
Result:=aFile;
if aFile=nil then
begin
{$IFDEF VerbosePJUFiler}
{$IFDEF VerbosePCUFiler}
writeln('TCustomTestCLI.AddDir add Dir=',Dir);
{$ENDIF}
FFiles.Add(TCLIFile.Create(Dir,'',DefaultFileAge,faDirectory));
end
else if (aFile.Attr and faDirectory)=0 then
begin
{$IFDEF VerbosePJUFiler}
{$IFDEF VerbosePCUFiler}
writeln('[20180224001036] TCustomTestCLI.AddDir file exists: Dir=',Dir);
{$ENDIF}
raise EPas2jsFileCache.Create('[20180224001036] TCustomTestCLI.AddDir Dir='+Dir);
@ -522,6 +533,14 @@ begin
end;
end;
procedure TCustomTestCLI.AssertFileExists(Filename: string);
var
aFile: TCLIFile;
begin
aFile:=FindFile(Filename);
AssertNotNull('File not found: '+Filename,aFile);
end;
function TCustomTestCLI.GetLogCount: integer;
begin
Result:=FLogMsgs.Count;