diff --git a/packages/fcl-js/src/jswriter.pp b/packages/fcl-js/src/jswriter.pp index 3c6923fa75..95ea3aa86e 100644 --- a/packages/fcl-js/src/jswriter.pp +++ b/packages/fcl-js/src/jswriter.pp @@ -109,7 +109,7 @@ Type private FBufPos, FCapacity: Cardinal; - FBuffer : TBuffer; + FBuffer: TBuffer; function GetAsString: TJSWriterString; {$ifdef fpc} function GetBuffer: Pointer; @@ -119,6 +119,7 @@ Type {$ifdef FPC_HAS_CPSTRING} function GetUnicodeString: UnicodeString; {$endif} + procedure SetAsString(const AValue: TJSWriterString); procedure SetCapacity(AValue: Cardinal); Protected Function DoWrite(Const S : TJSWriterString) : integer; override; @@ -136,7 +137,7 @@ Type {$endif} Property BufferLength : Integer Read GetBufferLength; Property Capacity : Cardinal Read GetCapacity Write SetCapacity; - Property AsString : TJSWriterString Read GetAsString; + Property AsString : TJSWriterString Read GetAsString Write SetAsString; {$ifdef FPC_HAS_CPSTRING} Property AsAnsiString : AnsiString Read GetAsString; deprecated 'use AsString instead, fpc 3.3.1'; Property AsUnicodeString : UnicodeString Read GetUnicodeString; @@ -320,6 +321,16 @@ begin end; {$endif} +procedure TBufferWriter.SetAsString(const AValue: TJSWriterString); +begin + {$ifdef pas2js} + FBuffer:=TJSArray.new; + FCapacity:=0; + {$endif} + FBufPos:=0; + DoWrite(AValue); +end; + procedure TBufferWriter.SetCapacity(AValue: Cardinal); begin if FCapacity=AValue then Exit; @@ -328,7 +339,7 @@ begin FBufPos:=Capacity; end; -Function TBufferWriter.DoWrite(Const S: TJSWriterString): integer; +function TBufferWriter.DoWrite(const S: TJSWriterString): integer; {$ifdef pas2js} begin Result:=Length(S)*2; @@ -358,7 +369,7 @@ end; {$endif} {$ifdef FPC_HAS_CPSTRING} -Function TBufferWriter.DoWrite(Const S: UnicodeString): integer; +function TBufferWriter.DoWrite(const S: UnicodeString): integer; Var DesLen,MinLen : Integer; @@ -379,14 +390,14 @@ begin end; {$endif} -Constructor TBufferWriter.Create(Const ACapacity: Cardinal); +constructor TBufferWriter.Create(const ACapacity: Cardinal); begin inherited Create; Capacity:=ACapacity; end; {$ifdef fpc} -Procedure TBufferWriter.SaveToFile(Const AFileName: String); +procedure TBufferWriter.SaveToFile(const AFileName: String); Var F : File; diff --git a/packages/pastojs/src/fppas2js.pp b/packages/pastojs/src/fppas2js.pp index 74862c1689..a6bb5a757f 100644 --- a/packages/pastojs/src/fppas2js.pp +++ b/packages/pastojs/src/fppas2js.pp @@ -353,9 +353,11 @@ Works: - dispose, new - typecast byte(longword) -> value & $ff - typecast TJSFunction(func) +- modeswitch OmitRTTI ToDos: - do not rename property Date +- cmd line param to set modeswitch - bug: DoIt(typeinfo(i)) where DoIt is in another unit and has TTypeInfo - bug: v:=a[0] gives Local variable "a" is assigned but never used diff --git a/packages/pastojs/src/pas2jscompiler.pp b/packages/pastojs/src/pas2jscompiler.pp index 87c4744531..9a83c5a63a 100644 --- a/packages/pastojs/src/pas2jscompiler.pp +++ b/packages/pastojs/src/pas2jscompiler.pp @@ -24,7 +24,7 @@ uses {$ELSE} RtlConsts, {$ENDIF} - Classes, SysUtils, contnrs, + Classes, SysUtils, contnrs, process, jstree, jswriter, JSSrcMap, PScanner, PParser, PasTree, PasResolver, PasUseAnalyzer, PasResolveEval, FPPas2Js, FPPJsSrcMap, Pas2jsFileUtils, Pas2jsLogger, @@ -81,6 +81,11 @@ const nUnitFileNotFound = 136; sUnitFileNotFound = 'unit file not found %s'; nClassInterfaceStyleIs = 137; sClassInterfaceStyleIs = 'Class interface style is %s'; nMacroXSetToY = 138; sMacroXSetToY = 'Macro %s set to %s'; + nPostProcessorX = 139; sPostProcessorX = 'Post processor: %s'; + nPostProcessorRunX = 140; sPostProcessorRunX = 'Run post processor: %s'; + nPostProcessorFailX = 141; sPostProcessorFailX = 'Post processor failed: %s'; + nPostProcessorWarnX = 142; sPostProcessorWarnX = 'Post processor: %s'; + nPostProcessorFinished = 143; sPostProcessorFinished = 'Post processor finished'; // Note: error numbers 201+ are used by Pas2jsFileCache //------------------------------------------------------------------------------ @@ -380,6 +385,12 @@ type TPas2JSWPOptimizer = class(TPasAnalyzer) end; + TPas2jsParamState = ( + ppsSingle, + ppsPostProc + ); + TPas2jsParamStates = set of TPas2jsParamState; + { TPas2jsCompiler } TPas2jsCompiler = class @@ -401,6 +412,8 @@ type FMode: TP2jsMode; FOptions: TP2jsCompilerOptions; FParamMacros: TPas2jsMacroEngine; + FParamState: TPas2jsParamState; + FPostProcs: TObjectList; FSrcMapSourceRoot: string; FTargetPlatform: TPasToJsPlatform; FTargetProcessor: TPasToJsProcessor; @@ -464,6 +477,7 @@ type procedure LoadConfig(CfgFilename: string); procedure LoadDefaultConfig; procedure ParamFatal(Msg: string); + procedure CheckParamsClosed; procedure ReadParam(Param: string; Quick, FromCmdLine: boolean); procedure ReadSingleLetterOptions(const Param: string; p: integer; const Allowed: string; out Enabled, Disabled: string); @@ -473,6 +487,8 @@ type procedure RegisterMessages; protected // DoWriteJSFile: return false to use the default write function. + procedure CallPostProcessors(const JSFilename: String; aWriter: TPas2JSMapper); virtual; + function CallPostProcessor(const JSFilename: String; Cmd: TStringList; JS: TJSWriterString): TJSWriterString; virtual; function DoWriteJSFile(const DestFilename: String; aWriter: TPas2JSMapper): Boolean; virtual; procedure Compile(StartTime: TDateTime); procedure ProcessQueue; @@ -516,9 +532,11 @@ type procedure WriteVersionLine; procedure WriteOptions; procedure WriteDefines; + procedure WriteUsedTools; procedure WriteFoldersAndSearchPaths; procedure WriteInfo; function GetShownMsgTypes: TMessageTypes; + function CmdListAsStr(CmdList: TStrings): string; procedure AddDefine(const aName: String); procedure AddDefine(const aName, Value: String); @@ -552,10 +570,12 @@ type property Mode: TP2jsMode read FMode write SetMode; property Options: TP2jsCompilerOptions read FOptions write SetOptions; property ParamMacros: TPas2jsMacroEngine read FParamMacros; + property ParamState: TPas2jsParamState read FParamState; {$IFDEF HasPas2jsFiler} property PrecompileGUID: TGUID read FPrecompileGUID write FPrecompileGUID; property PrecompileInitialFlags: TPCUInitialFlags read FPrecompileInitialFlags; {$ENDIF} + property PostProcs: TObjectList read FPostProcs; // list of TStrings property RTLVersionCheck: TP2jsRTLVersionCheck read FRTLVersionCheck write FRTLVersionCheck; property SrcMapEnable: boolean read GetSrcMapEnable write SetSrcMapEnable; property SrcMapSourceRoot: string read FSrcMapSourceRoot write FSrcMapSourceRoot; @@ -2556,115 +2576,54 @@ begin if aFile.IsMainFile and (TargetPlatform=PlatformNodeJS) then aFileWriter.WriteFile('rtl.run();'+LineEnding,aFile.PasFilename); - // Give chance to descendants to write file - if DoWriteJSFile(aFile.JSFilename,aFileWriter) then - exit;// descendant has written -> finished - - if (aFile.JSFilename='') and (FileCache.MainJSFile='.') then + if FreeWriter then begin - // write to stdout - if FreeWriter then + CallPostProcessors(aFile.JSFilename,aFileWriter); + + // Give chance to descendants to write file + if DoWriteJSFile(aFile.JSFilename,aFileWriter) then + exit;// descendant has written -> finished + + if (aFile.JSFilename='') and (FileCache.MainJSFile='.') then begin - {$IFDEF HasStdErr} - Log.WriteMsgToStdErr:=false; - {$ENDIF} - try - Log.LogRaw(aFileWriter.AsString); - finally + // write to stdout + if FreeWriter then + begin {$IFDEF HasStdErr} - Log.WriteMsgToStdErr:=coWriteMsgToStdErr in Options; + Log.WriteMsgToStdErr:=false; {$ENDIF} - end; - end; - end else if FreeWriter then - begin - // write to file - - //writeln('TPas2jsCompiler.WriteJSFiles ',aFile.PasFilename,' ',aFile.JSFilename); - Log.LogMsg(nWritingFile,[QuoteStr(FileCache.FormatPath(DestFilename))],'',0,0, - not (coShowLineNumbers in Options)); - - // check output directory - DestDir:=ChompPathDelim(ExtractFilePath(DestFilename)); - if (DestDir<>'') and not DirectoryExists(DestDir) then - begin - Log.LogMsg(nOutputDirectoryNotFound,[QuoteStr(FileCache.FormatPath(DestDir))]); - Terminate(ExitCodeFileNotFound); - end; - if DirectoryExists(DestFilename) then - begin - Log.LogMsg(nFileIsFolder,[QuoteStr(FileCache.FormatPath(DestFilename))]); - Terminate(ExitCodeWriteError); - end; - - MapFilename:=DestFilename+'.map'; - - // write js - try - {$IFDEF Pas2js} - buf:=TJSArray.new; - {$ELSE} - buf:=TMemoryStream.Create; - {$ENDIF} - try - {$IFDEF FPC_HAS_CPSTRING} - // UTF8-BOM - if (Log.Encoding='') or (Log.Encoding='utf8') then - begin - Src:=String(UTF8BOM); - buf.Write(Src[1],length(Src)); - end; - {$ENDIF} - // JS source - {$IFDEF Pas2js} - buf:=TJSArray(aFileWriter.Buffer).slice(); - {$ELSE} - buf.Write(aFileWriter.Buffer^,aFileWriter.BufferLength); - {$ENDIF} - // source map comment - if aFileWriter.SrcMap<>nil then - begin - Src:='//# sourceMappingURL='+ExtractFilename(MapFilename)+LineEnding; - {$IFDEF Pas2js} - buf.push(Src); - {$ELSE} - buf.Write(Src[1],length(Src)); + try + Log.LogRaw(aFileWriter.AsString); + finally + {$IFDEF HasStdErr} + Log.WriteMsgToStdErr:=coWriteMsgToStdErr in Options; {$ENDIF} end; - {$IFDEF Pas2js} - {$ELSE} - buf.Position:=0; - {$ENDIF} - FileCache.SaveToFile(buf,DestFilename); - finally - {$IFDEF Pas2js} - buf:=nil; - {$ELSE} - buf.Free; - {$ENDIF} end; - except - on E: Exception do begin - if ShowDebug then - Log.LogExceptionBackTrace(E); - {$IFDEF FPC} - if E.Message<>SafeFormat(SFCreateError,[DestFileName]) then - {$ENDIF} - Log.LogPlain('Error: '+E.Message); - Log.LogMsg(nUnableToWriteFile,[QuoteStr(FileCache.FormatPath(DestFilename))]); - Terminate(ExitCodeWriteError); - end - {$IFDEF Pas2js} - else HandleJSException('[20181031190637] TPas2jsCompiler.WriteJSFiles',JSExceptValue,true); - {$ENDIF} - end; - - // write source map - if aFileWriter.SrcMap<>nil then + end else if FreeWriter then begin - Log.LogMsg(nWritingFile,[QuoteStr(FileCache.FormatPath(MapFilename))],'',0,0, + // write to file + + //writeln('TPas2jsCompiler.WriteJSFiles ',aFile.PasFilename,' ',aFile.JSFilename); + Log.LogMsg(nWritingFile,[QuoteStr(FileCache.FormatPath(DestFilename))],'',0,0, not (coShowLineNumbers in Options)); - FinishSrcMap(aFileWriter.SrcMap); + + // check output directory + DestDir:=ChompPathDelim(ExtractFilePath(DestFilename)); + if (DestDir<>'') and not DirectoryExists(DestDir) then + begin + Log.LogMsg(nOutputDirectoryNotFound,[QuoteStr(FileCache.FormatPath(DestDir))]); + Terminate(ExitCodeFileNotFound); + end; + if DirectoryExists(DestFilename) then + begin + Log.LogMsg(nFileIsFolder,[QuoteStr(FileCache.FormatPath(DestFilename))]); + Terminate(ExitCodeWriteError); + end; + + MapFilename:=DestFilename+'.map'; + + // write js try {$IFDEF Pas2js} buf:=TJSArray.new; @@ -2672,13 +2631,35 @@ begin buf:=TMemoryStream.Create; {$ENDIF} try - // Note: No UTF-8 BOM in source map, Chrome 59 gives an error - aFileWriter.SrcMap.SaveToStream(buf); + {$IFDEF FPC_HAS_CPSTRING} + // UTF8-BOM + if (Log.Encoding='') or (Log.Encoding='utf8') then + begin + Src:=String(UTF8BOM); + buf.Write(Src[1],length(Src)); + end; + {$ENDIF} + // JS source + {$IFDEF Pas2js} + buf:=TJSArray(aFileWriter.Buffer).slice(); + {$ELSE} + buf.Write(aFileWriter.Buffer^,aFileWriter.BufferLength); + {$ENDIF} + // source map comment + if aFileWriter.SrcMap<>nil then + begin + Src:='//# sourceMappingURL='+ExtractFilename(MapFilename)+LineEnding; + {$IFDEF Pas2js} + buf.push(Src); + {$ELSE} + buf.Write(Src[1],length(Src)); + {$ENDIF} + end; {$IFDEF Pas2js} {$ELSE} buf.Position:=0; {$ENDIF} - FileCache.SaveToFile(buf,MapFilename); + FileCache.SaveToFile(buf,DestFilename); finally {$IFDEF Pas2js} buf:=nil; @@ -2694,13 +2675,57 @@ begin if E.Message<>SafeFormat(SFCreateError,[DestFileName]) then {$ENDIF} Log.LogPlain('Error: '+E.Message); - Log.LogMsg(nUnableToWriteFile,[QuoteStr(FileCache.FormatPath(MapFilename))]); + Log.LogMsg(nUnableToWriteFile,[QuoteStr(FileCache.FormatPath(DestFilename))]); Terminate(ExitCodeWriteError); end {$IFDEF Pas2js} - else HandleJSException('[20181031190737] TPas2jsCompiler.WriteJSFiles',JSExceptValue); + else HandleJSException('[20181031190637] TPas2jsCompiler.WriteJSFiles',JSExceptValue,true); {$ENDIF} end; + + // write source map + if aFileWriter.SrcMap<>nil then + begin + Log.LogMsg(nWritingFile,[QuoteStr(FileCache.FormatPath(MapFilename))],'',0,0, + not (coShowLineNumbers in Options)); + FinishSrcMap(aFileWriter.SrcMap); + try + {$IFDEF Pas2js} + buf:=TJSArray.new; + {$ELSE} + buf:=TMemoryStream.Create; + {$ENDIF} + try + // Note: No UTF-8 BOM in source map, Chrome 59 gives an error + aFileWriter.SrcMap.SaveToStream(buf); + {$IFDEF Pas2js} + {$ELSE} + buf.Position:=0; + {$ENDIF} + FileCache.SaveToFile(buf,MapFilename); + finally + {$IFDEF Pas2js} + buf:=nil; + {$ELSE} + buf.Free; + {$ENDIF} + end; + except + on E: Exception do begin + if ShowDebug then + Log.LogExceptionBackTrace(E); + {$IFDEF FPC} + if E.Message<>SafeFormat(SFCreateError,[DestFileName]) then + {$ENDIF} + Log.LogPlain('Error: '+E.Message); + Log.LogMsg(nUnableToWriteFile,[QuoteStr(FileCache.FormatPath(MapFilename))]); + Terminate(ExitCodeWriteError); + end + {$IFDEF Pas2js} + else HandleJSException('[20181031190737] TPas2jsCompiler.WriteJSFiles',JSExceptValue); + {$ENDIF} + end; + end; end; end; @@ -3216,11 +3241,13 @@ begin ReadParam(Line,false,false); end; end; + CheckParamsClosed; finally FCurrentCfgFilename:=OldCfgFilename; FCurrentCfgLineNumber:=OldCfgLineNumber; aFile.Free; end; + if ParamState<>ppsSingle then if ShowTriedUsedFiles then Log.LogMsgIgnoreFilter(nEndOfReadingConfigFile,[QuoteStr(CfgFilename)]); end; @@ -3269,10 +3296,27 @@ end; procedure TPas2jsCompiler.ParamFatal(Msg: string); begin - Log.LogPlain(['Fatal: ',Msg]); + if CurrentCfgFilename<>'' then + Log.Log(mtFatal,Msg,0,CurrentCfgFilename,CurrentCfgLineNumber,0) + else + Log.LogPlain(['Fatal: ',Msg]); Terminate(ExitCodeErrorInParams); end; +procedure TPas2jsCompiler.CheckParamsClosed; +begin + case ParamState of + ppsSingle: ; + ppsPostProc: + if CurrentCfgFilename<>'' then + ParamFatal('-Jpostproc requires a line with a single ; at the end') + else + ParamFatal('-Jpostproc requires a single ; at the end'); + else + ParamFatal('multi argument option needs closing'); + end; +end; + procedure TPas2jsCompiler.ReadParam(Param: string; Quick, FromCmdLine: boolean); procedure UnknownParam; @@ -3297,6 +3341,7 @@ var {$IFDEF HasPas2jsFiler} Found: Boolean; PF: TPas2JSPrecompileFormat; + PostProc: TStringList; {$ENDIF} begin //writeln('TPas2jsCompiler.ReadParam ',Param,' ',Quick,' ',FromCmdLine); @@ -3317,6 +3362,36 @@ begin l:=length(Param); p:=1; + + case ParamState of + ppsPostProc: + begin + // parse multi arguments of -Jpostproc command ; + if Quick then + PostProc:=nil + else + PostProc:=TStringList(PostProcs[PostProcs.Count-1]); + if Param=';' then + begin + if (PostProc<>nil) and (PostProc.Count=0) then + ParamFatal('-Jpostproc needs command'); + FParamState:=ppsSingle; + end else if PostProc<>nil then + begin + if PostProc.Count=0 then + begin + // check executable + Value:=FileCache.ExpandExecutable(Param,''); + if Value='' then + ParamFatal('-Jpostproc executable "'+Param+'" not found'); + Param:=Value; + end; + PostProc.Add(Param); + end; + exit; + end; + end; + case Param[p] of '-': begin @@ -3584,6 +3659,21 @@ begin else UnknownParam; end; + 'p': + begin + // -J