pas2jslogger.pp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. { Author: Mattias Gaertner 2017 [email protected]
  2. Abstract:
  3. Logging to stdout or file.
  4. Filtering messages by number and type.
  5. Registering messages with number, pattern and type (error, warning, note, etc).
  6. }
  7. unit Pas2jsLogger;
  8. {$mode objfpc}{$H+}
  9. {$inline on}
  10. interface
  11. uses
  12. Classes, SysUtils, PasTree, PScanner, jstree, jsbase, jswriter,
  13. Pas2jsFileUtils;
  14. const
  15. ExitCodeErrorInternal = 1; // internal error
  16. ExitCodeErrorInParams = 2; // error in command line parameters
  17. ExitCodeErrorInConfig = 3; // error in config file
  18. ExitCodeFileNotFound = 4;
  19. ExitCodeWriteError = 5;
  20. ExitCodeSyntaxError = 6;
  21. ExitCodeConverterError = 7;
  22. const
  23. DefaultLogMsgTypes = [mtFatal..mtDebug]; // by default show everything
  24. type
  25. { TPas2jsMessage }
  26. TPas2jsMessage = class
  27. public
  28. Number: integer;
  29. Typ: TMessageType;
  30. Pattern: string;
  31. end;
  32. TPas2jsLogEvent = Procedure (Sender : TObject; Const Msg : String) Of Object;
  33. { TPas2jsLogger }
  34. TPas2jsLogger = class
  35. private
  36. FEncoding: string;
  37. FMsgNumberDisabled: PInteger;// sorted ascending
  38. FMsgNumberDisabledCount: integer;
  39. FMsg: TFPList; // list of TPas2jsMessage
  40. FOnFormatPath: TPScannerFormatPathEvent;
  41. FOnLog: TPas2jsLogEvent;
  42. FOutputFile: TFileWriter;
  43. FOutputFilename: string;
  44. FShowMsgNumbers: boolean;
  45. FShowMsgTypes: TMessageTypes;
  46. FSorted: boolean;
  47. function GetMsgCount: integer;
  48. function GetMsgNumberDisabled(MsgNumber: integer): boolean;
  49. function GetMsgs(Index: integer): TPas2jsMessage; inline;
  50. function FindMsgNumberDisabled(MsgNumber: integer; FindInsertPos: boolean): integer;
  51. procedure SetEncoding(const AValue: string);
  52. procedure SetMsgNumberDisabled(MsgNumber: integer; AValue: boolean);
  53. procedure SetOutputFilename(AValue: string);
  54. procedure SetSorted(AValue: boolean);
  55. public
  56. constructor Create;
  57. destructor Destroy; override;
  58. procedure RegisterMsg(MsgType: TMessageType; MsgNumber: integer; Pattern: string);
  59. function FindMsg(MsgNumber: integer; ExceptionOnNotFound: boolean): TPas2jsMessage;
  60. procedure Sort;
  61. procedure LogRaw(const Msg: string); overload;
  62. procedure LogRaw(Args: array of const); overload;
  63. procedure LogLn;
  64. procedure LogMsg(MsgNumber: integer; Args: array of const;
  65. const Filename: string = ''; Line: integer = 0; Col: integer = 0;
  66. UseFilter: boolean = true);
  67. procedure LogMsgIgnoreFilter(MsgNumber: integer; Args: array of const);
  68. function MsgTypeToStr(MsgType: TMessageType): string;
  69. procedure Log(MsgType: TMessageType; Msg: string; MsgNumber: integer = 0;
  70. const Filename: string = ''; Line: integer = 0; Col: integer = 0;
  71. UseFilter: boolean = true);
  72. function GetMsgText(MsgNumber: integer; Args: array of const): string;
  73. function FormatMsg(MsgType: TMessageType; Msg: string; MsgNumber: integer = 0;
  74. const Filename: string = ''; Line: integer = 0; Col: integer = 0): string;
  75. procedure OpenOutputFile;
  76. procedure Flush;
  77. procedure CloseOutputFile;
  78. procedure Reset;
  79. public
  80. property Encoding: string read FEncoding write SetEncoding; // normalized
  81. property MsgCount: integer read GetMsgCount;
  82. property Msgs[Index: integer]: TPas2jsMessage read GetMsgs;
  83. property MsgNumberDisabled[MsgNumber: integer]: boolean read GetMsgNumberDisabled write SetMsgNumberDisabled;
  84. property OnFormatPath: TPScannerFormatPathEvent read FOnFormatPath write FOnFormatPath;
  85. property OutputFilename: string read FOutputFilename write SetOutputFilename;
  86. property ShowMsgNumbers: boolean read FShowMsgNumbers write FShowMsgNumbers;
  87. property ShowMsgTypes: TMessageTypes read FShowMsgTypes write FShowMsgTypes;
  88. property Sorted: boolean read FSorted write SetSorted;
  89. Property OnLog : TPas2jsLogEvent Read FOnLog Write FonLog;
  90. end;
  91. function CompareP2JMessage(Item1, Item2: Pointer): Integer;
  92. function AsString(Element: TPasElement; Full: boolean = true): string; overload;
  93. function AsString(Element: TJSElement): string; overload;
  94. function DbgString(Element: TJSElement; Indent: integer): string; overload;
  95. function DbgAsString(Element: TJSValue; Indent: integer): string; overload;
  96. function DbgAsString(Element: TJSArrayLiteralElements; Indent: integer): string; overload;
  97. function DbgAsString(Element: TJSObjectLiteralElements; Indent: integer): string; overload;
  98. function DbgAsString(Element: TJSObjectLiteralElement; Indent: integer): string; overload;
  99. function DbgHexMem(p: Pointer; Count: integer): string;
  100. function DbgStr(const s: string): string;
  101. implementation
  102. function CompareP2JMessage(Item1, Item2: Pointer): Integer;
  103. var
  104. Msg1: TPas2jsMessage absolute Item1;
  105. Msg2: TPas2jsMessage absolute Item2;
  106. begin
  107. Result:=Msg1.Number-Msg2.Number;
  108. end;
  109. function AsString(Element: TPasElement; Full: boolean): string;
  110. begin
  111. if Element=nil then
  112. Result:='(no element)'
  113. else begin
  114. Result:=Element.GetDeclaration(Full);
  115. end;
  116. end;
  117. function AsString(Element: TJSElement): string;
  118. var
  119. aTextWriter: TBufferWriter;
  120. aWriter: TJSWriter;
  121. begin
  122. aTextWriter:=TBufferWriter.Create(120);
  123. aWriter:=TJSWriter.Create(aTextWriter);
  124. aWriter.WriteJS(Element);
  125. Result:=aTextWriter.AsAnsistring;
  126. aWriter.Free;
  127. aTextWriter.Free;
  128. end;
  129. function DbgString(Element: TJSElement; Indent: integer): string;
  130. begin
  131. if Element=nil then
  132. Result:='(*no element*)'
  133. else if Element is TJSLiteral then begin
  134. Result:=DbgAsString(TJSLiteral(Element).Value,Indent+2);
  135. end else if Element is TJSPrimaryExpressionIdent then begin
  136. Result:=String(TJSPrimaryExpressionIdent(Element).Name);
  137. // array literal
  138. end else if Element is TJSArrayLiteral then begin
  139. Result:='['+DbgAsString(TJSArrayLiteral(Element).Elements,Indent+2)+']';
  140. // object literal
  141. end else if Element is TJSObjectLiteral then begin
  142. Result:='['+DbgAsString(TJSObjectLiteral(Element).Elements,Indent+2)+']';
  143. // arguments
  144. end else if Element is TJSArguments then begin
  145. Result:='('+DbgAsString(TJSArguments(Element).Elements,Indent+2)+')';
  146. // member
  147. end else if Element is TJSMemberExpression then begin
  148. Result:='('+DbgString(TJSMemberExpression(Element).MExpr,Indent+2)+')';
  149. // ToDo: TJSNewMemberExpression
  150. // ToDo: TJSDotMemberExpression
  151. // ToDo: TJSBracketMemberExpression
  152. // call
  153. end else if Element is TJSCallExpression then begin
  154. Result:=DbgString(TJSCallExpression(Element).Expr,Indent+2)
  155. +DbgString(TJSCallExpression(Element).Args,Indent+2);
  156. // unary
  157. end else if Element is TJSUnary then begin
  158. Result:=TJSUnary(Element).PrefixOperator
  159. +DbgString(TJSUnary(Element).A,Indent+2)
  160. +TJSUnary(Element).PostFixOperator;
  161. // binary
  162. end else if Element is TJSBinary then begin
  163. if Element is TJSStatementList then begin
  164. Result:=DbgString(TJSBinaryExpression(Element).A,Indent+2)+';'+LineEnding
  165. +Space(Indent)+DbgString(TJSBinaryExpression(Element).B,Indent);
  166. end else if Element is TJSVariableDeclarationList then begin
  167. Result:=DbgString(TJSBinaryExpression(Element).A,Indent+2)+';'+LineEnding
  168. +Space(Indent)+DbgString(TJSBinaryExpression(Element).B,Indent);
  169. end else if Element is TJSWithStatement then begin
  170. Result:='with ('+DbgString(TJSBinaryExpression(Element).A,Indent+2)+'){'+LineEnding
  171. +Space(Indent)+DbgString(TJSBinaryExpression(Element).B,Indent+2)+LineEnding
  172. +Space(Indent)+'}';
  173. end else if Element is TJSBinaryExpression then begin
  174. Result:=DbgString(TJSBinaryExpression(Element).A,Indent+2);
  175. if TJSBinaryExpression(Element).AllowCompact then
  176. Result+=TJSBinaryExpression(Element).OperatorString
  177. else
  178. Result+=' '+TJSBinaryExpression(Element).OperatorString+' ';
  179. Result+=DbgString(TJSBinaryExpression(Element).B,Indent+2);
  180. end else begin
  181. Result:='{: unknown binary Element: '+Element.Classname+':}';
  182. end;
  183. // ? :
  184. end else if Element is TJSConditionalExpression then begin
  185. Result:=DbgString(TJSConditionalExpression(Element).A,Indent+2)
  186. +'?'+DbgString(TJSConditionalExpression(Element).B,Indent+2)
  187. +':'+DbgString(TJSConditionalExpression(Element).C,Indent+2);
  188. // assignment
  189. end else if Element is TJSAssignStatement then begin
  190. Result:=DbgString(TJSAssignStatement(Element).LHS,Indent+2)
  191. +TJSAssignStatement(Element).OperatorString
  192. +DbgString(TJSAssignStatement(Element).Expr,Indent+2);
  193. // var
  194. end else if Element is TJSVarDeclaration then begin
  195. Result:=TJSVarDeclaration(Element).Name;
  196. if TJSVarDeclaration(Element).Init<>nil then
  197. Result+='='+DbgString(TJSVarDeclaration(Element).Init,Indent+2);
  198. // if(){} else {}
  199. end else if Element is TJSIfStatement then begin
  200. Result:='if('+DbgString(TJSIfStatement(Element).Cond,Indent+2)+'){'+LineEnding
  201. +Space(Indent+2)+DbgString(TJSIfStatement(Element).BTrue,Indent+2)+LineEnding
  202. +Space(Indent);
  203. if TJSIfStatement(Element).BFalse<>nil then
  204. Result+=' else {'+LineEnding
  205. +Space(Indent+2)+DbgString(TJSIfStatement(Element).BFalse,Indent+2)+LineEnding
  206. +Space(Indent)+'}';
  207. // body
  208. end else if Element is TJSBodyStatement then begin
  209. // while(){}
  210. if Element is TJSWhileStatement then begin
  211. Result:='while('+DbgString(TJSWhileStatement(Element).Cond,Indent+2)+')';
  212. if TJSWhileStatement(Element).Body<>nil then
  213. Result+=DbgString(TJSWhileStatement(Element).Body,Indent)
  214. else
  215. Result+='{}';
  216. // do{}while()
  217. end else if Element is TJSDoWhileStatement then begin
  218. Result:='do';
  219. if TJSDoWhileStatement(Element).Body<>nil then
  220. Result+=DbgString(TJSDoWhileStatement(Element).Body,Indent)
  221. else
  222. Result+='{}';
  223. Result+='('+DbgString(TJSDoWhileStatement(Element).Cond,Indent+2)+')';
  224. // for(Init;Incr;Cond)Body
  225. end else if Element is TJSForStatement then begin
  226. Result:='for(';
  227. if TJSForStatement(Element).Init<>nil then
  228. Result+=DbgString(TJSForStatement(Element).Init,Indent+2);
  229. Result+=';';
  230. if TJSForStatement(Element).Cond<>nil then
  231. Result+=DbgString(TJSForStatement(Element).Cond,Indent+2);
  232. Result+=';';
  233. if TJSForStatement(Element).Incr<>nil then
  234. Result+=DbgString(TJSForStatement(Element).Incr,Indent+2);
  235. Result+=')';
  236. if TJSForStatement(Element).Body<>nil then
  237. Result+=DbgString(TJSForStatement(Element).Body,Indent)
  238. else
  239. Result+='{}';
  240. // {}
  241. end else begin
  242. if TJSBodyStatement(Element).Body<>nil then
  243. Result+='{'+LineEnding
  244. +Space(Indent+2)+DbgString(TJSBodyStatement(Element).Body,Indent+2)+LineEnding
  245. +Space(Indent)+'}'
  246. else
  247. Result+='{}';
  248. end;
  249. end else begin
  250. Result:='{: unknown Element: '+Element.Classname+':}';
  251. end;
  252. end;
  253. function DbgAsString(Element: TJSValue; Indent: integer): string;
  254. begin
  255. if Element=nil then
  256. Result:='(no value)'
  257. else begin
  258. case Element.ValueType of
  259. jstUNDEFINED: Result:='undefined';
  260. jstNull: Result:='null';
  261. jstBoolean: Result:=BoolToStr(Element.AsBoolean,'true','false');
  262. jstNumber: str(Element.AsNumber,Result);
  263. jstString: Result:=AnsiQuotedStr(Element.AsString{%H-},'''');
  264. jstObject: Result:='{:OBJECT:}';
  265. jstReference: Result:='{:REFERENCE:}';
  266. JSTCompletion: Result:='{:COMPLETION:}';
  267. else Result:='{:Unknown ValueType '+IntToStr(ord(Element.ValueType))+':}';
  268. end;
  269. end;
  270. Result:=Space(Indent)+Result;
  271. end;
  272. function DbgAsString(Element: TJSArrayLiteralElements; Indent: integer): string;
  273. var
  274. i: Integer;
  275. begin
  276. Result:='';
  277. for i:=0 to TJSArrayLiteralElements(Element).Count-1 do begin
  278. if i>0 then Result+=',';
  279. Result+=DbgString(TJSArrayLiteralElements(Element).Elements[i].Expr,Indent+2);
  280. end;
  281. end;
  282. function DbgAsString(Element: TJSObjectLiteralElements; Indent: integer): string;
  283. var
  284. i: Integer;
  285. begin
  286. Result:='';
  287. for i:=0 to TJSObjectLiteralElements(Element).Count-1 do begin
  288. if i>0 then Result+=',';
  289. Result+=DbgString(TJSObjectLiteralElements(Element).Elements[i].Expr,Indent+2);
  290. end;
  291. end;
  292. function DbgAsString(Element: TJSObjectLiteralElement; Indent: integer): string;
  293. begin
  294. Result:=String(TJSObjectLiteralElement(Element).Name)
  295. +':'+DbgString(TJSObjectLiteralElement(Element).Expr,Indent+2);
  296. end;
  297. function DbgHexMem(p: Pointer; Count: integer): string;
  298. var
  299. i: Integer;
  300. begin
  301. Result:='';
  302. for i:=0 to Count-1 do
  303. Result:=Result+HexStr(ord(PChar(p)[i]),2);
  304. end;
  305. function DbgStr(const s: string): string;
  306. var
  307. i: Integer;
  308. c: Char;
  309. begin
  310. Result:='';
  311. for i:=1 to length(s) do begin
  312. c:=s[i];
  313. case c of
  314. #0..#31,#127..#255: Result+='$'+HexStr(ord(c),2);
  315. else Result+=c;
  316. end;
  317. end;
  318. end;
  319. { TPas2jsLogger }
  320. function TPas2jsLogger.GetMsgs(Index: integer): TPas2jsMessage;
  321. begin
  322. Result:=TPas2jsMessage(FMsg[Index]);
  323. end;
  324. function TPas2jsLogger.FindMsgNumberDisabled(MsgNumber: integer;
  325. FindInsertPos: boolean): integer;
  326. var
  327. l, r, m, CurMsgNumber: Integer;
  328. begin
  329. l:=0;
  330. r:=FMsgNumberDisabledCount-1;
  331. m:=0;
  332. while l<=r do begin
  333. m:=(l+r) div 2;
  334. CurMsgNumber:=FMsgNumberDisabled[m];
  335. if MsgNumber<CurMsgNumber then
  336. r:=m-1
  337. else if MsgNumber>CurMsgNumber then
  338. l:=m+1
  339. else
  340. exit(m);
  341. end;
  342. if FindInsertPos then begin
  343. Result:=m;
  344. if l>m then inc(Result);
  345. end else begin
  346. Result:=-1;
  347. end;
  348. end;
  349. procedure TPas2jsLogger.SetEncoding(const AValue: string);
  350. var
  351. NewValue: String;
  352. begin
  353. NewValue:=NormalizeEncoding(AValue);
  354. if FEncoding=NewValue then Exit;
  355. //LogRaw(ClassName+': Encoding changed from "'+FEncoding+'" to "'+NewValue+'"');
  356. FEncoding:=NewValue;
  357. end;
  358. function TPas2jsLogger.GetMsgNumberDisabled(MsgNumber: integer): boolean;
  359. begin
  360. Result:=FindMsgNumberDisabled(MsgNumber,false)>=0;
  361. end;
  362. procedure TPas2jsLogger.SetMsgNumberDisabled(MsgNumber: integer; AValue: boolean
  363. );
  364. var
  365. InsertPos, OldCount: Integer;
  366. begin
  367. OldCount:=FMsgNumberDisabledCount;
  368. if AValue then begin
  369. // enable
  370. InsertPos:=FindMsgNumberDisabled(MsgNumber,true);
  371. if (InsertPos<OldCount) and (FMsgNumberDisabled[InsertPos]=MsgNumber) then
  372. exit; // already disabled
  373. inc(FMsgNumberDisabledCount);
  374. ReAllocMem(FMsgNumberDisabled,SizeOf(Integer)*FMsgNumberDisabledCount);
  375. if InsertPos<OldCount then
  376. Move(FMsgNumberDisabled[InsertPos],FMsgNumberDisabled[InsertPos+1],
  377. SizeOf(Integer)*(OldCount-InsertPos));
  378. FMsgNumberDisabled[InsertPos]:=MsgNumber;
  379. end else begin
  380. // disable
  381. InsertPos:=FindMsgNumberDisabled(MsgNumber,false);
  382. if InsertPos<0 then exit;
  383. if InsertPos+1<OldCount then
  384. Move(FMsgNumberDisabled[InsertPos+1],FMsgNumberDisabled[InsertPos],
  385. SizeOf(Integer)*(OldCount-InsertPos-1));
  386. dec(FMsgNumberDisabledCount);
  387. ReAllocMem(FMsgNumberDisabled,SizeOf(Integer)*FMsgNumberDisabledCount);
  388. end;
  389. end;
  390. procedure TPas2jsLogger.SetOutputFilename(AValue: string);
  391. begin
  392. if FOutputFilename=AValue then Exit;
  393. CloseOutputFile;
  394. FOutputFilename:=AValue;
  395. if OutputFilename<>'' then
  396. OpenOutputFile;
  397. end;
  398. procedure TPas2jsLogger.SetSorted(AValue: boolean);
  399. begin
  400. if FSorted=AValue then Exit;
  401. FSorted:=AValue;
  402. if FSorted then Sort;
  403. end;
  404. constructor TPas2jsLogger.Create;
  405. begin
  406. FMsg:=TFPList.Create;
  407. FShowMsgTypes:=DefaultLogMsgTypes;
  408. end;
  409. destructor TPas2jsLogger.Destroy;
  410. var
  411. i: Integer;
  412. begin
  413. CloseOutputFile;
  414. for i:=0 to FMsg.Count-1 do
  415. TObject(FMsg[i]).Free;
  416. FreeAndNil(FMsg);
  417. ReAllocMem(FMsgNumberDisabled,0);
  418. FMsgNumberDisabledCount:=0;
  419. inherited Destroy;
  420. end;
  421. procedure TPas2jsLogger.RegisterMsg(MsgType: TMessageType; MsgNumber: integer;
  422. Pattern: string);
  423. var
  424. Msg: TPas2jsMessage;
  425. begin
  426. if MsgNumber=0 then
  427. raise Exception.Create('internal error: TPas2jsLogger.RegisterMsg MsgNumber=0');
  428. Msg:=TPas2jsMessage.Create;
  429. Msg.Number:=MsgNumber;
  430. Msg.Typ:=MsgType;
  431. Msg.Pattern:=Pattern;
  432. FMsg.Add(Msg);
  433. FSorted:=false;
  434. end;
  435. function TPas2jsLogger.GetMsgCount: integer;
  436. begin
  437. Result:=FMsg.Count;
  438. end;
  439. function TPas2jsLogger.FindMsg(MsgNumber: integer; ExceptionOnNotFound: boolean
  440. ): TPas2jsMessage;
  441. var
  442. l, r, m: Integer;
  443. Msg: TPas2jsMessage;
  444. begin
  445. if not FSorted then Sort;
  446. l:=0;
  447. r:=GetMsgCount-1;
  448. while l<=r do begin
  449. m:=(l+r) div 2;
  450. Msg:=Msgs[m];
  451. if MsgNumber<Msg.Number then
  452. r:=m-1
  453. else if MsgNumber>Msg.Number then
  454. l:=m+1
  455. else
  456. exit(Msg);
  457. end;
  458. Result:=nil;
  459. if ExceptionOnNotFound then
  460. raise Exception.Create('invalid message number '+IntToStr(MsgNumber));
  461. end;
  462. procedure TPas2jsLogger.Sort;
  463. var
  464. i: Integer;
  465. LastMsg, CurMsg: TPas2jsMessage;
  466. begin
  467. if FMsg.Count>1 then begin;
  468. FMsg.Sort(@CompareP2JMessage);
  469. // check for duplicates
  470. LastMsg:=TPas2jsMessage(FMsg[0]);
  471. for i:=1 to FMsg.Count-1 do begin
  472. CurMsg:=TPas2jsMessage(FMsg[i]);
  473. if LastMsg.Number=CurMsg.Number then
  474. raise Exception.Create('duplicate message number '+IntToStr(CurMsg.Number)+'. 1st="'+LastMsg.Pattern+'" 2nd="'+CurMsg.Pattern+'"');
  475. LastMsg:=CurMsg;
  476. end;
  477. end;
  478. FSorted:=true;
  479. end;
  480. function TPas2jsLogger.GetMsgText(MsgNumber: integer;
  481. Args: array of const): string;
  482. var
  483. Msg: TPas2jsMessage;
  484. begin
  485. Msg:=FindMsg(MsgNumber,true);
  486. Result:=MsgTypeToStr(Msg.Typ)+': '+Format(Msg.Pattern,Args);
  487. end;
  488. procedure TPas2jsLogger.LogRaw(const Msg: string);
  489. var
  490. S: String;
  491. begin
  492. S:=Msg;
  493. if Encoding='utf8' then
  494. else if Encoding='console' then
  495. S:=UTF8ToConsole(S)
  496. else if Encoding='system' then
  497. S:=UTF8ToSystemCP(S)
  498. else begin
  499. // default: write UTF-8 to outputfile and console codepage to console
  500. if FOutputFile=nil then
  501. S:=UTF8ToConsole(S);
  502. end;
  503. //writeln('TPas2jsLogger.LogRaw "',Encoding,'" "',DbgStr(S),'"');
  504. if FOnLog<>Nil then
  505. FOnLog(Self,S)
  506. else if FOutputFile<>nil then
  507. FOutputFile.Write(S+LineEnding)
  508. else begin
  509. // prevent codepage conversion magic
  510. SetCodePage(RawByteString(S), CP_OEMCP, False);
  511. writeln(S);
  512. end;
  513. end;
  514. procedure TPas2jsLogger.LogRaw(Args: array of const);
  515. var
  516. s: String;
  517. i: Integer;
  518. begin
  519. s:='';
  520. for i:=Low(Args) to High(Args) do
  521. begin
  522. case Args[i].VType of
  523. vtInteger: s += IntToStr(Args[i].VInteger);
  524. vtBoolean: s += BoolToStr(Args[i].VBoolean);
  525. vtChar: s += Args[i].VChar;
  526. {$ifndef FPUNONE}
  527. vtExtended: ; // Args[i].VExtended^;
  528. {$ENDIF}
  529. vtString: s += Args[i].VString^;
  530. vtPointer: ; // Args[i].VPointer;
  531. vtPChar: s += Args[i].VPChar;
  532. vtObject: ; // Args[i].VObject;
  533. vtClass: ; // Args[i].VClass;
  534. vtWideChar: s += AnsiString(Args[i].VWideChar);
  535. vtPWideChar: s += AnsiString(Args[i].VPWideChar);
  536. vtAnsiString: s += AnsiString(Args[i].VAnsiString);
  537. vtCurrency: ; // Args[i].VCurrency^);
  538. vtVariant: ; // Args[i].VVariant^);
  539. vtInterface: ; // Args[i].VInterface^);
  540. vtWidestring: s += AnsiString(WideString(Args[i].VWideString));
  541. vtInt64: s += IntToStr(Args[i].VInt64^);
  542. vtQWord: s += IntToStr(Args[i].VQWord^);
  543. vtUnicodeString:s += AnsiString(UnicodeString(Args[i].VUnicodeString));
  544. end;
  545. end;
  546. LogRaw(s);
  547. end;
  548. procedure TPas2jsLogger.LogLn;
  549. begin
  550. LogRaw('');
  551. end;
  552. procedure TPas2jsLogger.LogMsg(MsgNumber: integer; Args: array of const;
  553. const Filename: string; Line: integer; Col: integer; UseFilter: boolean);
  554. var
  555. s: String;
  556. Msg: TPas2jsMessage;
  557. begin
  558. Msg:=FindMsg(MsgNumber,true);
  559. if UseFilter and not (Msg.Typ in FShowMsgTypes) then exit;
  560. if MsgNumberDisabled[MsgNumber] then exit;
  561. s:=FormatMsg(Msg.Typ,SafeFormat(Msg.Pattern,Args),MsgNumber,Filename,Line,Col);
  562. LogRaw(s);
  563. end;
  564. procedure TPas2jsLogger.LogMsgIgnoreFilter(MsgNumber: integer;
  565. Args: array of const);
  566. begin
  567. LogMsg(MsgNumber,Args,'',0,0,false);
  568. end;
  569. function TPas2jsLogger.MsgTypeToStr(MsgType: TMessageType): string;
  570. begin
  571. case MsgType of
  572. mtFatal: Result:='Fatal';
  573. mtError: Result:='Error';
  574. mtWarning: Result:='Warning';
  575. mtNote: Result:='Note';
  576. mtHint: Result:='Hint';
  577. mtInfo: Result:='Info';
  578. mtDebug: Result:='Debug';
  579. else Result:='Verbose';
  580. end;
  581. end;
  582. procedure TPas2jsLogger.Log(MsgType: TMessageType; Msg: string;
  583. MsgNumber: integer; const Filename: string; Line: integer; Col: integer;
  584. UseFilter: boolean);
  585. var
  586. s: String;
  587. begin
  588. if UseFilter and not (MsgType in FShowMsgTypes) then exit;
  589. if MsgNumberDisabled[MsgNumber] then exit;
  590. s:=FormatMsg(MsgType,Msg,MsgNumber,Filename,Line,Col);
  591. LogRaw(s);
  592. end;
  593. function TPas2jsLogger.FormatMsg(MsgType: TMessageType; Msg: string;
  594. MsgNumber: integer; const Filename: string; Line: integer; Col: integer
  595. ): string;
  596. // e.g. file(line,col) type: (number) msg
  597. var
  598. s: String;
  599. begin
  600. s:='';
  601. if Filename<>'' then begin
  602. if Assigned(OnFormatPath) then
  603. s+=OnFormatPath(Filename)
  604. else
  605. s+=Filename;
  606. if Line>0 then begin
  607. s+='('+IntToStr(Line);
  608. if Col>0 then s+=','+IntToStr(Col);
  609. s+=')';
  610. end;
  611. if s<>'' then s+=' ';
  612. end;
  613. s+=MsgTypeToStr(MsgType)+': ';
  614. if ShowMsgNumbers and (MsgNumber<>0) then
  615. s+='('+IntToStr(MsgNumber)+') ';
  616. s+=Msg;
  617. Result:=s;
  618. end;
  619. procedure TPas2jsLogger.OpenOutputFile;
  620. begin
  621. if FOutputFile<>nil then exit;
  622. if OutputFilename='' then
  623. raise Exception.Create('Log has empty OutputFilename');
  624. if DirectoryExists(OutputFilename) then
  625. raise Exception.Create('Log is directory: "'+OutputFilename+'"');
  626. FOutputFile:=TFileWriter.Create(OutputFilename);
  627. if (Encoding='') or (Encoding='utf8') then
  628. FOutputFile.Write(UTF8BOM);
  629. end;
  630. procedure TPas2jsLogger.Flush;
  631. begin
  632. if FOutputFile<>nil then
  633. FOutputFile.Flush;
  634. end;
  635. procedure TPas2jsLogger.CloseOutputFile;
  636. begin
  637. if FOutputFile=nil then exit;
  638. FOutputFile.Flush;
  639. FreeAndNil(FOutputFile);
  640. end;
  641. procedure TPas2jsLogger.Reset;
  642. begin
  643. OutputFilename:='';
  644. if FMsgNumberDisabled<>nil then begin
  645. ReAllocMem(FMsgNumberDisabled,0);
  646. FMsgNumberDisabledCount:=0;
  647. end;
  648. ShowMsgNumbers:=false;
  649. FShowMsgTypes:=DefaultLogMsgTypes;
  650. end;
  651. end.