Răsfoiți Sursa

pas2js: logger adapted for pas2js

git-svn-id: trunk@40041 -
Mattias Gaertner 6 ani în urmă
părinte
comite
a089496183

+ 1 - 0
.gitattributes

@@ -6995,6 +6995,7 @@ packages/pastojs/src/pas2jscompiler.pp svneol=native#text/plain
 packages/pastojs/src/pas2jsfilecache.pp svneol=native#text/plain
 packages/pastojs/src/pas2jsfiler.pp svneol=native#text/plain
 packages/pastojs/src/pas2jsfileutils.pp svneol=native#text/plain
+packages/pastojs/src/pas2jsfileutilsnodejs.inc svneol=native#text/plain
 packages/pastojs/src/pas2jsfileutilsunix.inc svneol=native#text/plain
 packages/pastojs/src/pas2jsfileutilswin.inc svneol=native#text/plain
 packages/pastojs/src/pas2jslibcompiler.pp svneol=native#text/plain

+ 7 - 1
packages/pastojs/src/pas2js_defines.inc

@@ -1,5 +1,6 @@
 
 {$inline on}
+
 {$IFDEF Windows}
   {$define CaseInsensitiveFilenames}
   {$define HasUNCPaths}
@@ -11,6 +12,11 @@
   {$DEFINE NotLiteralFilenames} // e.g. HFS+ normalizes file names
 {$ENDIF}
 
-{$DEFINE UTF8_RTL}
+{$IFDEF FPC}
+  {$DEFINE UsePChar}
+  {$DEFINE HasInt64}
+  {$DEFINE UTF8_RTL}
+  {$DEFINE HasStdErr}
+{$ENDIF}
 
 

+ 4 - 4
packages/pastojs/src/pas2jscompiler.pp

@@ -2975,7 +2975,7 @@ var
   aFilename: String;
 begin
   // first try HOME directory
-  aFilename:=ChompPathDelim(GetEnvironmentVariableUTF8('HOME'));
+  aFilename:=ChompPathDelim(GetEnvironmentVariablePJ('HOME'));
   if aFilename<>'' then
     begin
     aFilename:=aFilename+PathDelim{$IFDEF UNIX}+'.'{$ENDIF}+DefaultConfigFile;
@@ -3716,7 +3716,7 @@ begin
   RegisterMessages;
 
   FFileCache:=TPas2jsFilesCache.Create(Log);
-  FFileCache.BaseDirectory:=GetCurrentDirUTF8;
+  FFileCache.BaseDirectory:=GetCurrentDirPJ;
   FFileCacheAutoFree:=true;
   FDirectoryCache:=FFileCache.DirectoryCache;
   FLog.OnFormatPath:[email protected];
@@ -3793,7 +3793,7 @@ function TPas2jsCompiler.OnMacroEnv(Sender: TObject; var Params: string;
   Lvl: integer): boolean;
 begin
   if Lvl=0 then ;
-  Params:=GetEnvironmentVariableUTF8(Params);
+  Params:=GetEnvironmentVariablePJ(Params);
   Result:=true;
 end;
 
@@ -4428,7 +4428,7 @@ end;
 
 function TPas2jsCompiler.ExpandFileName(const Filename: string): string;
 begin
-  Result:=ExpandFileNameUTF8(Filename,FileCache.BaseDirectory);
+  Result:=ExpandFileNamePJ(Filename,FileCache.BaseDirectory);
 end;
 
 end.

+ 4 - 4
packages/pastojs/src/pas2jsfilecache.pp

@@ -1425,7 +1425,7 @@ begin
 
   if ExtractFilePath(aFilename)<>'' then
     begin
-    Result:=ExpandFileNameUTF8(aFilename,BaseDirectory);
+    Result:=ExpandFileNamePJ(aFilename,BaseDirectory);
     if not FileExistsLogged(Result) then
       Result:='';
     exit;
@@ -1993,7 +1993,7 @@ begin
   if ExtractFilename(Result)='' then
     if RaiseOnError then
       raise EFileNotFoundError.Create('invalid file name "'+Filename+'"');
-  Result:=ExpandFileNameUTF8(Result,BaseDirectory);
+  Result:=ExpandFileNamePJ(Result,BaseDirectory);
   if (ExtractFilename(Result)='') or not FilenameIsAbsolute(Result) then
     if RaiseOnError then
       raise EFileNotFoundError.Create('invalid file name "'+Filename+'"');
@@ -2128,9 +2128,9 @@ function TPas2jsFilesCache.ExpandDirectory(const Filename, BaseDir: string
 begin
   if Filename='' then exit('');
   if BaseDir<>'' then
-    Result:=ExpandFileNameUTF8(Filename,BaseDir)
+    Result:=ExpandFileNamePJ(Filename,BaseDir)
   else
-    Result:=ExpandFileNameUTF8(Filename,BaseDirectory);
+    Result:=ExpandFileNamePJ(Filename,BaseDirectory);
   if Result='' then exit;
   Result:=IncludeTrailingPathDelimiter(Result);
 end;

+ 45 - 370
packages/pastojs/src/pas2jsfileutils.pp

@@ -28,6 +28,9 @@ uses
   {$IFDEF Unix}
   BaseUnix,
   {$ENDIF}
+  {$IFDEF Pas2JS}
+  NodeJSFS,
+  {$ENDIF}
   SysUtils, Classes;
 
 function FilenameIsAbsolute(const aFilename: string):boolean;
@@ -35,7 +38,7 @@ function FilenameIsWinAbsolute(const aFilename: string):boolean;
 function FilenameIsUnixAbsolute(const aFilename: string):boolean;
 function FileIsInPath(const Filename, Path: string): boolean;
 function ChompPathDelim(const Path: string): string;
-function ExpandFileNameUTF8(const FileName: string; {const} BaseDir: string = ''): string;
+function ExpandFileNamePJ(const FileName: string; {const} BaseDir: string = ''): string;
 function ExpandDirectory(const aDirectory: string): string;
 function TryCreateRelativePath(const Filename, BaseDirectory: String;
   UsePointDirectory: boolean; out RelPath: String): Boolean;
@@ -43,7 +46,7 @@ function ResolveDots(const AFilename: string): string;
 procedure ForcePathDelims(Var FileName: string);
 function GetForcedPathDelims(Const FileName: string): String;
 function ExtractFilenameOnly(const aFilename: string): string;
-function GetCurrentDirUTF8: String;
+function GetCurrentDirPJ: String;
 function CompareFilenames(const File1, File2: string): integer;
 
 function GetPhysicalFilename(const Filename: string;
@@ -53,9 +56,9 @@ function ResolveSymLinks(const Filename: string;
 function MatchGlobbing(Mask, Name: string): boolean;
 function FileIsWritable(const AFilename: string): boolean;
 
-function GetEnvironmentVariableCountUTF8: Integer;
-function GetEnvironmentStringUTF8(Index: Integer): string;
-function GetEnvironmentVariableUTF8(const EnvVar: string): String;
+function GetEnvironmentVariableCountPJ: Integer;
+function GetEnvironmentStringPJ(Index: Integer): string;
+function GetEnvironmentVariablePJ(const EnvVar: string): String;
 
 function GetNextDelimitedItem(const List: string; Delimiter: char;
                               var Position: integer): string;
@@ -64,6 +67,7 @@ type TChangeStamp = SizeInt;
 const InvalidChangeStamp = low(TChangeStamp);
 procedure IncreaseChangeStamp(var Stamp: TChangeStamp);
 
+{$IFDEF FPC_HAS_CPSTRING}
 const
   UTF8BOM = #$EF#$BB#$BF;
   EncodingUTF8 = 'UTF-8';
@@ -92,6 +96,7 @@ function SystemCPToUTF8(const s: string): string;
 function ConsoleToUTF8(const s: string): string;
 // converts UTF8 string to console encoding (used by Write, WriteLn)
 function UTF8ToConsole(const s: string): string;
+{$ENDIF FPC_HAS_CPSTRING}
 
 implementation
 
@@ -99,6 +104,7 @@ implementation
 uses Windows;
 {$ENDIF}
 
+{$IFDEF FPC_HAS_CPSTRING}
 var
   EncodingValid: boolean = false;
   DefaultTextEncoding: string = EncodingSystem;
@@ -108,11 +114,12 @@ var
   {$ENDIF}
   {$ENDIF}
   NonUTF8System: boolean = false;
+{$ENDIF}
 
 function FilenameIsWinAbsolute(const aFilename: string): boolean;
 begin
   Result:=((length(aFilename)>=3) and
-           (aFilename[1] in ['A'..'Z','a'..'z']) and (aFilename[2]=':')  and (aFilename[3]in AllowDirectorySeparators))
+           (aFilename[1] in ['A'..'Z','a'..'z']) and (aFilename[2]=':') and (aFilename[3]in AllowDirectorySeparators))
       or ((length(aFilename)>=2) and (aFilename[1] in AllowDirectorySeparators) and (aFilename[2] in AllowDirectorySeparators));
 end;
 
@@ -136,7 +143,7 @@ begin
   ExpPath:=IncludeTrailingPathDelimiter(Path);
   l:=length(ExpPath);
   Result:=(l>0) and (length(ExpFile)>l) and (ExpFile[l]=PathDelim)
-          and (AnsiCompareFileName(ExpPath,LeftStr(ExpFile,l))=0);
+          and (CompareFileNames(ExpPath,LeftStr(ExpFile,l))=0);
 end;
 
 function ChompPathDelim(const Path: string): string;
@@ -174,7 +181,7 @@ function ExpandDirectory(const aDirectory: string): string;
 begin
   Result:=aDirectory;
   if Result='' then exit;
-  Result:=ExpandFileNameUTF8(Result);
+  Result:=ExpandFileNamePJ(Result);
   if Result='' then exit;
   Result:=IncludeTrailingPathDelimiter(Result);
 end;
@@ -207,329 +214,44 @@ function TryCreateRelativePath(const Filename, BaseDirectory: String;
   - Filename = foo/bar BaseDirectory = bar/foo Result = False (no shared base directory)
   - Filename = /foo BaseDirectory = bar Result = False (mixed absolute and relative)
 }
-
-  function IsNameChar(c: char): boolean; inline;
-  begin
-    Result:=(c<>#0) and not (c in AllowDirectorySeparators);
-  end;
-
-var
-  UpDirCount: Integer;
-  ResultPos: Integer;
-  i: Integer;
-  FileNameRestLen, SharedDirs: Integer;
-  FileP, BaseP, FileEndP, BaseEndP: PChar;
 begin
   Result:=false;
   RelPath:=Filename;
   if (BaseDirectory='') or (Filename='') then exit;
-  // check for different windows file drives
-  if (CompareText(ExtractFileDrive(Filename),
-                     ExtractFileDrive(BaseDirectory))<>0)
-  then
-    exit;
-
-  FileP:=PChar(Filename);
-  BaseP:=PChar(BaseDirectory);
-
-  //writeln('TryCreateRelativePath START File="',FileP,'" Base="',BaseP,'"');
-
-  // skip matching directories
-  SharedDirs:=0;
-  if FileP^ in AllowDirectorySeparators then
-  begin
-    if not (BaseP^ in AllowDirectorySeparators) then exit;
-    repeat
-      while FileP^ in AllowDirectorySeparators do inc(FileP);
-      while BaseP^ in AllowDirectorySeparators do inc(BaseP);
-      if (FileP^=#0) or (BaseP^=#0) then break;
-      //writeln('TryCreateRelativePath check match .. File="',FileP,'" Base="',BaseP,'"');
-      FileEndP:=FileP;
-      BaseEndP:=BaseP;
-      while IsNameChar(FileEndP^) do inc(FileEndP);
-      while IsNameChar(BaseEndP^) do inc(BaseEndP);
-      if CompareFilenames(copy(Filename,FileP-PChar(Filename)+1,FileEndP-FileP),
-        copy(BaseDirectory,BaseP-PChar(BaseDirectory)+1,BaseEndP-BaseP))<>0
-      then
-        break;
-      FileP:=FileEndP;
-      BaseP:=BaseEndP;
-      inc(SharedDirs);
-    until false;
-  end else if (BaseP^ in AllowDirectorySeparators) then
-    exit;
-
-  //writeln('TryCreateRelativePath skipped matches File="',FileP,'" Base="',BaseP,'"');
-  if SharedDirs=0 then exit;
-
-  // calculate needed '../'
-  UpDirCount:=0;
-  BaseEndP:=BaseP;
-  while IsNameChar(BaseEndP^) do begin
-    inc(UpDirCount);
-    while IsNameChar(BaseEndP^) do inc(BaseEndP);
-    while BaseEndP^ in AllowDirectorySeparators do inc(BaseEndP);
-  end;
-
-  //writeln('TryCreateRelativePath UpDirCount=',UpDirCount,' File="',FileP,'" Base="',BaseP,'"');
-  // create relative filename
-  if (FileP^=#0) and (UpDirCount=0) then
-  begin
-    // Filename is the BaseDirectory
-    if UsePointDirectory then
-      RelPath:='.'
-    else
-      RelPath:='';
-    exit(true);
-  end;
-
-  FileNameRestLen:=length(Filename)-(FileP-PChar(Filename));
-  SetLength(RelPath,3*UpDirCount+FileNameRestLen);
-  ResultPos:=1;
-  for i:=1 to UpDirCount do begin
-    RelPath[ResultPos]:='.';
-    RelPath[ResultPos+1]:='.';
-    RelPath[ResultPos+2]:=PathDelim;
-    inc(ResultPos,3);
-  end;
-  if FileNameRestLen>0 then
-    Move(FileP^,RelPath[ResultPos],FileNameRestLen);
-  Result:=true;
+  writeln('TryCreateRelativePath ToDo: ',Filename,' Base=',BaseDirectory,' UsePointDirectory=',UsePointDirectory);
 end;
 
 function ResolveDots(const AFilename: string): string;
 //trim double path delims and expand special dirs like .. and .
 //on Windows change also '/' to '\' except for filenames starting with '\\?\'
-
-  {$ifdef windows}
-  function IsDriveDelim(const Path: string; p: integer): boolean; inline;
-  begin
-    Result:=(p=2) and (Path[2]=DriveDelim) and (Path[1] in ['a'..'z','A'..'Z']);
-  end;
-  {$endif}
-
-  function IsPathDelim(const Path: string; p: integer): boolean;
-  begin
-    if (p<=0) or (Path[p]=PathDelim) then exit(true);
-    {$ifdef windows}
-    if IsDriveDelim(Path,p) then
-      exit(true);
-    {$endif}
-    Result:=false;
-  end;
-
-var SrcPos, DestPos, Len, DirStart: integer;
-  c: char;
-  MacroPos: LongInt;
+var
+  Len: Integer;
 begin
   Len:=length(AFilename);
   if Len=0 then exit('');
 
   Result:=AFilename;
-
-  {$ifdef windows}
-  //Special case: everything is literal after this, even dots (this does not apply to '//?/')
-  if (length(AFilename)>=4) and (AFilename[1]='\') and (AFilename[2]='\')
-  and (AFilename[3]='?') and (AFilename[4]='\') then
-    exit;
-  {$endif}
-
-  SrcPos:=1;
-  DestPos:=1;
-
-  // trim double path delimiters and special dirs . and ..
-  while (SrcPos<=Len) do begin
-    c:=AFilename[SrcPos];
-    {$ifdef windows}
-    //change / to \. The WinApi accepts both, but it leads to strange effects in other places
-    if (c in AllowDirectorySeparators) then c := PathDelim;
-    {$endif}
-    // check for duplicate path delims
-    if (c=PathDelim) then
-    begin
-      inc(SrcPos);
-      {$IFDEF Windows}
-      if (DestPos>2)
-      {$ELSE}
-      if (DestPos>1)
-      {$ENDIF}
-      and (Result[DestPos-1]=PathDelim) then
-      begin
-        // skip duplicate PathDelim
-        continue;
-      end;
-      Result[DestPos]:=c;
-      inc(DestPos);
-      continue;
-    end;
-    // check for special dirs . and ..
-    if (c='.') then
-    begin
-      if (SrcPos<Len) then
-      begin
-        if (AFilename[SrcPos+1] in AllowDirectorySeparators)
-        and IsPathDelim(Result,DestPos-1) then
-        begin
-          // special dir ./ or */./
-          // -> skip
-          inc(SrcPos,2);
-          while (SrcPos<=Len) and (AFilename[SrcPos] in AllowDirectorySeparators) do
-            inc(SrcPos);
-          continue;
-        end else if (AFilename[SrcPos+1]='.')
-        and ((SrcPos+1=Len) or (AFilename[SrcPos+2] in AllowDirectorySeparators)) then
-        begin
-          // special dir ..
-          //  1. ..      -> copy
-          //  2. /..     -> skip .., keep /
-          //  3. C:..    -> copy
-          //  4. C:\..   -> skip .., keep C:\
-          //  5. \\..    -> skip .., keep \\
-          //  6. ../..   -> copy because if the first '..' was not resolved, the next can't neither
-          //  7. dir/..  -> trim dir and ..
-          //  8. dir$macro/..  -> copy
-          if DestPos=1 then
-          begin
-            //  1. .. or ../  -> copy
-          end else if (DestPos=2) and (Result[1]=PathDelim) then
-          begin
-            //  2. /..     -> skip .., keep /
-            inc(SrcPos,2);
-            continue;
-          {$IFDEF Windows}
-          end else if (DestPos=3) and IsDriveDelim(Result,2) then
-          begin
-            //  3. C:..    -> copy
-          end else if (DestPos=4) and (Result[3]=PathDelim)
-          and IsDriveDelim(Result,2) then
-          begin
-            //  4. C:\..   -> skip .., keep C:\
-            inc(SrcPos,2);
-            continue;
-          end else if (DestPos=3) and (Result[1]=PathDelim)
-          and (Result[2]=PathDelim) then
-          begin
-            //  5. \\..    -> skip .., keep \\
-            inc(SrcPos,2);
-            continue;
-          {$ENDIF}
-          end else if (DestPos>1) and (Result[DestPos-1]=PathDelim) then
-          begin
-            // */.
-            if (DestPos>3)
-            and (Result[DestPos-2]='.') and (Result[DestPos-3]='.')
-            and IsPathDelim(Result,DestPos-4) then
-            begin
-              //  6. ../..   -> copy because if the first '..' was not resolved, the next can't neither
-            end else begin
-              //  7. xxxdir/..  -> trim dir and skip ..
-              DirStart:=DestPos-2;
-              while (DirStart>1) and (Result[DirStart-1]<>PathDelim) do
-                dec(DirStart);
-              {$ifdef windows}
-              if (DirStart=1) and IsDriveDelim(Result,2) then
-                inc(DirStart,2);
-              {$endif}
-              MacroPos:=DirStart;
-              while MacroPos<DestPos do begin
-                if (Result[MacroPos]='$')
-                and (Result[MacroPos+1] in ['(','a'..'z','A'..'Z']) then
-                begin
-                  // 8. directory contains a macro -> keep
-                  break;
-                end;
-                inc(MacroPos);
-              end;
-              if MacroPos=DestPos then
-              begin
-                // previous directory does not contain a macro -> remove dir/..
-                DestPos:=DirStart;
-                inc(SrcPos,2);
-                //writeln('ResolveDots ',DestPos,' SrcPos=',SrcPos,' File="',AFilename,'" Result="',copy(Result,1,DestPos-1),'"');
-                if SrcPos>Len then
-                begin
-                  // '..' at end of filename
-                  if (DestPos>1) and (Result[DestPos-1]=PathDelim) then
-                  begin
-                    // foo/dir/.. -> foo
-                    dec(DestPos);
-                  end else if (DestPos=1) then
-                  begin
-                    // foo/.. -> .
-                    Result[1]:='.';
-                    DestPos:=2;
-                  end;
-                end else if DestPos=1 then
-                begin
-                  // e.g. 'foo/../'
-                  while (SrcPos<=Len) and (AFilename[SrcPos] in AllowDirectorySeparators) do
-                    inc(SrcPos);
-                end;
-                continue;
-              end;
-            end;
-          end;
-        end;
-      end else begin
-        // special dir . at end of filename
-        if DestPos=1 then
-        begin
-          Result:='.';
-          exit;
-        end;
-        if (DestPos>2) and (Result[DestPos-1]=PathDelim)
-        {$ifdef windows}
-        and not IsDriveDelim(Result,DestPos-2)
-        {$endif}
-        then begin
-          // foo/. -> foo
-          // C:foo\. -> C:foo
-          // C:\. -> C:\
-          dec(DestPos);
-        end;
-        break;
-      end;
-    end;
-    // copy directory
-    repeat
-      Result[DestPos]:=c;
-      inc(DestPos);
-      inc(SrcPos);
-      if (SrcPos>Len) then break;
-      c:=AFilename[SrcPos];
-      {$ifdef windows}
-      //change / to \. The WinApi accepts both, but it leads to strange effects in other places
-      if (c in AllowDirectorySeparators) then c := PathDelim;
-      {$endif}
-      if c=PathDelim then break;
-    until false;
-  end;
-  // trim result
-  if DestPos<=length(AFilename) then
-    if (DestPos=1) then
-      Result:='.'
-    else
-      SetLength(Result,DestPos-1);
+  writeln('ResolveDots ToDo ',AFilename);
 end;
 
 procedure ForcePathDelims(Var FileName: string);
-var
-  i: Integer;
 begin
-  for i:=1 to length(FileName) do
-    {$IFDEF Windows}
-    if Filename[i]='/' then
-      Filename[i]:='\';
-    {$ELSE}
-    if Filename[i]='\' then
-      Filename[i]:='/';
-    {$ENDIF}
+  Filename:=GetForcedPathDelims(Filename);
 end;
 
 function GetForcedPathDelims(const FileName: string): String;
+var
+  i: Integer;
+  c: Char;
 begin
-  Result:=FileName;
-  ForcePathDelims(Result);
+  Result:=Filename;
+  if PathDelim='/' then
+    c:='\'
+  else
+    c:='/';
+  for i:=1 to length(Result) do
+    if Result[i]=c then
+      Result[i]:=PathDelim;
 end;
 
 function ExtractFilenameOnly(const aFilename: string): string;
@@ -552,73 +274,16 @@ end;
 
 function CompareFilenames(const File1, File2: string): integer;
 begin
-  Result:=AnsiCompareFileName(File1,File2);
+  writeln('CompareFilenames ToDo ',File1,' ',File2);
+  Result:=0;
 end;
 
 function MatchGlobbing(Mask, Name: string): boolean;
 // match * and ?
-
-  function IsNameEnd(NameP: PChar): boolean; inline;
-  begin
-    Result:=(NameP^=#0) and (NameP-PChar(Name)=length(Name));
-  end;
-
-  function Check(MaskP, NameP: PChar): boolean;
-  var
-    c: Integer;
-  begin
-    repeat
-      case MaskP^ of
-      #0:
-        exit(IsNameEnd(NameP));
-      '?':
-        if not IsNameEnd(NameP) then
-        begin
-          inc(MaskP);
-          c:=UTF8CharacterStrictLength(NameP);
-          if c<1 then c:=1;
-          inc(NameP,c);
-        end else
-          exit(false);
-      '*':
-        begin
-          repeat
-            inc(MaskP);
-          until MaskP^<>'*';
-          if MaskP=#0 then exit(true);
-          while not IsNameEnd(NameP) do begin
-            inc(NameP);
-            if Check(MaskP,NameP) then exit(true);
-          end;
-          exit(false);
-        end;
-      else
-        if NameP^<>MaskP^ then exit(false);
-        c:=UTF8CharacterStrictLength(MaskP);
-        if c<1 then c:=1;
-        inc(MaskP);
-        c:=UTF8CharacterStrictLength(NameP);
-        if c<1 then c:=1;
-        inc(NameP,c);
-      end;
-    until false;
-  end;
-
-var
-  MaskP: PChar;
 begin
   if Mask='' then exit(Name='');
-  {$IFDEF CaseInsensitiveFilenames}
-  Mask:=AnsiLowerCase(Mask);
-  Name:=AnsiLowerCase(Name);
-  {$ENDIF}
-  MaskP:=PChar(Mask);
-  while (MaskP^='*') and (MaskP[1]='*') do inc(MaskP);
-  if (MaskP^='*') and (MaskP[1]=#0) then
-    exit(true); // the * mask fits all, even the empty string
-  if Name='' then
-    exit(false);
-  Result:=Check(MaskP,PChar(Name));
+  writeln('MatchGlobbing ToDo ',Mask,' Name=',Name);
+  Result:=false;
 end;
 
 function GetNextDelimitedItem(const List: string; Delimiter: char;
@@ -641,6 +306,7 @@ begin
     Stamp:=InvalidChangeStamp+1;
 end;
 
+{$IFDEF FPC_HAS_CPSTRING}
 function IsNonUTF8System: boolean;
 begin
   Result:=NonUTF8System;
@@ -756,6 +422,7 @@ begin
   // conversion magic
   SetCodePage(RawByteString(Result), CP_ACP, False);
 end;
+{$ENDIF}
 
 {$IFDEF Unix}
   {$I pas2jsfileutilsunix.inc}
@@ -763,9 +430,13 @@ end;
 {$IFDEF Windows}
   {$I pas2jsfileutilswin.inc}
 {$ENDIF}
+{$IFDEF NodeJS}
+  {$I pas2jsfileutilsnodejs.inc}
+{$ENDIF}
 
 procedure InternalInit;
 begin
+  {$IFDEF FPC_HAS_CPSTRING}
   SetMultiByteConversionCodePage(CP_UTF8);
   // SetMultiByteFileSystemCodePage(CP_UTF8); not needed, this is the default under Windows
   SetMultiByteRTLFileSystemCodePage(CP_UTF8);
@@ -776,12 +447,16 @@ begin
   {$ELSE}
   NonUTF8System:=SysUtils.CompareText(DefaultTextEncoding,'UTF8')<>0;
   {$ENDIF}
+  {$ENDIF}
+
   InitPlatform;
 end;
 
 initialization
   InternalInit;
+{$IFDEF FPC}
 finalization
   FinalizePlatform;
+{$ENDIF}
 end.
 

+ 130 - 0
packages/pastojs/src/pas2jsfileutilsnodejs.inc

@@ -0,0 +1,130 @@
+{%MainUnit pas2jsfileutils.pas}
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2018  Mattias Gaertner  [email protected]
+
+    NodeJS backend of pas2jsfileutils
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************
+}
+
+function FilenameIsAbsolute(const aFilename: string): boolean;
+begin
+  writeln('FilenameIsAbsolute ToDo ',aFilename);
+  Result:=FilenameIsUnixAbsolute(aFilename);
+end;
+
+function ExpandFileNamePJ(const FileName: string; BaseDir: string): string;
+var
+  IsAbs: Boolean;
+  HomeDir, Fn: String;
+begin
+  Fn := FileName;
+  ForcePathDelims(Fn);
+  IsAbs := FileNameIsUnixAbsolute(Fn);
+  if (not IsAbs) then
+  begin
+    if ((Length(Fn) > 1) and (Fn[1] = '~') and (Fn[2] = '/')) or (Fn = '~') then
+    begin
+      HomeDir := GetEnvironmentVariablePJ('HOME');
+      if not FileNameIsUnixAbsolute(HomeDir) then
+        HomeDir := ExpandFileNamePJ(HomeDir,'');
+      Fn := HomeDir + Copy(Fn,2,length(Fn));
+      IsAbs := True;
+    end;
+  end;
+  if IsAbs then
+  begin
+    Result := ResolveDots(Fn);
+  end
+  else
+  begin
+    if (BaseDir = '') then
+      Fn := IncludeTrailingPathDelimiter(GetCurrentDirPJ) + Fn
+    else
+      Fn := IncludeTrailingPathDelimiter(BaseDir) + Fn;
+    Fn := ResolveDots(Fn);
+    //if BaseDir is not absolute then this needs to be expanded as well
+    if not FileNameIsUnixAbsolute(Fn) then
+      Fn := ExpandFileNamePJ(Fn, '');
+    Result := Fn;
+  end;
+end;
+
+function GetCurrentDirPJ: String;
+begin
+  writeln('GetCurrentDirPJ ToDo');
+  Result:='';
+end;
+
+function GetPhysicalFilename(const Filename: string; ExceptionOnError: boolean
+  ): string;
+var
+  OldPath, NewPath: String;
+  p, l: integer;
+begin
+  Result:=Filename;
+  p:=1;
+  l:=length(Result);
+  while p<=l do
+  begin
+    while (p<=l) and (Result[p]='/') do
+      inc(p);
+    if p>l then exit;
+    if Result[p]<>'/' then
+    begin
+      repeat
+        inc(p);
+      until (p>l) or (Result[p]='/');
+      OldPath:=LeftStr(Result,p-1);
+      NewPath:=ResolveSymLinks(OldPath,ExceptionOnError);
+      if NewPath='' then exit('');
+      if OldPath<>NewPath then
+      begin
+        Result:=NewPath+copy(Result,length(OldPath)+1,length(Result));
+        p:=length(NewPath)+1;
+      end;
+    end;
+  end;
+end;
+
+function ResolveSymLinks(const Filename: string; ExceptionOnError: boolean
+  ): string;
+begin
+  writeln('ResolveSymLinks ToDo ',Filename,' ',ExceptionOnError);
+  Result:=Filename;
+end;
+
+function FileIsWritable(const AFilename: string): boolean;
+begin
+  writeln('FileIsWritable ToDo ',AFilename);
+  Result := false;
+end;
+
+function GetEnvironmentVariableCountPJ: Integer;
+begin
+  Result:=GetEnvironmentVariableCount;
+end;
+
+function GetEnvironmentStringPJ(Index: Integer): string;
+begin
+  Result:=GetEnvironmentString(Index);
+end;
+
+function GetEnvironmentVariablePJ(const EnvVar: string): String;
+begin
+  Result:=GetEnvironmentVariable(EnvVar);
+end;
+
+procedure InitPlatform;
+begin
+
+end;
+

+ 11 - 10
packages/pastojs/src/pas2jsfileutilsunix.inc

@@ -3,7 +3,7 @@
     This file is part of the Free Component Library (FCL)
     Copyright (c) 2018  Mattias Gaertner  [email protected]
 
-    Pascal to Javascript converter class.
+    Unix backend of pas2jsfileutils
 
     See the file COPYING.FPC, included in this distribution,
     for details about the copyright.
@@ -20,7 +20,7 @@ begin
   Result:=FilenameIsUnixAbsolute(aFilename);
 end;
 
-function ExpandFileNameUTF8(const FileName: string; BaseDir: string): string;
+function ExpandFileNamePJ(const FileName: string; BaseDir: string): string;
 var
   IsAbs: Boolean;
   HomeDir, Fn: String;
@@ -32,9 +32,9 @@ begin
   begin
     if ((Length(Fn) > 1) and (Fn[1] = '~') and (Fn[2] = '/')) or (Fn = '~') then
     begin
-      HomeDir := GetEnvironmentVariableUTF8('HOME');
+      HomeDir := GetEnvironmentVariablePJ('HOME');
       if not FileNameIsUnixAbsolute(HomeDir) then
-        HomeDir := ExpandFileNameUtf8(HomeDir,'');
+        HomeDir := ExpandFileNamePJ(HomeDir,'');
       Fn := HomeDir + Copy(Fn,2,length(Fn));
       IsAbs := True;
     end;
@@ -46,18 +46,18 @@ begin
   else
   begin
     if (BaseDir = '') then
-      Fn := IncludeTrailingPathDelimiter(GetCurrentDirUtf8) + Fn
+      Fn := IncludeTrailingPathDelimiter(GetCurrentDirPJ) + Fn
     else
       Fn := IncludeTrailingPathDelimiter(BaseDir) + Fn;
     Fn := ResolveDots(Fn);
     //if BaseDir is not absolute then this needs to be expanded as well
     if not FileNameIsUnixAbsolute(Fn) then
-      Fn := ExpandFileNameUtf8(Fn, '');
+      Fn := ExpandFileNamePJ(Fn, '');
     Result := Fn;
   end;
 end;
 
-function GetCurrentDirUTF8: String;
+function GetCurrentDirPJ: String;
 begin
   Result:=GetCurrentDir;
 end;
@@ -148,17 +148,17 @@ begin
   Result := BaseUnix.FpAccess(AFilename, BaseUnix.W_OK) = 0;
 end;
 
-function GetEnvironmentVariableCountUTF8: Integer;
+function GetEnvironmentVariableCountPJ: Integer;
 begin
   Result:=GetEnvironmentVariableCount;
 end;
 
-function GetEnvironmentStringUTF8(Index: Integer): string;
+function GetEnvironmentStringPJ(Index: Integer): string;
 begin
   Result:=ConsoleToUTF8(GetEnvironmentString(Index));
 end;
 
-function GetEnvironmentVariableUTF8(const EnvVar: string): String;
+function GetEnvironmentVariablePJ(const EnvVar: string): String;
 begin
   Result:=ConsoleToUTF8(GetEnvironmentVariable(EnvVar));
 end;
@@ -225,3 +225,4 @@ procedure FinalizePlatform;
 begin
 
 end;
+

+ 7 - 7
packages/pastojs/src/pas2jsfileutilswin.inc

@@ -268,7 +268,7 @@ begin
   {$endif}
 end;
 
-function ExpandFileNameUtf8(const FileName: string; {const} BaseDir: String = ''): String;
+function ExpandFileNamePJ(const FileName: string; {const} BaseDir: String = ''): String;
 var
   IsAbs, StartsWithRoot, CanUseBaseDir : Boolean;
   {$ifndef WinCE}
@@ -305,11 +305,11 @@ begin
     begin
       FnDrive := UpCase(Fn[1]);
       GetDirUtf8(Byte(FnDrive)-64, CurDir{%H-});
-      CurDrive := UpCase(GetCurrentDirUtf8[1]);
+      CurDrive := UpCase(GetCurrentDirPJ[1]);
     end
     else
     begin
-      CurDir := GetCurrentDirUtf8;
+      CurDir := GetCurrentDirPJ;
       FnDrive := UpCase(CurDir[1]);
       CurDrive := FnDrive;
     end;
@@ -377,7 +377,7 @@ begin
   end;
 end;
 
-function GetCurrentDirUtf8: String;
+function GetCurrentDirPJ: String;
 {$ifndef WinCE}
 var
   w   : UnicodeString;
@@ -421,7 +421,7 @@ begin
   Result:=((FileGetAttrUTF8(AFilename) and faReadOnly) = 0);
 end;
 
-function GetEnvironmentVariableCountUTF8: Integer;
+function GetEnvironmentVariableCountPJ: Integer;
 var
   hp,p : PWideChar;
 begin
@@ -437,7 +437,7 @@ begin
   FreeEnvironmentStringsW(p);
 end;
 
-function GetEnvironmentStringUTF8(Index: Integer): string;
+function GetEnvironmentStringPJ(Index: Integer): string;
 var
   hp,p : PWideChar;
 begin
@@ -455,7 +455,7 @@ begin
   FreeEnvironmentStringsW(p);
 end;
 
-function GetEnvironmentVariableUTF8(const EnvVar: string): String;
+function GetEnvironmentVariablePJ(const EnvVar: string): String;
 begin
   Result:=UTF16ToUTF8(SysUtils.GetEnvironmentVariable(UTF8ToUTF16(EnvVar)));
 end;

+ 193 - 74
packages/pastojs/src/pas2jslogger.pp

@@ -23,9 +23,14 @@ unit Pas2jsLogger;
 {$mode objfpc}{$H+}
 {$inline on}
 
+{$i pas2js_defines.inc}
+
 interface
 
 uses
+  {$IFDEF Pas2JS}
+  JS, NodeJSFS,
+  {$ENDIF}
   Classes, SysUtils, PasTree, PScanner, jstree, jsbase, jswriter, fpjson,
   Pas2jsFileUtils;
 
@@ -42,6 +47,41 @@ const
 const
   DefaultLogMsgTypes = [mtFatal..mtDebug]; // by default show everything
 
+type
+  {$IFDEF Pas2JS}
+
+  { TPas2jsStream }
+
+  TPas2jsStream = class
+  public
+    procedure Write(const s: string); virtual; abstract;
+  end;
+
+  { TPas2jsFileStream }
+
+  TPas2jsFileStream = class(TPas2JSStream)
+  public
+    constructor Create(Filename: string; Mode: cardinal);
+    destructor Destroy; override;
+    procedure Write(const s: string); override;
+  end;
+const
+  fmCreate        = $FF00;
+  fmOpenRead      = 0;
+  //fmOpenWrite     = 1;
+  //fmOpenReadWrite = 2;
+  { Share modes}
+  //fmShareCompat    = $0000;
+  //fmShareExclusive = $0010;
+  //fmShareDenyWrite = $0020;
+  //fmShareDenyRead  = $0030;
+  fmShareDenyNone  = $0040;
+
+  {$ELSE}
+  TPas2jsStream = TStream;
+  TPas2jsFileStream = TFileStream;
+  {$ENDIF}
+
 type
 
   { TPas2jsMessage }
@@ -59,7 +99,7 @@ type
 
   TPas2jsLogger = class
   private
-    FDebugLog: TStream;
+    FDebugLog: TPas2JSStream;
     FEncoding: string;
     FLastMsgCol: integer;
     FLastMsgFile: string;
@@ -67,8 +107,7 @@ type
     FLastMsgNumber: integer;
     FLastMsgTxt: string;
     FLastMsgType: TMessageType;
-    FMsgNumberDisabled: PInteger;// sorted ascending
-    FMsgNumberDisabledCount: integer;
+    FMsgNumberDisabled: array of Integer;// sorted ascending
     FMsg: TFPList; // list of TPas2jsMessage
     FOnFormatPath: TPScannerFormatPathEvent;
     FOnLog: TPas2jsLogEvent;
@@ -77,7 +116,9 @@ type
     FShowMsgNumbers: boolean;
     FShowMsgTypes: TMessageTypes;
     FSorted: boolean;
+    {$IFDEF HasStdErr}
     FWriteMsgToStdErr: boolean;
+    {$ENDIF}
     function GetMsgCount: integer;
     function GetMsgNumberDisabled(MsgNumber: integer): boolean;
     function GetMsgs(Index: integer): TPas2jsMessage; inline;
@@ -87,7 +128,7 @@ type
     procedure SetOutputFilename(AValue: string);
     procedure SetSorted(AValue: boolean);
     procedure DoLogRaw(const Msg: string; SkipEncoding : Boolean);
-    function Concatenate(Args: array of const): string;
+    function Concatenate(Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF}): string;
   public
     constructor Create;
     destructor Destroy; override;
@@ -95,20 +136,25 @@ type
     function FindMsg(MsgNumber: integer; ExceptionOnNotFound: boolean): TPas2jsMessage;
     procedure Sort;
     procedure LogRaw(const Msg: string); overload;
-    procedure LogRaw(Args: array of const); overload;
+    procedure LogRaw(Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF}); overload;
     procedure LogLn;
     procedure LogPlain(const Msg: string); overload;
-    procedure LogPlain(Args: array of const); overload;
-    procedure LogMsg(MsgNumber: integer; Args: array of const;
+    procedure LogPlain(Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF}); overload;
+    procedure LogMsg(MsgNumber: integer;
+      Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF};
       const Filename: string = ''; Line: integer = 0; Col: integer = 0;
       UseFilter: boolean = true);
     procedure Log(MsgType: TMessageType; Msg: string; MsgNumber: integer = 0;
       const Filename: string = ''; Line: integer = 0; Col: integer = 0;
       UseFilter: boolean = true);
-    procedure LogMsgIgnoreFilter(MsgNumber: integer; Args: array of const);
+    procedure LogMsgIgnoreFilter(MsgNumber: integer;
+      Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF});
+    {$IFDEF FPC}
     procedure LogExceptionBackTrace;
+    {$ENDIF}
     function MsgTypeToStr(MsgType: TMessageType): string;
-    function GetMsgText(MsgNumber: integer; Args: array of const): string;
+    function GetMsgText(MsgNumber: integer;
+      Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF}): string;
     function FormatMsg(MsgType: TMessageType; Msg: string; MsgNumber: integer = 0;
       const Filename: string = ''; Line: integer = 0; Col: integer = 0): string;
     function FormatJSONMsg(MsgType: TMessageType; Msg: string; MsgNumber: integer = 0;
@@ -131,7 +177,9 @@ type
     property OutputFilename: string read FOutputFilename write SetOutputFilename;
     property ShowMsgNumbers: boolean read FShowMsgNumbers write FShowMsgNumbers;
     property ShowMsgTypes: TMessageTypes read FShowMsgTypes write FShowMsgTypes;
+    {$IFDEF HasStdErr}
     property WriteMsgToStdErr: boolean read FWriteMsgToStdErr write FWriteMsgToStdErr;
+    {$ENDIF}
     property Sorted: boolean read FSorted write SetSorted;
     property OnLog: TPas2jsLogEvent read FOnLog write FOnLog;
     property LastMsgType: TMessageType read FLastMsgType write FLastMsgType;
@@ -140,13 +188,13 @@ type
     property LastMsgCol: integer read FLastMsgCol write FLastMsgCol;
     property LastMsgTxt: string read FLastMsgTxt write FLastMsgTxt;
     property LastMsgNumber: integer read FLastMsgNumber write FLastMsgNumber;
-    property DebugLog: TStream read FDebugLog write FDebugLog;
+    property DebugLog: TPas2jsStream read FDebugLog write FDebugLog;
   end;
 
-function CompareP2JMessage(Item1, Item2: Pointer): Integer;
+function CompareP2JMessage(Item1, Item2: {$IFDEF Pas2JS}JSValue{$ELSE}Pointer{$ENDIF}): Integer;
 
-function QuoteStr(const s: string): string;
-function DeQuoteStr(const s: string): string;
+function QuoteStr(const s: string; Quote: char = '"'): string;
+function DeQuoteStr(const s: string; Quote: char = '"'): string;
 function AsString(Element: TPasElement; Full: boolean = true): string; overload;
 function AsString(Element: TJSElement): string; overload;
 function DbgString(Element: TJSElement; Indent: integer): string; overload;
@@ -154,12 +202,14 @@ function DbgAsString(Element: TJSValue; Indent: integer): string; overload;
 function DbgAsString(Element: TJSArrayLiteralElements; Indent: integer): string; overload;
 function DbgAsString(Element: TJSObjectLiteralElements; Indent: integer): string; overload;
 function DbgAsString(Element: TJSObjectLiteralElement; Indent: integer): string; overload;
+{$IFDEF UsePChar}
 function DbgHexMem(p: Pointer; Count: integer): string;
+{$ENDIF}
 function DbgStr(const s: string): string;
 
 implementation
 
-function CompareP2JMessage(Item1, Item2: Pointer): Integer;
+function CompareP2JMessage(Item1, Item2: {$IFDEF Pas2JS}JSValue{$ELSE}Pointer{$ENDIF}): Integer;
 var
   Msg1: TPas2jsMessage absolute Item1;
   Msg2: TPas2jsMessage absolute Item2;
@@ -167,14 +217,14 @@ begin
   Result:=Msg1.Number-Msg2.Number;
 end;
 
-function QuoteStr(const s: string): string;
+function QuoteStr(const s: string; Quote: char): string;
 begin
-  Result:=AnsiQuotedStr(S,'"');
+  Result:={$IFDEF Pas2JS}SysUtils.QuotedStr{$ELSE}AnsiQuotedStr{$ENDIF}(S,Quote);
 end;
 
-function DeQuoteStr(const s: string): string;
+function DeQuoteStr(const s: string; Quote: char): string;
 begin
-  Result:=AnsiDequotedStr(S,'"');
+  Result:={$IFDEF Pas2JS}SysUtils.DeQuoteString{$ELSE}AnsiDequotedStr{$ENDIF}(S,Quote);
 end;
 
 function AsString(Element: TPasElement; Full: boolean): string;
@@ -252,16 +302,16 @@ begin
     if Element is TJSStatementList then
     begin
       Result:=DbgString(TJSBinaryExpression(Element).A,Indent+2)+';'+LineEnding
-             +Space(Indent)+DbgString(TJSBinaryExpression(Element).B,Indent);
+             +StringOfChar(' ',Indent)+DbgString(TJSBinaryExpression(Element).B,Indent);
     end else if Element is TJSVariableDeclarationList then
     begin
       Result:=DbgString(TJSBinaryExpression(Element).A,Indent+2)+';'+LineEnding
-             +Space(Indent)+DbgString(TJSBinaryExpression(Element).B,Indent);
+             +StringOfChar(' ',Indent)+DbgString(TJSBinaryExpression(Element).B,Indent);
     end else if Element is TJSWithStatement then
     begin
       Result:='with ('+DbgString(TJSBinaryExpression(Element).A,Indent+2)+'){'+LineEnding
-             +Space(Indent)+DbgString(TJSBinaryExpression(Element).B,Indent+2)+LineEnding
-             +Space(Indent)+'}';
+             +StringOfChar(' ',Indent)+DbgString(TJSBinaryExpression(Element).B,Indent+2)+LineEnding
+             +StringOfChar(' ',Indent)+'}';
     end else if Element is TJSBinaryExpression then
     begin
       Result:=DbgString(TJSBinaryExpression(Element).A,Indent+2);
@@ -299,12 +349,12 @@ begin
   end else if Element is TJSIfStatement then
   begin
     Result:='if('+DbgString(TJSIfStatement(Element).Cond,Indent+2)+'){'+LineEnding
-       +Space(Indent+2)+DbgString(TJSIfStatement(Element).BTrue,Indent+2)+LineEnding
-       +Space(Indent);
+       +StringOfChar(' ',Indent+2)+DbgString(TJSIfStatement(Element).BTrue,Indent+2)+LineEnding
+       +StringOfChar(' ',Indent);
     if TJSIfStatement(Element).BFalse<>nil then
       Result+=' else {'+LineEnding
-         +Space(Indent+2)+DbgString(TJSIfStatement(Element).BFalse,Indent+2)+LineEnding
-         +Space(Indent)+'}';
+         +StringOfChar(' ',Indent+2)+DbgString(TJSIfStatement(Element).BFalse,Indent+2)+LineEnding
+         +StringOfChar(' ',Indent)+'}';
 
   // body
   end else if Element is TJSBodyStatement then
@@ -351,8 +401,8 @@ begin
     end else begin
       if TJSBodyStatement(Element).Body<>nil then
         Result+='{'+LineEnding
-          +Space(Indent+2)+DbgString(TJSBodyStatement(Element).Body,Indent+2)+LineEnding
-          +Space(Indent)+'}'
+          +StringOfChar(' ',Indent+2)+DbgString(TJSBodyStatement(Element).Body,Indent+2)+LineEnding
+          +StringOfChar(' ',Indent)+'}'
       else
         Result+='{}';
     end;
@@ -372,14 +422,14 @@ begin
     jstNull: Result:='null';
     jstBoolean: Result:=BoolToStr(Element.AsBoolean,'true','false');
     jstNumber: str(Element.AsNumber,Result);
-    jstString: Result:=AnsiQuotedStr(Element.AsString{%H-},'''');
+    jstString: Result:=QuoteStr(Element.AsString{%H-},'''');
     jstObject: Result:='{:OBJECT:}';
     jstReference: Result:='{:REFERENCE:}';
     JSTCompletion: Result:='{:COMPLETION:}';
     else Result:='{:Unknown ValueType '+IntToStr(ord(Element.ValueType))+':}';
     end;
   end;
-  Result:=Space(Indent)+Result;
+  Result:=StringOfChar(' ',Indent)+Result;
 end;
 
 function DbgAsString(Element: TJSArrayLiteralElements; Indent: integer): string;
@@ -410,6 +460,7 @@ begin
           +':'+DbgString(TJSObjectLiteralElement(Element).Expr,Indent+2);
 end;
 
+{$IFDEF UsePChar}
 function DbgHexMem(p: Pointer; Count: integer): string;
 var
   i: Integer;
@@ -418,6 +469,7 @@ begin
   for i:=0 to Count-1 do
     Result:=Result+HexStr(ord(PChar(p)[i]),2);
 end;
+{$ENDIF}
 
 function DbgStr(const s: string): string;
 var
@@ -434,6 +486,26 @@ begin
   end;
 end;
 
+{$IFDEF Pas2JS}
+{ TPas2jsFileStream }
+
+constructor TPas2jsFileStream.Create(Filename: string; Mode: cardinal);
+begin
+  writeln('TPas2JSFileStream.Create TODO ',Filename,' Mode=',Mode);
+end;
+
+destructor TPas2jsFileStream.Destroy;
+begin
+  writeln('TPas2JSFileStream.Destroy TODO');
+  inherited Destroy;
+end;
+
+procedure TPas2jsFileStream.Write(const s: string);
+begin
+  writeln('TPas2JSFileStream.Write TODO s="',s,'"');
+end;
+{$ENDIF}
+
 { TPas2jsLogger }
 
 function TPas2jsLogger.GetMsgs(Index: integer): TPas2jsMessage;
@@ -447,7 +519,7 @@ var
   l, r, m, CurMsgNumber: Integer;
 begin
   l:=0;
-  r:=FMsgNumberDisabledCount-1;
+  r:=length(FMsgNumberDisabled)-1;
   m:=0;
   while l<=r do begin
     m:=(l+r) div 2;
@@ -472,7 +544,11 @@ procedure TPas2jsLogger.SetEncoding(const AValue: string);
 var
   NewValue: String;
 begin
+  {$IFDEF Pas2JS}
+  NewValue:=Trim(lowercase(AValue));
+  {$ELSE}
   NewValue:=NormalizeEncoding(AValue);
+  {$ENDIF}
   if FEncoding=NewValue then Exit;
   //LogPlain(ClassName+': Encoding changed from "'+FEncoding+'" to "'+NewValue+'"');
   FEncoding:=NewValue;
@@ -488,28 +564,33 @@ procedure TPas2jsLogger.SetMsgNumberDisabled(MsgNumber: integer; AValue: boolean
 var
   InsertPos, OldCount: Integer;
 begin
-  OldCount:=FMsgNumberDisabledCount;
+  OldCount:=length(FMsgNumberDisabled);
   if AValue then
   begin
     // enable
     InsertPos:=FindMsgNumberDisabled(MsgNumber,true);
     if (InsertPos<OldCount) and (FMsgNumberDisabled[InsertPos]=MsgNumber) then
       exit; // already disabled
-    inc(FMsgNumberDisabledCount);
-    ReAllocMem(FMsgNumberDisabled,SizeOf(Integer)*FMsgNumberDisabledCount);
-    if InsertPos<OldCount then
-      Move(FMsgNumberDisabled[InsertPos],FMsgNumberDisabled[InsertPos+1],
-           SizeOf(Integer)*(OldCount-InsertPos));
+    // insert into array
+    {$IF defined(FPC) and (FPC_FULLVERSION<30101)}
+    SetLength(FMsgNumberDisabled,OldCount+1);
     FMsgNumberDisabled[InsertPos]:=MsgNumber;
+    {$ELSE}
+    Insert(MsgNumber,FMsgNumberDisabled,InsertPos);
+    {$ENDIF}
   end else begin
     // disable
     InsertPos:=FindMsgNumberDisabled(MsgNumber,false);
     if InsertPos<0 then exit;
+    // delete from array
+    {$IF defined(FPC) and (FPC_FULLVERSION<30101)}
     if InsertPos+1<OldCount then
       Move(FMsgNumberDisabled[InsertPos+1],FMsgNumberDisabled[InsertPos],
            SizeOf(Integer)*(OldCount-InsertPos-1));
-    dec(FMsgNumberDisabledCount);
-    ReAllocMem(FMsgNumberDisabled,SizeOf(Integer)*FMsgNumberDisabledCount);
+    SetLength(FMsgNumberDisabled,OldCount-1);
+    {$ELSE}
+    Delete(FMsgNumberDisabled,InsertPos,1);
+    {$ENDIF}
   end;
 end;
 
@@ -536,6 +617,7 @@ begin
   if SkipEncoding then
     S:=Msg
   else begin
+    {$IFDEF FPC_HAS_CPSTRING}
     if (Encoding='utf8') or (Encoding='json') then
       S:=Msg
     else if Encoding='console' then
@@ -547,6 +629,9 @@ begin
       if FOutputFile=nil then
         S:=UTF8ToConsole(Msg);
     end;
+    {$ELSE}
+    S:=Msg;
+    {$ENDIF}
   end;
   //writeln('TPas2jsLogger.LogPlain "',Encoding,'" "',DbgStr(S),'"');
   if DebugLog<>nil then
@@ -556,48 +641,74 @@ begin
   else if FOutputFile<>nil then
     FOutputFile.Write(S+LineEnding)
   else begin
+    {$IFDEF FPC_HAS_CPSTRING}
     // prevent codepage conversion magic
     SetCodePage(RawByteString(S), CP_OEMCP, False);
+    {$ENDIF}
     {AllowWriteln}
+    {$IFDEF HasStdErr}
     if WriteMsgToStdErr then
       writeln(StdErr,S)
     else
+    {$ENDIF}
       writeln(S);
     {AllowWriteln-}
   end;
 end;
 
-function TPas2jsLogger.Concatenate(Args: array of const): string;
+function TPas2jsLogger.Concatenate(
+  Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF}): string;
 var
   s: String;
   i: Integer;
+  {$IFDEF Pas2JS}
+  V: JSValue;
+  {$ELSE}
+  V: TVarRec;
+  {$ENDIF}
 begin
   s:='';
   for i:=Low(Args) to High(Args) do
   begin
-    case Args[i].VType of
-      vtInteger:      s += IntToStr(Args[i].VInteger);
-      vtBoolean:      s += BoolToStr(Args[i].VBoolean);
-      vtChar:         s += Args[i].VChar;
+    V:=Args[i];
+    {$IFDEF Pas2JS}
+    case jsTypeOf(V) of
+    'boolean':
+      if V then s+='true' else s+='false';
+    'number':
+      if isInteger(V) then
+        s+=str(NativeInt(V))
+      else
+        s+=str(Double(V));
+    'string':
+      s+=String(V);
+    else continue;
+    end;
+    {$ELSE}
+    case V.VType of
+      vtInteger:      s += IntToStr(V.VInteger);
+      vtBoolean:      s += BoolToStr(V.VBoolean);
+      vtChar:         s += V.VChar;
       {$ifndef FPUNONE}
-      vtExtended:     ; //  Args[i].VExtended^;
+      vtExtended:     ; //  V.VExtended^;
       {$ENDIF}
-      vtString:       s += Args[i].VString^;
-      vtPointer:      ; //  Args[i].VPointer;
-      vtPChar:        s += Args[i].VPChar;
-      vtObject:       ; //  Args[i].VObject;
-      vtClass:        ; //  Args[i].VClass;
-      vtWideChar:     s += AnsiString(Args[i].VWideChar);
-      vtPWideChar:    s += AnsiString(Args[i].VPWideChar);
-      vtAnsiString:   s += AnsiString(Args[i].VAnsiString);
-      vtCurrency:     ; //  Args[i].VCurrency^);
-      vtVariant:      ; //  Args[i].VVariant^);
-      vtInterface:    ; //  Args[i].VInterface^);
-      vtWidestring:   s += AnsiString(WideString(Args[i].VWideString));
-      vtInt64:        s += IntToStr(Args[i].VInt64^);
-      vtQWord:        s += IntToStr(Args[i].VQWord^);
-      vtUnicodeString:s += AnsiString(UnicodeString(Args[i].VUnicodeString));
+      vtString:       s += V.VString^;
+      vtPointer:      ; //  V.VPointer;
+      vtPChar:        s += V.VPChar;
+      vtObject:       ; //  V.VObject;
+      vtClass:        ; //  V.VClass;
+      vtWideChar:     s += AnsiString(V.VWideChar);
+      vtPWideChar:    s += AnsiString(V.VPWideChar);
+      vtAnsiString:   s += AnsiString(V.VAnsiString);
+      vtCurrency:     ; //  V.VCurrency^);
+      vtVariant:      ; //  V.VVariant^);
+      vtInterface:    ; //  V.VInterface^);
+      vtWidestring:   s += AnsiString(WideString(V.VWideString));
+      vtInt64:        s += IntToStr(V.VInt64^);
+      vtQWord:        s += IntToStr(V.VQWord^);
+      vtUnicodeString:s += AnsiString(UnicodeString(V.VUnicodeString));
     end;
+    {$ENDIF}
   end;
   Result:=s;
 end;
@@ -615,10 +726,9 @@ begin
   CloseOutputFile;
   CloseDebugLog;
   for i:=0 to FMsg.Count-1 do
-    TObject(FMsg[i]).Free;
+    TObject(FMsg[i]).{$IFDEF Pas2JS}Destroy{$ELSE}Free{$ENDIF};
   FreeAndNil(FMsg);
-  ReAllocMem(FMsgNumberDisabled,0);
-  FMsgNumberDisabledCount:=0;
+  FMsgNumberDisabled:=nil;
   inherited Destroy;
 end;
 
@@ -688,7 +798,7 @@ begin
 end;
 
 function TPas2jsLogger.GetMsgText(MsgNumber: integer;
-  Args: array of const): string;
+  Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF}): string;
 var
   Msg: TPas2jsMessage;
 begin
@@ -702,7 +812,8 @@ begin
   DoLogRaw(Msg,False);
 end;
 
-procedure TPas2jsLogger.LogRaw(Args: array of const);
+procedure TPas2jsLogger.LogRaw(
+  Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF});
 begin
   LogRaw(Concatenate(Args));
 end;
@@ -716,7 +827,11 @@ procedure TPas2jsLogger.DebugLogWriteLn(Msg: string);
 begin
   if FDebugLog=nil then exit;
   Msg:=Msg+LineEnding;
+  {$IFDEF Pas2JS}
+  FDebugLog.Write(Msg);
+  {$ELSE}
   FDebugLog.Write(Msg[1],length(Msg));
+  {$ENDIF}
 end;
 
 function TPas2jsLogger.GetEncodingCaption: string;
@@ -724,9 +839,11 @@ begin
   Result:=Encoding;
   if Result='' then
   begin
+    {$IFDEF FPC_HAS_CPSTRING}
     if FOutputFile=nil then
       Result:='console'
     else
+    {$ENDIF}
       Result:='utf-8';
   end;
   if Result='console' then
@@ -753,12 +870,14 @@ begin
     DoLogRaw(Msg,False);
 end;
 
-procedure TPas2jsLogger.LogPlain(Args: array of const);
+procedure TPas2jsLogger.LogPlain(
+  Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF});
 begin
   LogPlain(Concatenate(Args));
 end;
 
-procedure TPas2jsLogger.LogMsg(MsgNumber: integer; Args: array of const;
+procedure TPas2jsLogger.LogMsg(MsgNumber: integer;
+  Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF};
   const Filename: string; Line: integer; Col: integer; UseFilter: boolean);
 var
   Msg: TPas2jsMessage;
@@ -789,11 +908,12 @@ begin
 end;
 
 procedure TPas2jsLogger.LogMsgIgnoreFilter(MsgNumber: integer;
-  Args: array of const);
+  Args: array of {$IFDEF Pas2JS}jsvalue{$ELSE}const{$ENDIF});
 begin
   LogMsg(MsgNumber,Args,'',0,0,false);
 end;
 
+{$IFDEF FPC}
 procedure TPas2jsLogger.LogExceptionBackTrace;
 var
   lErrorAddr: CodePointer;
@@ -808,6 +928,7 @@ begin
   for FrameNumber := 0 to FrameCount-1 do
     Log(mtDebug,BackTraceStrFunc(Frames[FrameNumber]));
 end;
+{$ENDIF}
 
 function TPas2jsLogger.MsgTypeToStr(MsgType: TMessageType): string;
 begin
@@ -887,8 +1008,10 @@ begin
   if DirectoryExists(OutputFilename) then
     raise Exception.Create('Log is directory: "'+OutputFilename+'"');
   FOutputFile:=TFileWriter.Create(OutputFilename);
+  {$IFDEF FPC_HAS_CPSTRING}
   if (Encoding='') or (Encoding='utf8') then
     FOutputFile.Write(UTF8BOM);
+  {$ENDIF}
 end;
 
 procedure TPas2jsLogger.Flush;
@@ -907,11 +1030,7 @@ end;
 procedure TPas2jsLogger.Reset;
 begin
   OutputFilename:='';
-  if FMsgNumberDisabled<>nil then
-  begin
-    ReAllocMem(FMsgNumberDisabled,0);
-    FMsgNumberDisabledCount:=0;
-  end;
+  FMsgNumberDisabled:=nil;
   ShowMsgNumbers:=false;
   FShowMsgTypes:=DefaultLogMsgTypes;
 end;
@@ -930,7 +1049,7 @@ procedure TPas2jsLogger.OpenDebugLog;
 const
   DbgLogFilename = 'pas2jsdebug.log';
 begin
-  FDebugLog:=TFileStream.Create(DbgLogFilename,fmCreate or fmShareDenyNone);
+  FDebugLog:=TPas2jsFileStream.Create(DbgLogFilename,fmCreate or fmShareDenyNone);
 end;
 
 procedure TPas2jsLogger.CloseDebugLog;