Browse Source

* Synchronize with trunk

git-svn-id: branches/unicodekvm@40090 -
nickysn 6 years ago
parent
commit
bd1de8c3f5

+ 1 - 1
packages/fcl-js/src/jsscanner.pp

@@ -498,7 +498,7 @@ begin
   TokenStart := TokenStr;
   repeat
     Inc(TokenStr);
-    If (TokenStr[0]='\') and (TokenStr[1]='u') then
+    //If (TokenStr[0]='\') and (TokenStr[1]='u') then
   until not (TokenStr[0] in ['A'..'Z', 'a'..'z', '0'..'9', '_','$']);
   Len:=(TokenStr-TokenStart);
   SetLength(FCurTokenString,Len);

+ 12 - 4
packages/fcl-js/src/jssrcmap.pas

@@ -40,8 +40,10 @@ uses
     {$ifdef nodejs}
     NodeJSFS,
     {$endif}
+  {$else}
+  contnrs,
   {$endif}
-  Classes, SysUtils, contnrs, fpjson
+  Classes, SysUtils, fpjson
   {$ifdef HasJsonParser}
   , jsonparser, jsonscanner
   {$endif}
@@ -140,8 +142,8 @@ type
     function ToJSON: TJSONObject; virtual;
     function ToString: string; override;
     procedure LoadFromJSON(Obj: TJSONObject); virtual;
+    procedure SaveToStream(aStream: TFPJSStream); virtual;
     {$ifdef HasStreams}
-    procedure SaveToStream(aStream: TStream); virtual;
     procedure LoadFromStream(aStream: TStream); virtual;
     procedure SaveToFile(Filename: string); virtual;
     procedure LoadFromFile(Filename: string); virtual;
@@ -1079,21 +1081,27 @@ begin
   ParseMappings(aMappings);
 end;
 
-{$ifdef HasStreams}
-procedure TSourceMap.SaveToStream(aStream: TStream);
+procedure TSourceMap.SaveToStream(aStream: TFPJSStream);
 var
   Obj: TJSONObject;
 begin
   Obj:=ToJSON;
   try
     if smoSafetyHeader in Options then
+      begin
+      {$ifdef pas2js}
+      aStream.push(DefaultSrcMapHeader);
+      {$else}
       aStream.Write(DefaultSrcMapHeader[1],length(DefaultSrcMapHeader));
+      {$endif}
+      end;
     Obj.DumpJSON(aStream);
   finally
     Obj.Free;
   end;
 end;
 
+{$ifdef HasStreams}
 procedure TSourceMap.LoadFromStream(aStream: TStream);
 var
   s: string;

+ 10 - 12
packages/fcl-js/src/jswriter.pp

@@ -28,7 +28,7 @@ uses
   {$ifdef pas2js}
   JS,
   {$endif}
-  SysUtils, jstoken, jsbase, jstree;
+  SysUtils, jsbase, jstree;
 
 Type
   {$ifdef pas2js}
@@ -99,11 +99,13 @@ Type
   end;
   {$endif}
 
+  TBufferWriter_Buffer = Array of {$ifdef fpc}byte{$else}string{$endif};
+
   { TBufferWriter }
 
   TBufferWriter = Class(TTextWriter)
   private type
-    TBuffer = Array of {$ifdef fpc}byte{$else}string{$endif};
+    TBuffer = TBufferWriter_Buffer;
   private
     FBufPos,
     FCapacity: Cardinal;
@@ -129,6 +131,9 @@ Type
     Procedure SaveToFile(Const AFileName : String);
     Property Buffer : Pointer Read GetBuffer;
     {$endif}
+    {$ifdef pas2js}
+    Property Buffer: TBufferWriter_Buffer read FBuffer;
+    {$endif}
     Property BufferLength : Integer Read GetBufferLength;
     Property Capacity : Cardinal Read GetCapacity Write SetCapacity;
     Property AsString : TJSWriterString Read GetAsString;
@@ -528,7 +533,7 @@ function TJSWriter.EscapeString(const S: TJSString; Quote: TJSEscapeQuote
 Var
   I,J,L : Integer;
   R: TJSString;
-  c: Char;
+  c: WideChar;
 
 begin
   I:=1;
@@ -538,7 +543,7 @@ begin
   While I<=L do
     begin
     c:=S[I];
-    if (c in [#0..#31,'"','''','/','\']) then
+    if (c in [#0..#31,'"','''','/','\']) or (c>=#$ff00) then
       begin
       R:=R+Copy(S,J,I-J);
       Case c of
@@ -552,6 +557,7 @@ begin
         #10 : R:=R+'\n';
         #12 : R:=R+'\f';
         #13 : R:=R+'\r';
+        #$ff00..#$ffff: R:=R+'\u'+TJSString(HexStr(ord(c),4));
       end;
       J:=I+1;
       end;
@@ -1462,14 +1468,6 @@ procedure TJSWriter.WriteSwitchStatement(El: TJSSwitchStatement);
 
 Var
   C : Boolean;
-
-  Procedure WriteCaseLabel(L : TJSString);
-
-  begin
-    Write(l);
-  end;
-
-Var
   I : Integer;
   EC : TJSCaseElement;
 

+ 10 - 10
packages/fcl-json/src/fpjson.pp

@@ -30,7 +30,6 @@ uses
   contnrs;
 
 type
-
   TJSONtype = (jtUnknown, jtNumber, jtString, jtBoolean, jtNull, jtArray, jtObject);
   TJSONInstanceType = (
     jitUnknown,
@@ -52,9 +51,11 @@ type
   TJSONCharType = AnsiChar;
   PJSONCharType = ^TJSONCharType;
   TJSONVariant = variant;
+  TFPJSStream = TMemoryStream;
   {$else}
   TJSONCharType = char;
   TJSONVariant = jsvalue;
+  TFPJSStream = TJSArray;
   {$endif}
   TFormatOption = (foSingleLineArray,   // Array without CR/LF : all on one line
                    foSingleLineObject,  // Object without CR/LF : all on one line
@@ -142,9 +143,7 @@ Type
   public
     Constructor Create; virtual;
     Procedure Clear;  virtual; Abstract;
-    {$ifdef fpc}
-    Procedure DumpJSON(S : TStream);
-    {$endif}
+    Procedure DumpJSON(S : TFPJSStream);
     // Get enumerator
     function GetEnumerator: TBaseJSONEnumerator; virtual;
     Function FindPath(Const APath : TJSONStringType) : TJSONdata;
@@ -1272,14 +1271,16 @@ begin
   Clear;
 end;
 
-{$ifdef fpc}
-procedure TJSONData.DumpJSON(S: TStream);
+procedure TJSONData.DumpJSON(S: TFPJSStream);
 
   Procedure W(T : String);
-
   begin
-    if (T<>'') then
-      S.WriteBuffer(T[1],Length(T)*SizeOf(Char));
+    if T='' then exit;
+    {$ifdef pas2js}
+    S.push(T);
+    {$else}
+    S.WriteBuffer(T[1],Length(T)*SizeOf(Char));
+    {$endif}
   end;
 
 Var
@@ -1318,7 +1319,6 @@ begin
     W(AsJSON)
   end;
 end;
-{$endif}
 
 class function TJSONData.GetCompressedJSON: Boolean; {$ifdef fpc}static;{$endif}
 begin

+ 1 - 1
packages/fcl-passrc/src/pasresolveeval.pas

@@ -736,7 +736,7 @@ procedure ReleaseEvalValue(var Value: TResEvalValue);
 begin
   if Value=nil then exit;
   if Value.Element<>nil then exit;
-  Value.Free;
+  Value.{$ifdef pas2js}Destroy{$else}Free{$endif};
   Value:=nil;
 end;
 

+ 26 - 9
packages/fcl-passrc/src/pasresolver.pp

@@ -3498,9 +3498,12 @@ end;
 
 procedure TPasIdentifierScope.InternalAdd(Item: TPasIdentifier);
 var
-  Index: Integer;
   OldItem: TPasIdentifier;
   LoName: string;
+  {$ifdef pas2js}
+  {$ELSE}
+  Index: Integer;
+  {$ENDIF}
 begin
   LoName:=lowercase(Item.Identifier);
   {$ifdef pas2js}
@@ -5501,7 +5504,7 @@ procedure TPasResolver.FinishMethodDeclHeader(Proc: TPasProcedure);
     Proc.Visibility:=OverloadProc.Visibility;
   end;
 
-  {$IF FPC_FULLVERSION<30101}
+  {$IF defined(fpc) and (FPC_FULLVERSION<30101)}
   procedure Delete(var A: TArrayOfPasProcedure; Index, Count: integer); overload;
   var
     i: Integer;
@@ -11975,7 +11978,6 @@ var
   Value: TResEvalValue;
   Int, MinIntVal, MaxIntVal: TMaxPrecInt;
   Flo: TMaxPrecFloat;
-  c: Char;
   w: WideChar;
 begin
   Result:=nil;
@@ -12064,8 +12066,7 @@ begin
       {$ifdef FPC_HAS_CPSTRING}
       else if (bt=btAnsiChar) or ((bt=btChar) and (BaseTypeChar=btAnsiChar)) then
         try
-          c:=Char(Int);
-          Result:=TResEvalString.CreateValue(c);
+          Result:=TResEvalString.CreateValue(Char(Int));
         except
           RaiseMsg(20180125112510,nRangeCheckError,sRangeCheckError,[],Params);
         end
@@ -12380,6 +12381,7 @@ var
 begin
   if Proc=nil then ;
   P:=Params.Params;
+  if P=nil then ;
   FinishCallArgAccess(P[0],rraVarParam);
   FinishCallArgAccess(P[1],rraRead);
 end;
@@ -12442,6 +12444,7 @@ var
 begin
   if Proc=nil then ;
   P:=Params.Params;
+  if P=nil then ;
   FinishCallArgAccess(P[0],rraVarParam);
   FinishCallArgAccess(P[1],rraRead);
 end;
@@ -13246,6 +13249,7 @@ var
 begin
   if Proc=nil then ;
   P:=Params.Params;
+  if P=nil then ;
   FinishCallArgAccess(P[0],rraRead);
   FinishCallArgAccess(P[1],rraVarParam);
 end;
@@ -13341,6 +13345,7 @@ var
 begin
   if Proc=nil then ;
   P:=Params.Params;
+  if P=nil then ;
   FinishCallArgAccess(P[0],rraOutParam);
   for i:=0 to length(Params.Params)-1 do
     FinishCallArgAccess(P[i],rraRead);
@@ -13588,6 +13593,7 @@ var
 begin
   if Proc=nil then ;
   P:=Params.Params;
+  if P=nil then ;
   FinishCallArgAccess(P[0],rraVarParam);
   FinishCallArgAccess(P[1],rraRead);
   FinishCallArgAccess(P[2],rraRead);
@@ -14194,6 +14200,7 @@ var
   i: Integer;
   UsesUnit: TPasUsesUnit;
 begin
+  Result:=nil;
   //writeln('TPasResolver.FindElement Name="',aName,'"');
   ErrorEl:=nil; // use nil to use scanner position as error position
 
@@ -15464,6 +15471,7 @@ begin
     if Scope is TPasProcedureScope then
       exit(TPasProcedureScope(Scope));
   until false;
+  Result:=nil;
 end;
 
 class function TPasResolver.MangleSourceLineNumber(Line, Column: integer
@@ -16113,6 +16121,7 @@ begin
         [],Params);
     ArrayEl:=TPasArrayType(NextType);
   until false;
+  Result:=cIncompatible;
 end;
 
 function TPasResolver.CheckOverloadProcCompatibility(Proc1, Proc2: TPasProcedure
@@ -16393,8 +16402,10 @@ var
   C: TClass;
   EnumType: TPasEnumType;
   bt: TResolverBaseType;
-  w: WideChar;
   LTypeEl: TPasType;
+  {$ifdef FPC_HAS_CPSTRING}
+  w: WideChar;
+  {$endif}
 begin
   LTypeEl:=LeftResolved.LoTypeEl;
   if (LTypeEl<>nil)
@@ -17066,7 +17077,10 @@ begin
             end
           else if (C=TPasProcedureType) or (C=TPasFunctionType) then
             // UntypedPointer:=procvar
-            Result:=cLossyConversion;
+            Result:=cLossyConversion
+          else if C=TPasPointerType then
+            // UntypedPointer:=TypedPointer
+            Result:=cExact;
           end;
         end;
       end
@@ -18112,7 +18126,8 @@ var
 
   function RaiseIncompatType: integer;
   begin
-    if not RaiseOnIncompatible then exit(cIncompatible);
+    Result:=cIncompatible;
+    if not RaiseOnIncompatible then exit;
     RaiseIncompatibleTypeRes(20170216152505,nIncompatibleTypesGotExpected,
       [],RHS,LHS,ErrorEl);
   end;
@@ -18364,8 +18379,10 @@ function TPasResolver.CheckAssignCompatibilityArrayType(const LHS,
     Value: TResEvalValue;
     ElBT: TResolverBaseType;
     l: Integer;
-    US: UnicodeString;
     S: String;
+    {$ifdef FPC_HAS_CPSTRING}
+    US: UnicodeString;
+    {$endif}
   begin
     if Expr=nil then exit;
     ElBT:=GetActualBaseType(ElTypeResolved.BaseType);

+ 17 - 19
packages/fcl-passrc/tests/tcresolver.pas

@@ -238,8 +238,7 @@ type
     Procedure TestInt_ForIn;
 
     // strings
-    Procedure TestChar_Ord;
-    Procedure TestChar_Chr;
+    Procedure TestChar_BuiltInProcs;
     Procedure TestString_SetLength;
     Procedure TestString_Element;
     Procedure TestStringElement_MissingArgFail;
@@ -3158,25 +3157,21 @@ begin
   ParseProgram;
 end;
 
-procedure TTestResolver.TestChar_Ord;
+procedure TTestResolver.TestChar_BuiltInProcs;
 begin
   StartProgram(false);
-  Add('var');
-  Add('  c: char;');
-  Add('  i: longint;');
-  Add('begin');
-  Add('  i:=ord(c);');
-  ParseProgram;
-end;
-
-procedure TTestResolver.TestChar_Chr;
-begin
-  StartProgram(false);
-  Add('var');
-  Add('  c: char;');
-  Add('  i: longint;');
-  Add('begin');
-  Add('  c:=chr(i);');
+  Add([
+  'var',
+  '  c: char;',
+  '  i: longint;',
+  'begin',
+  '  i:=ord(c);',
+  '  c:=chr(i);',
+  '  c:=pred(c);',
+  '  c:=succ(c);',
+  '  c:=low(c);',
+  '  c:=high(c);',
+  '']);
   ParseProgram;
 end;
 
@@ -14259,12 +14254,15 @@ begin
   '  r: TRec;',
   '  p: PRec;',
   '  i: longint;',
+  '  Ptr: pointer;',
   'begin',
   '  p:=@r;',
   '  i:=p^.x;',
   '  p^.x:=i;',
   '  if i=p^.x then;',
   '  if p^.x=i then;',
+  '  ptr:=p;',
+  '  p:=PRec(ptr);',
   '']);
   ParseProgram;
 end;

+ 35 - 2
packages/pastojs/src/fppas2js.pp

@@ -8125,7 +8125,8 @@ begin
         or (C=TPasRecordType)
         or (C=TPasEnumType)
         or (C=TPasRangeType)
-        or (C=TPasArrayType) then
+        or (C=TPasArrayType)
+        or (C=TPasPointerType) then
       begin
       // typecast
       // default is to simply replace  "aType(value)" with "value"
@@ -9745,8 +9746,10 @@ var
   begin
     V:=ConvertElement(Param,AContext);
     if IsPred then
+      // pred(int) -> Param-1
       Expr:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,El))
     else
+      // succ(int) -> Param+1
       Expr:=TJSAdditiveExpressionPlus(CreateElement(TJSAdditiveExpressionPlus,El));
     Expr.A:=V;
     Expr.B:=CreateLiteralNumber(El,1);
@@ -9756,11 +9759,35 @@ var
   procedure CreateSwitchBool;
   begin
     if IsPred then
+      // pred(bool) -> false
       ConvertBuiltIn_PredSucc:=CreateLiteralBoolean(El,false)
     else
+      // succ(bool) -> true
       ConvertBuiltIn_PredSucc:=CreateLiteralBoolean(El,true);
   end;
 
+  procedure CreateCharPredSucc(Param: TPasExpr);
+  var
+    V: TJSElement;
+    Call: TJSCallExpression;
+    Expr: TJSAdditiveExpression;
+  begin
+    V:=ConvertElement(Param,AContext);
+    // V.charCodeAt()
+    Call:=CreateCallCharCodeAt(V,0,El);
+    if IsPred then
+      // pred(V) -> V.charCodeAt-1
+      Expr:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,El))
+    else
+      // succ(V) -> V.charCodeAt+1
+      Expr:=TJSAdditiveExpressionPlus(CreateElement(TJSAdditiveExpressionPlus,El));
+    Expr.A:=Call;
+    Expr.B:=CreateLiteralNumber(El,1);
+    // String.fromCharCode(V.charCodeAt+1)
+    Call:=CreateCallFromCharCode(Expr,El);
+    ConvertBuiltIn_PredSucc:=Call;
+  end;
+
 var
   Param: TPasExpr;
   Value: TResEvalValue;
@@ -9781,6 +9808,11 @@ begin
     CreateSwitchBool;
     exit;
     end
+  else if ResolvedEl.BaseType in btAllJSChars then
+    begin
+    CreateCharPredSucc(Param);
+    exit;
+    end
   else if ResolvedEl.BaseType=btContext then
     begin
     if TypeEl.ClassType=TPasEnumType then
@@ -18469,7 +18501,8 @@ begin
         CreatePrimitiveDotExpr(FBuiltInNames[pbivnExceptObject],El),El);
       ListFirst.A:=V;
       // add statements
-      AddToStatementList(ListFirst,ListLast,ConvertElement(El.Body,AContext),El);
+      if El.Body<>nil then
+        AddToStatementList(ListFirst,ListLast,ConvertElement(El.Body,AContext),El);
       end
     else if El.Body<>nil then
       // add statements

+ 246 - 167
packages/pastojs/src/pas2jscompiler.pp

@@ -20,9 +20,11 @@ interface
 
 uses
   {$IFDEF Pas2js}
-  NodeJSFS,
+  JS, NodeJSFS,
+  {$ELSE}
+  RtlConsts,
   {$ENDIF}
-  Classes, SysUtils, RtlConsts, contnrs,
+  Classes, SysUtils, contnrs,
   jstree, jswriter, JSSrcMap,
   PScanner, PParser, PasTree, PasResolver, PasUseAnalyzer, PasResolveEval,
   FPPas2Js, FPPJsSrcMap, Pas2jsFileUtils, Pas2jsLogger,
@@ -305,10 +307,10 @@ type
     procedure HandleException(E: Exception);
     procedure DoLogMsgAtEl(MsgType: TMessageType; const Msg: string;
       MsgNumber: integer; El: TPasElement);
-    function OnWriterIsElementUsed(Sender: TObject; El: TPasElement): boolean;
     procedure RaiseInternalError(id: TMaxPrecInt; Msg: string);
     procedure ReaderFinished;
     {$IFDEF HasPas2jsFiler}
+    function OnWriterIsElementUsed(Sender: TObject; El: TPasElement): boolean;
     procedure WritePCU;
     {$ENDIF}
   public
@@ -1240,12 +1242,6 @@ begin
   Log.Log(MsgType,Msg,MsgNumber,Filename,Line,Col);
 end;
 
-function TPas2jsCompilerFile.OnWriterIsElementUsed(Sender: TObject;
-  El: TPasElement): boolean;
-begin
-  Result:=UseAnalyzer.IsUsed(El);
-end;
-
 procedure TPas2jsCompilerFile.RaiseInternalError(id: TMaxPrecInt; Msg: string);
 begin
   Compiler.RaiseInternalError(id,Msg);
@@ -1301,6 +1297,12 @@ begin
 end;
 
 {$IFDEF HasPas2jsFiler}
+function TPas2jsCompilerFile.OnWriterIsElementUsed(Sender: TObject;
+  El: TPasElement): boolean;
+begin
+  Result:=UseAnalyzer.IsUsed(El);
+end;
+
 procedure TPas2jsCompilerFile.WritePCU;
 var
   PF: TPas2JSPrecompileFormat;
@@ -2076,6 +2078,8 @@ begin
     Value:=CondDirectiveBool[true];
     exit(true);
   end;
+
+  Result:=false;
 end;
 
 procedure TPas2jsCompiler.Compile(StartTime: TDateTime);
@@ -2459,7 +2463,11 @@ var
 var
   DestFilename, DestDir, Src, MapFilename: String;
   aJSWriter: TJSWriter;
-  ms: TMemoryStream;
+  {$IFDEF Pas2js}
+  buf: TJSArray;
+  {$ELSE}
+  buf: TMemoryStream;
+  {$ENDIF}
 begin
   //writeln('TPas2jsCompiler.WriteJSFiles ',aFile.PasFilename,' Need=',aFile.NeedBuild,' Checked=',Checked.Find(aFile)<>nil);
   if (aFile.JSModule=nil) or (not aFile.NeedBuild) then exit;
@@ -2502,8 +2510,10 @@ begin
       aJSWriter.WriteJS(aFile.JSModule);
     except
       on E: Exception do begin
+        {$IFDEF FPC}
         if ShowDebug then
           Log.LogExceptionBackTrace;
+        {$ENDIF}
         Log.LogPlain('[20180204193420] Error while creating JavaScript "'+FileCache.FormatPath(DestFilename)+'": '+E.Message);
         Terminate(ExitCodeErrorInternal);
       end;
@@ -2521,11 +2531,15 @@ begin
       // write to stdout
       if FreeWriter then
       begin
+        {$IFDEF HasStdErr}
         Log.WriteMsgToStdErr:=false;
+        {$ENDIF}
         try
           Log.LogRaw(aFileWriter.AsString);
         finally
+          {$IFDEF HasStdErr}
           Log.WriteMsgToStdErr:=coWriteMsgToStdErr in Options;
+          {$ENDIF}
         end;
       end;
     end else if FreeWriter then
@@ -2553,33 +2567,58 @@ begin
 
       // write js
       try
-        ms:=TMemoryStream.Create;
+        {$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);
-            ms.Write(Src[1],length(Src));
+            buf.Write(Src[1],length(Src));
           end;
+          {$ENDIF}
           // JS source
-          ms.Write(aFileWriter.Buffer^,aFileWriter.BufferLength);
+          {$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;
-            ms.Write(Src[1],length(Src));
+            {$IFDEF Pas2js}
+            buf.push(Src);
+            {$ELSE}
+            buf.Write(Src[1],length(Src));
+            {$ENDIF}
           end;
-          ms.Position:=0;
-          FileCache.SaveToFile(ms,DestFilename);
+          {$IFDEF Pas2js}
+          {$ELSE}
+          buf.Position:=0;
+          {$ENDIF}
+          FileCache.SaveToFile(buf,DestFilename);
         finally
-          ms.Free;
+          {$IFDEF Pas2js}
+          buf:=nil;
+          {$ELSE}
+          buf.Free;
+          {$ENDIF}
         end;
       except
         on E: Exception do begin
+          {$IFDEF FPC}
           if ShowDebug then
             Log.LogExceptionBackTrace;
           if E.Message<>SafeFormat(SFCreateError,[DestFileName]) then
             Log.LogPlain('Error: '+E.Message);
+          {$ELSE}
+          Log.LogPlain('Error: '+E.Message);
+          {$ENDIF}
           Log.LogMsg(nUnableToWriteFile,[QuoteStr(FileCache.FormatPath(DestFilename))]);
           Terminate(ExitCodeWriteError);
         end;
@@ -2592,21 +2631,36 @@ begin
                    not (coShowLineNumbers in Options));
         FinishSrcMap(aFileWriter.SrcMap);
         try
-          ms:=TMemoryStream.Create;
+          {$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(ms);
-            ms.Position:=0;
-            FileCache.SaveToFile(ms,MapFilename);
+            aFileWriter.SrcMap.SaveToStream(buf);
+            {$IFDEF Pas2js}
+            {$ELSE}
+            buf.Position:=0;
+            {$ENDIF}
+            FileCache.SaveToFile(buf,MapFilename);
           finally
-            ms.Free;
+            {$IFDEF Pas2js}
+            buf:=nil;
+            {$ELSE}
+            buf.Free;
+            {$ENDIF}
           end;
         except
           on E: Exception do begin
+            {$IFDEF FPC}
             if ShowDebug then
               Log.LogExceptionBackTrace;
             if E.Message<>SafeFormat(SFCreateError,[DestFileName]) then
               Log.LogPlain('Error: '+E.Message);
+            {$ELSE}
+            Log.LogPlain('Error: '+E.Message);
+            {$ENDIF}
             Log.LogMsg(nUnableToWriteFile,[QuoteStr(FileCache.FormatPath(MapFilename))]);
             Terminate(ExitCodeWriteError);
           end;
@@ -2853,7 +2907,9 @@ end;
 procedure TPas2jsCompiler.SetWriteMsgToStdErr(const AValue: boolean);
 begin
   SetOption(coWriteMsgToStdErr,AValue);
+  {$IFDEF HasStdErr}
   Log.WriteMsgToStdErr:=AValue;
+  {$ENDIF}
 end;
 
 procedure TPas2jsCompiler.AddDefinesForTargetPlatform;
@@ -2911,14 +2967,14 @@ const
   IdentChars = ['a'..'z','A'..'Z','_','0'..'9'];
 var
   Line: String;
-  p, StartP: PChar;
+  l, p, StartP: integer;
 
   function GetWord: String;
   begin
     StartP:=p;
-    while (p^ in IdentChars) or (p^>#127) do inc(p);
-    Result:=copy(Line,StartP-PChar(Line)+1,p-StartP);
-    while p^ in [' ',#9] do inc(p);
+    while (p<=l) and ((Line[p] in IdentChars) or (Line[p]>#127)) do inc(p);
+    Result:=copy(Line,StartP,p-StartP);
+    while (p<=l) and (Line[p] in [' ',#9]) do inc(p);
   end;
 
   procedure DebugCfgDirective(const s: string);
@@ -2951,15 +3007,16 @@ begin
       if ShowDebug then
         Log.LogMsgIgnoreFilter(nInterpretingFileOption,[QuoteStr(Line)]);
       if Line='' then continue;
-      p:=PChar(Line);
-      while (p^ in [' ',#9]) do inc(p);
-      if p^=#0 then continue; // empty line
+      l:=length(Line);
+      p:=1;
+      while (p<=l) and (Line[p] in [' ',#9]) do inc(p);
+      if p>l then continue; // empty line
 
-      if p^='#' then
+      if (p<=l) and (Line[p]='#') then
       begin
         // cfg directive
         inc(p);
-        if p^ in [#0,#9,' ','-'] then continue; // comment
+        if (p>l) or (Line[p] in [#0,#9,' ','-']) then continue; // comment
         Directive:=lowercase(GetWord);
         case Directive of
         'ifdef','ifndef':
@@ -2987,7 +3044,7 @@ begin
             inc(IfLvl);
             if Skip=skipNone then
             begin
-              Expr:=copy(Line,p-PChar(Line)+1,length(Line));
+              Expr:=copy(Line,p,length(Line));
               if ConditionEvaluator.Eval(Expr) then
               begin
                 // execute block
@@ -3031,7 +3088,7 @@ begin
             if (Skip=skipIf) and (IfLvl=SkipLvl) then
             begin
               // if-block was skipped -> try this elseif
-              Expr:=copy(Line,p-PChar(Line)+1,length(Line));
+              Expr:=copy(Line,p,length(Line));
               if ConditionEvaluator.Eval(Expr) then
               begin
                 // execute elseif block
@@ -3067,7 +3124,7 @@ begin
             end;
           end;
         'error':
-          ParamFatal('user defined: '+copy(Line,p-PChar(Line)+1,length(Line)))
+          ParamFatal('user defined: '+copy(Line,p,length(Line)))
         else
           if Skip=skipNone then
             CfgSyntaxError('unknown directive "#'+Directive+'"')
@@ -3077,7 +3134,7 @@ begin
       end else if Skip=skipNone then
       begin
         // option line
-        Line:=String(p);
+        Line:=copy(Line,p,length(Line));
         ReadParam(Line,false,false);
       end;
     end;
@@ -3157,9 +3214,10 @@ var
   p, l, i: Integer;
   c: Char;
   aProc, pr: TPasToJsProcessor;
-  Enable, Found: Boolean;
+  Enable: Boolean;
   aPlatform, pl: TPasToJsPlatform;
   {$IFDEF HasPas2jsFiler}
+  Found: Boolean;
   PF: TPas2JSPrecompileFormat;
   {$ENDIF}
 begin
@@ -3939,7 +3997,13 @@ begin
       FreeStuff;
     except
       on E: Exception do
+      begin
+        {$IFDEF Pas2js}
+        Log.LogRaw('TPas2jsCompiler.Destroy '+E.Message);
+        {$ELSE}
         Log.LogExceptionBackTrace;
+        {$ENDIF}
+      end;
     end
   else
     FreeStuff;
@@ -4039,7 +4103,9 @@ begin
 
   ClearDefines;
   TStringList(FDefines).Sorted:=True;
+  {$IFDEF FPC}
   TStringList(FDefines).Duplicates:=dupError;
+  {$ENDIF}
 
   AddDefine('PAS2JS');
   AddDefine('PAS2JS_FULLVERSION',IntToStr((VersionMajor*100+VersionMinor)*100+VersionRelease));
@@ -4126,8 +4192,10 @@ begin
     on E: ECompilerTerminate do
     begin
     end else begin
+      {$IFDEF FPC}
       if ShowDebug then
         Log.LogExceptionBackTrace;
+      {$ENDIF}
       raise;
     end;
   end;
@@ -4138,17 +4206,18 @@ const
   MaxLineLen = 78;
   Indent = 12;
 
-  procedure l(s: string);
+  procedure w(s: string);
   var
-    p, LastCharStart, WordBreak: PChar;
+    l, p, LastCharStart, WordBreak: integer;
     Len: integer;
     CodePointCount: Integer;
 
     procedure InitLine;
     begin
-      p:=PChar(s);
+      l:=length(s);
+      p:=1;
       LastCharStart:=p;
-      WordBreak:=nil;
+      WordBreak:=0;
       CodePointCount:=0;
     end;
 
@@ -4159,19 +4228,23 @@ const
       exit;
     end;
     InitLine;
-    repeat
-      case p^ of
-      #0:
-        if p-PChar(s)=length(s) then
-          break
-        else
-          inc(p);
-      'a'..'z','A'..'Z','0'..'9','_','-','.',',','"','''','`',#128..#255:
+    while p<=l do
+    begin
+      case s[p] of
+      'a'..'z','A'..'Z','0'..'9','_','-','.',',','"','''','`',
+      #128..high(char) :
         begin
         LastCharStart:=p;
-        Len:=UTF8CharacterStrictLength(p);
+        {$IFDEF FPC_HAS_CPSTRING}
+        Len:=UTF8CharacterStrictLength(@s[p]);
         if Len=0 then Len:=1;
         inc(p,Len);
+        {$ELSE}
+        if (p<l) and (s[p] in [#$DC00..#$DFFF]) then
+          inc(p,2)
+        else
+          inc(p,1);
+        {$ENDIF}
         end;
       else
         LastCharStart:=p;
@@ -4181,15 +4254,16 @@ const
       inc(CodePointCount);
       if CodePointCount>=MaxLineLen then
       begin
-        if (WordBreak=nil) or (WordBreak-PChar(s)<MaxLineLen div 3) then
+        if (WordBreak=0)
+            or (WordBreak<MaxLineLen div {$IFDEF FPC_HAS_CPSTRING}3{$ELSE}2{$ENDIF}) then
           WordBreak:=LastCharStart;
-        Len:=WordBreak-PChar(s);
+        Len:=WordBreak-1;
         Log.LogRaw(LeftStr(s,Len));
         Delete(s,1,len);
-        s:=Space(Indent)+Trim(s);
+        s:=StringOfChar(' ',Indent)+Trim(s);
         InitLine;
       end;
-    until false;
+    end;
     Log.LogRaw(s);
   end;
 
@@ -4201,128 +4275,128 @@ begin
   Log.LogLn;
   if CompilerExe<>'' then
   begin
-    l('Usage: '+CompilerExe+' <your.pas>');
+    w('Usage: '+CompilerExe+' <your.pas>');
   end else begin
-    l('Usage: pas2js <your.pas>');
+    w('Usage: pas2js <your.pas>');
   end;
   Log.LogLn;
-  l('Options:');
-  l('Put + after a boolean switch option to enable it, - to disable it');
-  l('  @<x>    : Read compiler options from file <x> in addition to the default '+DefaultConfigFile);
-  l('  -B      : Rebuild all');
-  l('  -d<x>   : Defines the symbol <x>. Optional: -d<x>:=<value>');
-  l('  -i<x>   : Write information and halt. <x> is a combination of the following:');
-  l('    -iD   : Write compiler date');
-  l('    -iSO  : Write compiler OS');
-  l('    -iSP  : Write compiler host processor');
-  l('    -iTO  : Write target platform');
-  l('    -iTP  : Write target processor');
-  l('    -iV   : Write short compiler version');
-  l('    -iW   : Write full compiler version');
-  l('    -ic   : Write list of supported JS processors usable by -P<x>');
-  l('    -io   : Write list of supported optimizations usable by -Oo<x>');
-  l('    -it   : Write list of supported targets usable by -T<x>');
-  l('  -C<x>   : Code generation options. <x> is a combination of the following letters:');
+  w('Options:');
+  w('Put + after a boolean switch option to enable it, - to disable it');
+  w('  @<x>    : Read compiler options from file <x> in addition to the default '+DefaultConfigFile);
+  w('  -B      : Rebuild all');
+  w('  -d<x>   : Defines the symbol <x>. Optional: -d<x>:=<value>');
+  w('  -i<x>   : Write information and halt. <x> is a combination of the following:');
+  w('    -iD   : Write compiler date');
+  w('    -iSO  : Write compiler OS');
+  w('    -iSP  : Write compiler host processor');
+  w('    -iTO  : Write target platform');
+  w('    -iTP  : Write target processor');
+  w('    -iV   : Write short compiler version');
+  w('    -iW   : Write full compiler version');
+  w('    -ic   : Write list of supported JS processors usable by -P<x>');
+  w('    -io   : Write list of supported optimizations usable by -Oo<x>');
+  w('    -it   : Write list of supported targets usable by -T<x>');
+  w('  -C<x>   : Code generation options. <x> is a combination of the following letters:');
   // -C3        Turn on ieee error checking for constants
-  l('    o     : Overflow checking of integer operations');
+  w('    o     : Overflow checking of integer operations');
   // -CO        Check for possible overflow of integer operations
-  l('    r     : Range checking');
-  l('    R     : Object checks. Verify method calls and object type casts.');
-  l('  -F...   Set file names and paths:');
-  l('   -Fe<x> : Redirect output to file <x>. UTF-8 encoded.');
-  l('   -FE<x> : Set main output path to <x>');
-  l('   -Fi<x> : Add <x> to include paths');
-  l('   -FN<x> : add <x> to namespaces. Namespaces with trailing - are removed.');
-  l('            Delphi calls this flag "unit scope names".');
-  //l('   -Fr<x> : Load error message file <x>');
-  l('   -Fu<x> : Add <x> to unit paths');
-  l('   -FU<x> : Set unit output path to <x>');
-  l('  -I<x>   : Add <x> to include paths, same as -Fi');
-  l('  -J...  Extra options of pas2js');
-  l('   -Jc    : Write all JavaScript concatenated into the output file');
-  l('   -Je<x> : Encode messages as <x>.');
-  l('     -Jeconsole : Console codepage. This is the default.');
-  l('     -Jesystem  : System codepage. On non Windows console and system are the same.');
-  l('     -Jeutf-8   : Unicode UTF-8. Default when using -Fe.');
-  l('     -JeJSON    : Output compiler messages as JSON. Logo etc are outputted as-is.');
-  l('   -Ji<x> : Insert JS file <x> into main JS file. E.g. -Jirtl.js. Can be given multiple times. To remove a file name append a minus, e.g. -Jirtl.js-.');
-  l('   -Jl    : lower case identifiers');
-  l('   -Jm    : generate source maps');
-  l('     -Jmsourceroot=<x> : use x as "sourceRoot", prefix URL for source file names.');
-  l('     -Jmbasedir=<x> : write source file names relative to directory x.');
-  l('     -Jminclude : include Pascal sources in source map.');
-  l('     -Jmxssiheader : start source map with XSSI protection )]}'', default.');
-  l('     -Jm- : disable generating source maps');
-  l('   -Jo<x> : Enable or disable extra option. The x is case insensitive:');
-  l('     -JoSearchLikeFPC : search source files like FPC, default: search case insensitive.');
-  l('     -JoUseStrict : add "use strict" to modules, default.');
-  l('     -JoCheckVersion- : do not add rtl version check, default.');
-  l('     -JoCheckVersion=main : insert rtl version check into main.');
-  l('     -JoCheckVersion=system : insert rtl version check into system unit init.');
-  l('     -JoCheckVersion=unit : insert rtl version check into every unit init.');
-  l('   -Ju<x> : Add <x> to foreign unit paths. Foreign units are not compiled.');
+  w('    r     : Range checking');
+  w('    R     : Object checks. Verify method calls and object type casts.');
+  w('  -F...   Set file names and paths:');
+  w('   -Fe<x> : Redirect output to file <x>. UTF-8 encoded.');
+  w('   -FE<x> : Set main output path to <x>');
+  w('   -Fi<x> : Add <x> to include paths');
+  w('   -FN<x> : add <x> to namespaces. Namespaces with trailing - are removed.');
+  w('            Delphi calls this flag "unit scope names".');
+  //w('   -Fr<x> : Load error message file <x>');
+  w('   -Fu<x> : Add <x> to unit paths');
+  w('   -FU<x> : Set unit output path to <x>');
+  w('  -I<x>   : Add <x> to include paths, same as -Fi');
+  w('  -J...  Extra options of pas2js');
+  w('   -Jc    : Write all JavaScript concatenated into the output file');
+  w('   -Je<x> : Encode messages as <x>.');
+  w('     -Jeconsole : Console codepage. This is the default.');
+  w('     -Jesystem  : System codepage. On non Windows console and system are the same.');
+  w('     -Jeutf-8   : Unicode UTF-8. Default when using -Fe.');
+  w('     -JeJSON    : Output compiler messages as JSON. Logo etc are outputted as-is.');
+  w('   -Ji<x> : Insert JS file <x> into main JS file. E.g. -Jirtl.js. Can be given multiple times. To remove a file name append a minus, e.g. -Jirtl.js-.');
+  w('   -Jl    : lower case identifiers');
+  w('   -Jm    : generate source maps');
+  w('     -Jmsourceroot=<x> : use x as "sourceRoot", prefix URL for source file names.');
+  w('     -Jmbasedir=<x> : write source file names relative to directory x.');
+  w('     -Jminclude : include Pascal sources in source map.');
+  w('     -Jmxssiheader : start source map with XSSI protection )]}'', default.');
+  w('     -Jm- : disable generating source maps');
+  w('   -Jo<x> : Enable or disable extra option. The x is case insensitive:');
+  w('     -JoSearchLikeFPC : search source files like FPC, default: search case insensitive.');
+  w('     -JoUseStrict : add "use strict" to modules, default.');
+  w('     -JoCheckVersion- : do not add rtl version check, default.');
+  w('     -JoCheckVersion=main : insert rtl version check into main.');
+  w('     -JoCheckVersion=system : insert rtl version check into system unit init.');
+  w('     -JoCheckVersion=unit : insert rtl version check into every unit init.');
+  w('   -Ju<x> : Add <x> to foreign unit paths. Foreign units are not compiled.');
   {$IFDEF HasPas2jsFiler}
   if PrecompileFormats.Count>0 then
   begin
-    l('   -JU<x> : Create precompiled units in format x.');
+    w('   -JU<x> : Create precompiled units in format x.');
     for i:=0 to PrecompileFormats.Count-1 do
       with PrecompileFormats[i] do
-        l('     -JU'+Ext+' : '+Description);
-    l('     -JU- : Disable prior -JU<x> option. Do not create precompiled units.');
+        w('     -JU'+Ext+' : '+Description);
+    w('     -JU- : Disable prior -JU<x> option. Do not create precompiled units.');
   end;
   {$ENDIF}
-  l('  -l      : Write logo');
-  l('  -MDelphi: Delphi 7 compatibility mode');
-  l('  -MObjFPC: FPC''s Object Pascal compatibility mode (default)');
-  l('  -NS<x>  : obsolete: add <x> to namespaces. Same as -FN<x>');
-  l('  -n      : Do not read the default config files');
-  l('  -o<x>   : Change main JavaScript file to <x>, "." means stdout');
-  l('  -O<x>   : Optimizations:');
-  l('    -O-   : Disable optimizations');
-  l('    -O1   : Level 1 optimizations (quick and debugger friendly)');
-  //l('    -O2   : Level 2 optimizations (Level 1 + not debugger friendly)');
-  l('    -Oo<x> : Enable or disable optimization. The x is case insensitive:');
-  l('      -OoEnumNumbers[-] : write enum value as number instead of name. Default in -O1.');
-  l('      -OoRemoveNotUsedPrivates[-] : Default is enabled');
-  l('      -OoRemoveNotUsedDeclarations[-] : Default enabled for programs with -Jc');
-  l('  -P<x>   : Set target processor. Case insensitive:');
-  l('    -Pecmascript5 : default');
-  l('    -Pecmascript6');
-  l('  -S<x>   : Syntax options. <x> is a combination of the following letters:');
-  l('    a     : Turn on assertions');
-  l('    c     : Support operators like C (*=,+=,/= and -=)');
-  l('    d     : Same as -Mdelphi');
-  l('    m     : Enables macro replacements');
-  l('    2     : Same as -Mobjfpc (default)');
-  l('  -SI<x>   : Set interface style to <x>');
-  l('    -SIcom   : COM compatible interface (default)');
-  l('    -SIcorba : CORBA compatible interface');
-  l('  -T<x>   : Set target platform');
-  l('    -Tbrowser : default');
-  l('    -Tnodejs  : add pas.run(), includes -Jc');
-  l('  -u<x>   : Undefines the symbol <x>');
-  l('  -v<x>   : Be verbose. <x> is a combination of the following letters:');
-  l('    e     : Show errors (default)');
-  l('    w     : Show warnings');
-  l('    n     : Show notes');
-  l('    h     : Show hints');
-  l('    i     : Show info');
-  l('    l     : Show line numbers, needs -vi');
-  l('    a     : Show everything');
-  l('    0     : Show nothing (except errors)');
-  l('    b     : Show file names with full path');
-  l('    c     : Show conditionals');
-  l('    t     : Show tried/used files');
-  l('    d     : Show debug notes and info, enables -vni');
-  l('    q     : Show message numbers');
-  l('    x     : Show used tools');
-  l('    v     : Write pas2jsdebug.log with lots of debugging info');
-  l('    z     : Write messages to stderr, -o. still uses stdout.');
-  l('  -vm<x>,<y>: Do not show messages numbered <x> and <y>.');
-  l('  -?      : Show this help');
-  l('  -h      : Show this help');
+  w('  -l      : Write logo');
+  w('  -MDelphi: Delphi 7 compatibility mode');
+  w('  -MObjFPC: FPC''s Object Pascal compatibility mode (default)');
+  w('  -NS<x>  : obsolete: add <x> to namespaces. Same as -FN<x>');
+  w('  -n      : Do not read the default config files');
+  w('  -o<x>   : Change main JavaScript file to <x>, "." means stdout');
+  w('  -O<x>   : Optimizations:');
+  w('    -O-   : Disable optimizations');
+  w('    -O1   : Level 1 optimizations (quick and debugger friendly)');
+  //w('    -O2   : Level 2 optimizations (Level 1 + not debugger friendly)');
+  w('    -Oo<x> : Enable or disable optimization. The x is case insensitive:');
+  w('      -OoEnumNumbers[-] : write enum value as number instead of name. Default in -O1.');
+  w('      -OoRemoveNotUsedPrivates[-] : Default is enabled');
+  w('      -OoRemoveNotUsedDeclarations[-] : Default enabled for programs with -Jc');
+  w('  -P<x>   : Set target processor. Case insensitive:');
+  w('    -Pecmascript5 : default');
+  w('    -Pecmascript6');
+  w('  -S<x>   : Syntax options. <x> is a combination of the following letters:');
+  w('    a     : Turn on assertions');
+  w('    c     : Support operators like C (*=,+=,/= and -=)');
+  w('    d     : Same as -Mdelphi');
+  w('    m     : Enables macro replacements');
+  w('    2     : Same as -Mobjfpc (default)');
+  w('  -SI<x>   : Set interface style to <x>');
+  w('    -SIcom   : COM compatible interface (default)');
+  w('    -SIcorba : CORBA compatible interface');
+  w('  -T<x>   : Set target platform');
+  w('    -Tbrowser : default');
+  w('    -Tnodejs  : add pas.run(), includes -Jc');
+  w('  -u<x>   : Undefines the symbol <x>');
+  w('  -v<x>   : Be verbose. <x> is a combination of the following letters:');
+  w('    e     : Show errors (default)');
+  w('    w     : Show warnings');
+  w('    n     : Show notes');
+  w('    h     : Show hints');
+  w('    i     : Show info');
+  w('    l     : Show line numbers, needs -vi');
+  w('    a     : Show everything');
+  w('    0     : Show nothing (except errors)');
+  w('    b     : Show file names with full path');
+  w('    c     : Show conditionals');
+  w('    t     : Show tried/used files');
+  w('    d     : Show debug notes and info, enables -vni');
+  w('    q     : Show message numbers');
+  w('    x     : Show used tools');
+  w('    v     : Write pas2jsdebug.log with lots of debugging info');
+  w('    z     : Write messages to stderr, -o. still uses stdout.');
+  w('  -vm<x>,<y>: Do not show messages numbered <x> and <y>.');
+  w('  -?      : Show this help');
+  w('  -h      : Show this help');
   Log.LogLn;
-  l('Macros: Format is $Name, $Name$ or $Name()');
+  w('Macros: Format is $Name, $Name$ or $Name()');
   for i:=0 to ParamMacros.Count-1 do begin
     ParamMacro:=ParamMacros[i];
     Log.LogRaw(['  $',ParamMacro.Name,BoolToStr(ParamMacro.CanHaveParams,'()',''),': ',ParamMacro.Description]);
@@ -4505,9 +4579,11 @@ begin
 
   if (UnitFilename='') or not DirectoryCache.FileExists(UnitFilename) then
   begin
+    {$IFDEF HasPas2jsFiler}
     if aFormat=nil then
       Log.LogMsg(nSourceFileNotFound,[QuoteStr(UnitFilename)])
     else
+    {$ENDIF}
       Log.LogMsg(nUnitFileNotFound,[QuoteStr(UnitFilename)]);
     Terminate(ExitCodeFileNotFound);
   end;
@@ -4519,7 +4595,7 @@ begin
     Terminate(ExitCodeFileNotFound);
   end;
 
-  aFile:=TPas2jsCompilerFile.Create(Self,UnitFilename,aFormat);
+  aFile:=TPas2jsCompilerFile.Create(Self,UnitFilename{$IFDEF HasPas2jsFiler},aFormat{$ENDIF});
   if UseUnitName<>'' then
     begin
     {$IFDEF VerboseSetPasUnitName}
@@ -4549,13 +4625,16 @@ begin
 
   if ShowDebug then
     Log.LogPlain(['Debug: Opening file "',UnitFilename,'"...']);
-  if aFile.PCUFormat=nil then
+  {$IFDEF HasPas2jsFiler}
+  if aFile.PCUFormat<>nil then
   begin
-    // open file (beware: this changes FileResolver.BaseDirectory)
-    aFile.OpenFile(UnitFilename);
-  end else begin
     aFile.FileResolver.BaseDirectory:=ExtractFilePath(UnitFilename);
     aFile.CreatePCUReader;
+  end else
+  {$ENDIF}
+  begin
+    // open file (beware: this changes FileResolver.BaseDirectory)
+    aFile.OpenFile(UnitFilename);
   end;
 end;
 

+ 14 - 7
packages/pastojs/src/pas2jsfilecache.pp

@@ -31,6 +31,7 @@ uses
     {$ENDIF}
   {$ENDIF}
   Classes, SysUtils,
+  fpjson,
   PScanner, PasUseAnalyzer, PasResolver, FPPJsSrcMap,
   {$IFDEF HasPas2jsFiler}
   Pas2JsFiler,
@@ -339,9 +340,7 @@ type
     procedure GetListing(const aDirectory: string; var Files: TStrings;
                          FullPaths: boolean = true);
     procedure RaiseDuplicateFile(aFilename: string);
-    {$IFDEF HasStreams}
-    procedure SaveToFile(ms: TMemoryStream; Filename: string);
-    {$ENDIF}
+    procedure SaveToFile(ms: TFPJSStream; Filename: string);
     function ExpandDirectory(const Filename, BaseDir: string): string;
   public
     property AllJSIntoMainJS: Boolean read GetAllJSIntoMainJS write SetAllJSIntoMainJS;
@@ -2166,15 +2165,19 @@ begin
   end;
 end;
 
-{$IFDEF HasStreams}
-procedure TPas2jsFilesCache.SaveToFile(ms: TMemoryStream; Filename: string);
+procedure TPas2jsFilesCache.SaveToFile(ms: TFPJSStream; Filename: string);
 var
   s: string;
-  l: Int64;
+  {$IFDEF FPC}
   i: Integer;
+  l: TMaxPrecInt;
+  {$ENDIF}
 begin
   if Assigned(OnWriteFile) then
   begin
+    {$IFDEF Pas2js}
+    s:=ms.join('');
+    {$ELSE}
     l:=ms.Size-ms.Position;
     if l>0 then
     begin
@@ -2184,9 +2187,13 @@ begin
     end
     else
       s:='';
+    {$ENDIF}
     OnWriteFile(Filename,s);
   end else
   begin
+    {$IFDEF Pas2js}
+    raise Exception.Create('TPas2jsFilesCache.SaveToFile TODO '+Filename);
+    {$ELSE}
     try
       ms.SaveToFile(Filename);
     except
@@ -2201,9 +2208,9 @@ begin
         raise;
       end;
     end;
+    {$ENDIF}
   end;
 end;
-{$ENDIF}
 
 function TPas2jsFilesCache.ExpandDirectory(const Filename, BaseDir: string
   ): string;

+ 28 - 30
packages/pastojs/tests/tcmodules.pas

@@ -263,8 +263,7 @@ type
     // strings
     Procedure TestCharConst;
     Procedure TestChar_Compare;
-    Procedure TestChar_Ord;
-    Procedure TestChar_Chr;
+    Procedure TestChar_BuiltInProcs;
     Procedure TestStringConst;
     Procedure TestStringConstSurrogate;
     Procedure TestString_Length;
@@ -5803,18 +5802,25 @@ begin
     '']));
 end;
 
-procedure TTestModule.TestChar_Ord;
+procedure TTestModule.TestChar_BuiltInProcs;
 begin
   StartProgram(false);
-  Add('var');
-  Add('  c: char;');
-  Add('  i: longint;');
-  Add('  s: string;');
-  Add('begin');
-  Add('  i:=ord(c);');
-  Add('  i:=ord(s[i]);');
+  Add([
+  'var',
+  '  c: char;',
+  '  i: longint;',
+  '  s: string;',
+  'begin',
+  '  i:=ord(c);',
+  '  i:=ord(s[i]);',
+  '  c:=chr(i);',
+  '  c:=pred(c);',
+  '  c:=succ(c);',
+  '  c:=low(c);',
+  '  c:=high(c);',
+  '']);
   ConvertProgram;
-  CheckSource('TestChar_Ord',
+  CheckSource('TestChar_BuiltInProcs',
     LinesToStr([
     'this.c = "";',
     'this.i = 0;',
@@ -5823,25 +5829,11 @@ begin
     LinesToStr([
     '$mod.i = $mod.c.charCodeAt();',
     '$mod.i = $mod.s.charCodeAt($mod.i-1);',
-    '']));
-end;
-
-procedure TTestModule.TestChar_Chr;
-begin
-  StartProgram(false);
-  Add('var');
-  Add('  c: char;');
-  Add('  i: longint;');
-  Add('begin');
-  Add('  c:=chr(i);');
-  ConvertProgram;
-  CheckSource('TestChar_Chr',
-    LinesToStr([
-    'this.c = "";',
-    'this.i = 0;'
-    ]),
-    LinesToStr([
     '$mod.c = String.fromCharCode($mod.i);',
+    '$mod.c = String.fromCharCode($mod.c.charCodeAt() - 1);',
+    '$mod.c = String.fromCharCode($mod.c.charCodeAt() + 1);',
+    '$mod.c = "\x00";',
+    '$mod.c = "\uFFFF";',
     '']));
 end;
 
@@ -7209,7 +7201,7 @@ begin
     '']),
     LinesToStr([ // $mod.$main
     '$mod.c = "\x00";',
-    '$mod.c = "'#$EF#$BF#$BF'";',
+    '$mod.c = "\uFFFF";',
     '$mod.Arr[66] = "a";',
     '$mod.Arr[68] = $mod.Arr[$mod.c.charCodeAt()];',
     '$mod.Arr[$mod.c.charCodeAt()] = $mod.Arr[100];',
@@ -18252,6 +18244,7 @@ begin
   '  r: TRec;',
   '  p: PRec;',
   '  q: ^TRec;',
+  '  Ptr: pointer;',
   'begin',
   '  new(p);',
   '  p:=@r;',
@@ -18263,6 +18256,8 @@ begin
   '  dispose(p);',
   '  new(q);',
   '  dispose(q);',
+  '  Ptr:=p;',
+  '  p:=PRec(ptr);',
   '']);
   ConvertProgram;
   CheckSource('TestPointer_Record',
@@ -18280,6 +18275,7 @@ begin
     'this.r = new $mod.TRec();',
     'this.p = null;',
     'this.q = null;',
+    'this.Ptr = null;',
     '']),
     LinesToStr([ // $mod.$main
     '$mod.p = new $mod.TRec();',
@@ -18292,6 +18288,8 @@ begin
     '$mod.p = null;',
     '$mod.q = new $mod.TRec();',
     '$mod.q = null;',
+    '$mod.Ptr = $mod.p;',
+    '$mod.p = $mod.Ptr;',
     '']));
 end;
 

+ 1 - 0
utils/pas2js/nodepas2js.pp

@@ -13,4 +13,5 @@ uses
 
 begin
   // Your code here
+
 end.