Browse Source

pastojs: test EncodeVLQ

git-svn-id: trunk@38085 -
Mattias Gaertner 7 years ago
parent
commit
373c7fb655
2 changed files with 747 additions and 18 deletions
  1. 623 11
      packages/pastojs/src/pas2jsfiler.pp
  2. 124 7
      packages/pastojs/tests/tcfiler.pas

+ 623 - 11
packages/pastojs/src/pas2jsfiler.pp

@@ -17,14 +17,14 @@ Abstract:
   Write and read a precompiled module.
 
   Store whole unit, except all
-  procedure declarations, proc bodies, finalization/initialization sections are
-  replaced by
+    procedure declarations, proc bodies, finalization/initialization sections are
+    replaced by
     -precompiled code
     -lists of references
     -local consts
   The useanalyzer needs the references - TPas2jsUseAnalyzer.
 
-  Due to uses cycles, ability to stop read interface
+  Due to uses cycles, ability to stop read after interface
   ReadContinueImplementation
 }
 unit Pas2JsFiler;
@@ -34,36 +34,648 @@ unit Pas2JsFiler;
 interface
 
 uses
-  Classes, SysUtils, FPPas2Js, PasResolver, PasTree;
+  Classes, SysUtils, contnrs, FPPas2Js, Pas2jsFileUtils, PasTree,
+  PScanner, PParser, PasResolveEval, PasResolver;
+
+const
+  PJUMagic = 'Pas2JSCache';
+  PJUVersion = 1;
 
 type
+  TPJUSourceFileKind = (
+    sfkUnit,   // 0
+    sfkInclude // 1
+  );
+  TPJUSourceFileKinds = set of TPJUSourceFileKind;
+  TPJUSourceFileChecksum = longint;
+
+  { TPJUSourceFile }
+
+  TPJUSourceFile = class
+  public
+    Kind: TPJUSourceFileKind;
+    Filename: string;
+    Checksum: TPJUSourceFileChecksum;
+    Index: integer;
+  end;
+
+  TPJUInitialFlags = class
+  public
+    ParserOptions: TPOptions;
+    ModeSwitches: TModeSwitches;
+    BoolSwitches: TBoolSwitches;
+    ResolverOptions: TPasResolverOptions;
+    PasTojsOptions: TPasToJsConverterOptions;
+    TargetPlatform: TPasToJsPlatform;
+    TargetProcessor: TPasToJsProcessor;
+    // ToDo: defines
+  end;
+
+  { TPasToJsWriter }
+
+  TPasToJsWriter = class
+  private
+    FInitialFlags: TPJUInitialFlags;
+    FParser: TPasParser;
+    FResolver: TPasResolver;
+    FScanner: TPascalScanner;
+    FSourceFiles: TObjectList;
+    FSourceFilesSorted: array of TPJUSourceFile;
+    FStream: TStream;
+  protected
+    procedure WriteStr(const s: string);
+    procedure WriteInt(const i: MaxPrecInt);
+    procedure WriteText(const s: string);
+    procedure WriteHeaderMagic; virtual;
+    procedure WriteHeaderVersion; virtual;
+    procedure WriteInitialFlags; virtual;
+    procedure WriteSrcFiles; virtual;
+  public
+    constructor Create; virtual;
+    destructor Destroy; override;
+    procedure Clear;
+    procedure WriteModule(aResolver: TPasResolver; aStream: TStream;
+      InitFlags: TPJUInitialFlags); virtual;
+    property Resolver: TPasResolver read FResolver;
+    property Stream: TStream read FStream;
+    property Parser: TPasParser read FParser;
+    property Scanner: TPascalScanner read FScanner;
+    property InitialFlags: TPJUInitialFlags read FInitialFlags;
+  end;
+
+  EPas2JsReadError = class(Exception)
+  public
+    Owner: TObject;
+  end;
 
-  { TPasToJsFiler }
+  { TPasToJsReader }
 
-  TPasToJsFiler = class
+  TPasToJsReader = class
+  private
+    FCur: PByte;
+    FEnd: PByte;
+    FFileVersion: longint;
+    FInitialFlags: TPJUInitialFlags;
+    FParser: TPasParser;
+    FPJU: String;
+    FResolver: TPasResolver;
+    FScanner: TPascalScanner;
+    FSourceFiles: TObjectList;
+  protected
+    procedure RaiseMsg(Id: int64; const Msg: string = '');
+    procedure RaiseEOF(Id: int64);
+    function ReadStr(Cnt: integer): string;
+    function CheckStr(const s: string): boolean;
+    function ReadInt: MaxPrecInt; overload;
+    function ReadInt(LowBound, UpBound: MaxPrecInt): MaxPrecInt; overload;
+    function ReadText: string;
+    procedure ReadHeaderMagic; virtual;
+    procedure ReadHeaderVersion; virtual;
+    procedure ReadInitialFlags; virtual;
+    procedure ReadSrcFiles; virtual;
   public
     constructor Create; virtual;
     destructor Destroy; override;
-    procedure WriteModule(aResolver: TPasResolver); virtual;
+    procedure Clear;
+    procedure ReadModule(aResolver: TPasResolver; aPJU: String); virtual;
+    property Resolver: TPasResolver read FResolver;
+    property Parser: TPasParser read FParser;
+    property Scanner: TPascalScanner read FScanner;
+    property PJU: String read FPJU;
+    property FileVersion: longint read FFileVersion;
+    property InitialFlags: TPJUInitialFlags read FInitialFlags;
   end;
 
+function ComparePJUSrcFiles(File1, File2: Pointer): integer;
+function EncodeVLQ(i: MaxPrecInt): string; overload;
+function EncodeVLQ(i: MaxPrecUInt): string; overload;
+function DecodeVLQ(const s: string): MaxPrecInt; // base256 Variable Length Quantity
+function DecodeVLQ(var p: PByte): MaxPrecInt; // base256 Variable Length Quantity
+
+function ModeSwitchToInt(ms: TModeSwitch): byte;
+
+function dbgmem(const s: string): string; overload;
+function dbgmem(p: PChar; Cnt: integer): string; overload;
+
 implementation
 
-{ TPasToJsFiler }
+function ComparePJUSrcFiles(File1, File2: Pointer): integer;
+var
+  Src1: TPJUSourceFile absolute File1;
+  Src2: TPJUSourceFile absolute File2;
+begin
+  Result:=CompareStr(Src1.Filename,Src2.Filename);
+end;
+
+function EncodeVLQ(i: MaxPrecInt): string;
+{ Convert signed number to base256-VLQ:
+  Each byte has 8bit, where the least significant bit is the continuation bit
+  (1=there is a next byte).
+  The first byte contains the sign bit in the last bit
+  and the 6 most significant bits of the number.
+  For example:
+  0 = %00000000 => 0
+  1 = %00000001 => -0
+  2 = %00000010 => 1
+  130 5 = %10000010 %00000101 = 000010 0000101 = 100000101 = 133
+}
+var
+  digits: integer;
+begin
+  digits:=0;
+  if i<0 then
+    begin
+    if i=Low(MaxPrecInt) then
+      begin
+      Result:=EncodeVLQ(High(MaxPrecInt)+1);
+      Result[1]:=chr(ord(Result[1]) or 1);
+      exit;
+      end;
+    digits:=1;
+    i:=-i;
+    end;
+  inc(digits,(i and %111111) shl 1);
+  i:=i shr 6;
+  if i>0 then
+    inc(digits,%10000000); // need another byte -> set continuation bit
+  Result:=chr(digits);
+  while i>0 do
+    begin
+    digits:=i and %1111111;
+    i:=i shr 7;
+    if i>0 then
+      inc(digits,%10000000); // need another byte -> set continuation bit
+    Result:=Result+chr(digits);
+    end;
+end;
+
+function EncodeVLQ(i: MaxPrecUInt): string;
+var
+  digits: integer;
+begin
+  digits:=(i and %111111) shl 1;
+  if i>0 then
+    inc(digits,%10000000); // need another byte -> set continuation bit
+  Result:=chr(digits);
+  i:=i shr 6;
+  while i>0 do
+    begin
+    digits:=i and %1111111;
+    i:=i shr 7;
+    if i>0 then
+      inc(digits,%10000000); // need another byte -> set continuation bit
+    Result:=Result+chr(digits);
+    end;
+end;
+
+function DecodeVLQ(const s: string): MaxPrecInt;
+var
+  p: PByte;
+begin
+  if s='' then
+    raise EConvertError.Create('DecodeVLQ empty');
+  p:=PByte(s);
+  Result:=DecodeVLQ(p);
+  if p-PByte(s)<>length(s) then
+    raise EConvertError.Create('DecodeVLQ waste');
+end;
+
+function DecodeVLQ(var p: PByte): MaxPrecInt;
+{ Convert base256-VLQ to signed number,
+  For the fomat see EncodeVLQ
+}
+
+  procedure RaiseInvalid;
+  begin
+    raise ERangeError.Create('DecodeVLQ');
+  end;
+
+const
+  MaxShift = 63; // actually log2(High(MaxPrecInt))
+var
+  digit, Shift: Integer;
+  Negated: Boolean;
+begin
+  digit:=p^;
+  inc(p);
+  Negated:=(digit and 1)>0;
+  Result:=(digit shr 1) and %111111;
+  Shift:=6;
+  while digit>=%10000000 do
+    begin
+    digit:=p^;
+    inc(p);
+    if Shift>MaxShift then
+      RaiseInvalid;
+    inc(Result,MaxPrecInt(digit and %1111111) shl Shift);
+    inc(Shift,7);
+    end;
+  if Negated then
+    Result:=-Result;
+end;
+
+function ModeSwitchToInt(ms: TModeSwitch): byte;
+begin
+  case ms of
+    msNone: Result:=0;
+    msFpc: Result:=1;
+    msObjfpc: Result:=2;
+    msDelphi: Result:=3;
+    msDelphiUnicode: Result:=4;
+    msTP7: Result:=5;
+    msMac: Result:=6;
+    msIso: Result:=7;
+    msExtpas: Result:=8;
+    msGPC: Result:=9;
+    msClass: Result:=10;
+    msObjpas: Result:=11;
+    msResult: Result:=12;
+    msStringPchar: Result:=13;
+    msCVarSupport: Result:=14;
+    msNestedComment: Result:=15;
+    msTPProcVar: Result:=16;
+    msMacProcVar: Result:=17;
+    msRepeatForward: Result:=18;
+    msPointer2Procedure: Result:=19;
+    msAutoDeref: Result:=20;
+    msInitFinal: Result:=21;
+    msDefaultAnsistring: Result:=22;
+    msOut: Result:=23;
+    msDefaultPara: Result:=24;
+    msHintDirective: Result:=25;
+    msDuplicateNames: Result:=26;
+    msProperty: Result:=27;
+    msDefaultInline: Result:=28;
+    msExcept: Result:=29;
+    msObjectiveC1: Result:=30;
+    msObjectiveC2: Result:=31;
+    msNestedProcVars: Result:=32;
+    msNonLocalGoto: Result:=33;
+    msAdvancedRecords: Result:=34;
+    msISOLikeUnaryMinus: Result:=35;
+    msSystemCodePage: Result:=36;
+    msFinalFields: Result:=37;
+    msDefaultUnicodestring: Result:=38;
+    msTypeHelpers: Result:=39;
+    msCBlocks: Result:=40;
+    msISOLikeIO: Result:=41;
+    msISOLikeProgramsPara: Result:=42;
+    msISOLikeMod: Result:=43;
+    msExternalClass: Result:=44;
+    msPrefixedAttributes: Result:=45;
+    msIgnoreInterfaces: Result:=46;
+    msIgnoreAttributes: Result:=47;
+  end;
+end;
+
+function dbgmem(const s: string): string;
+begin
+  if s='' then exit('');
+  Result:=dbgmem(PChar(s),length(s));
+end;
+
+function dbgmem(p: PChar; Cnt: integer): string;
+
+  procedure AddLine(const Line: string);
+  begin
+    if Result<>'' then
+      Result:=Result+LineEnding;
+    Result:=Result+Line;
+  end;
+
+var
+  c: Char;
+  IsTxt: boolean;
+  Line: String;
+  i: Integer;
+begin
+  Result:='';
+  if (p=nil) or (Cnt<=0) then exit;
+  Line:='';
+  IsTxt:=false;
+  for i:=0 to Cnt-1 do
+    begin
+    c:=p[i];
+    if c in ['a'..'z','A'..'Z','_','/','0'..'9'] then
+      begin
+      if not IsTxt then
+        begin
+        Line:=Line+'''';
+        IsTxt:=true;
+        end;
+      Line:=Line+c;
+      end
+    else
+      begin
+      if IsTxt then
+        begin
+        Line:=Line+'''';
+        IsTxt:=false;
+        end;
+      Line:=Line+'#'+HexStr(ord(c),2);
+      end;
+    if length(Line)>78 then
+      begin
+      AddLine(Line);
+      Line:='';
+      end;
+    end;
+  if Line<>'' then
+    AddLine(Line);
+end;
+
+{ TPasToJsReader }
+
+procedure TPasToJsReader.RaiseMsg(Id: int64; const Msg: string);
+begin
+  raise EPas2JsReadError.Create('['+IntToStr(Id)+'] '+Msg);
+end;
 
-constructor TPasToJsFiler.Create;
+procedure TPasToJsReader.RaiseEOF(Id: int64);
 begin
+  RaiseMsg(Id,'unexpected EOF');
+end;
+
+function TPasToJsReader.ReadStr(Cnt: integer): string;
+begin
+  if Cnt=0 then exit('');
+  if Cnt>0 then
+    begin
+    if FEnd-FCur<Cnt then
+      RaiseEOF(20180130192845);
+    SetLength(Result,Cnt);
+    System.Move(FCur^,Result[1],Cnt);
+    inc(FCur,Cnt);
+    end
+  else
+    RaiseMsg(20180130193811);
+end;
 
+function TPasToJsReader.CheckStr(const s: string): boolean;
+var
+  l: Integer;
+begin
+  l:=length(s);
+  if l=0 then exit(true);
+  if FEnd-FCur<l then
+    RaiseEOF(20180130201602);
+  if not CompareMem(FCur,@s[1],l) then exit(false);
+  inc(FCur,l);
+  Result:=true;
+end;
+
+function TPasToJsReader.ReadInt: MaxPrecInt;
+begin
+  if FCur=FEnd then
+    RaiseMsg(20180130201047);
+  Result:=DecodeVLQ(FCur); // Note: safe because FEnd^=#0
+  if FCur>FEnd then
+    begin
+    FCur:=FEnd;
+    RaiseMsg(20180130200819);
+    end;
 end;
 
-destructor TPasToJsFiler.Destroy;
+function TPasToJsReader.ReadInt(LowBound, UpBound: MaxPrecInt): MaxPrecInt;
 begin
+  Result:=ReadInt();
+  if Result<LowBound then
+    RaiseMsg(20180130203354)
+  else if Result>UpBound then
+    RaiseMsg(20180130203413);
+end;
+
+function TPasToJsReader.ReadText: string;
+var
+  l: MaxPrecInt;
+begin
+  l:=ReadInt;
+  if l=0 then
+    exit('')
+  else if l<0 then
+    RaiseMsg(20180130200936)
+  else if FEnd-FCur<l then
+    RaiseMsg(20180130200946);
+  SetLength(Result,l);
+  System.Move(FCur^,Result[1],l);
+  inc(FCur,l);
+end;
+
+procedure TPasToJsReader.ReadHeaderMagic;
+begin
+  if not CheckStr(PJUMagic) then
+    RaiseMsg(20180130201710,'not a pju file');
+end;
+
+procedure TPasToJsReader.ReadHeaderVersion;
+begin
+  FFileVersion:=ReadInt;
+  if FFileVersion<1 then
+    RaiseMsg(20180130201801,'invalid pju file version');
+  if FFileVersion>PJUVersion then
+    RaiseMsg(20180130201822,'pju file was created by a newer compiler.');
+end;
+
+procedure TPasToJsReader.ReadInitialFlags;
+begin
+  // Write modeswitches
+  //for ms in InitialFlags.Modeswitches do
+  //  WriteInt(ModeSwitchToInt(ms));
+  //WriteInt(-1);
+  // ToDo: write initial flags: BoolSwitches, used defines, used macros
+end;
+
+procedure TPasToJsReader.ReadSrcFiles;
+var
+  Cnt: MaxPrecInt;
+  i: Integer;
+  CurFile: TPJUSourceFile;
+  CurFilename: String;
+begin
+  if not CheckStr('Files') then
+    RaiseMsg(20180130202024);
+  Cnt:=ReadInt;
+  for i:=0 to Cnt-1 do
+    begin
+    CurFile:=TPJUSourceFile.Create;
+    FSourceFiles.Add(CurFile);
+    CurFile.Kind:=TPJUSourceFileKind(ReadInt(ord(low(TPJUSourceFileKind)),ord(high(TPJUSourceFileKind))));
+    CurFilename:=ReadText;
+    if CurFilename='' then
+      RaiseMsg(20180130203605);
+    if length(CurFilename)>MAX_PATH then
+      RaiseMsg(20180130203624);
+    DoDirSeparators(CurFilename);
+    if CurFilename<>ResolveDots(CurFilename) then
+      RaiseMsg(20180130203841);
+    if ExtractFilenameOnly(CurFilename)='' then
+      RaiseMsg(20180130203924);
+    CurFile.Filename:=CurFilename;
+    CurFile.Checksum:=ReadInt(low(TPJUSourceFileChecksum),high(TPJUSourceFileChecksum));
+    end;
+end;
+
+constructor TPasToJsReader.Create;
+begin
+  FSourceFiles:=TObjectList.Create(true);
+  FInitialFlags:=TPJUInitialFlags.Create;
+end;
+
+destructor TPasToJsReader.Destroy;
+begin
+  FreeAndNil(FInitialFlags);
+  FreeAndNil(FSourceFiles);
   inherited Destroy;
 end;
 
-procedure TPasToJsFiler.WriteModule(aResolver: TPasResolver);
+procedure TPasToJsReader.Clear;
+begin
+  FSourceFiles.Clear;
+  FResolver:=nil;
+  FPJU:='';
+  FInitialFlags.Free;
+  FInitialFlags:=TPJUInitialFlags.Create;
+end;
+
+procedure TPasToJsReader.ReadModule(aResolver: TPasResolver; aPJU: String);
 begin
+  FResolver:=aResolver;
+  FParser:=Resolver.CurrentParser;
+  FScanner:=FParser.Scanner;
+  FPJU:=aPJU;
+  FCur:=PByte(PChar(FPJU));
+  FEnd:=FCur+length(FPJU);
 
+  ReadHeaderMagic;
+  ReadHeaderVersion;
+  ReadSrcFiles;
+  ReadInitialFlags;
+end;
+
+{ TPasToJsWriter }
+
+procedure TPasToJsWriter.WriteStr(const s: string);
+begin
+  if s='' then exit;
+  FStream.Write(s[1],length(s));
+end;
+
+procedure TPasToJsWriter.WriteInt(const i: MaxPrecInt);
+begin
+  WriteStr(EncodeVLQ(i));
+  //writeln('TPasToJsWriter.WriteInt ',i,' ',dbgmem(EncodeVLQ(i)));
+end;
+
+procedure TPasToJsWriter.WriteText(const s: string);
+begin
+  WriteInt(length(s));
+  if s<>'' then
+    WriteStr(s);
+end;
+
+procedure TPasToJsWriter.WriteHeaderMagic;
+begin
+  WriteStr(PJUMagic);
+end;
+
+procedure TPasToJsWriter.WriteHeaderVersion;
+begin
+  WriteInt(PJUVersion);
+end;
+
+procedure TPasToJsWriter.WriteInitialFlags;
+begin
+  // Write modeswitches
+  //for ms in InitialFlags.Modeswitches do
+  //  WriteInt(ModeSwitchToInt(ms));
+  //WriteInt(-1);
+  // ToDo: write initial flags: BoolSwitches, used defines, used macros
+end;
+
+procedure TPasToJsWriter.WriteSrcFiles;
+var
+  CurFile: TPJUSourceFile;
+  List: TFPList;
+  i: Integer;
+begin
+  List:=TFPList.Create;
+  try
+    // get files from scanner
+    for i:=0 to Scanner.Files.Count-1 do
+      begin
+      CurFile:=TPJUSourceFile.Create;
+      CurFile.Index:=i;
+      CurFile.Filename:=Scanner.Files[i];
+      if i=0 then
+        CurFile.Kind:=sfkUnit
+      else
+        CurFile.Kind:=sfkInclude;
+      // ToDo: checksum
+      List.Add(CurFile);
+      end;
+
+    // create FSourceFilesSorted;
+    List.Sort(@ComparePJUSrcFiles);
+    SetLength(FSourceFilesSorted,List.Count);
+    for i:=0 to List.Count-1 do
+      FSourceFilesSorted[i]:=TPJUSourceFile(List[i]);
+
+    // write
+    WriteStr('Files');
+    WriteInt(FSourceFiles.Count);
+    for i:=0 to FSourceFiles.Count-1 do
+      begin
+      CurFile:=TPJUSourceFile(FSourceFiles[i]);
+      WriteInt(ord(CurFile.Kind));
+      WriteText(CurFile.Filename);
+      WriteInt(CurFile.Checksum);
+      end;
+  finally
+    List.Free;
+  end;
+end;
+
+constructor TPasToJsWriter.Create;
+begin
+  FSourceFiles:=TObjectList.Create(true);
+end;
+
+destructor TPasToJsWriter.Destroy;
+begin
+  Clear;
+  FreeAndNil(FSourceFiles);
+  inherited Destroy;
+end;
+
+procedure TPasToJsWriter.Clear;
+begin
+  FSourceFiles.Clear;
+  FResolver:=nil;
+  FParser:=nil;
+  FScanner:=nil;
+  FStream:=nil;
+  FInitialFlags:=nil;
+end;
+
+procedure TPasToJsWriter.WriteModule(aResolver: TPasResolver; aStream: TStream;
+  InitFlags: TPJUInitialFlags);
+begin
+  FResolver:=aResolver;
+  FParser:=Resolver.CurrentParser;
+  FScanner:=FParser.Scanner;
+  FStream:=aStream;
+  FInitialFlags:=InitFlags;
+  try
+    WriteHeaderMagic;
+    WriteHeaderVersion;
+    WriteInitialFlags;
+    WriteSrcFiles;
+    // ToDo: WriteUsedModulesPrecompiledChecksums;
+    // ToDo: WriteModule;
+    // ToDo: write final flags: modeswitches, boolswitches, used defines
+  finally
+    FStream:=nil;
+  end;
 end;
 
 end.

+ 124 - 7
packages/pastojs/tests/tcfiler.pas

@@ -24,8 +24,8 @@ interface
 
 uses
   Classes, SysUtils, fpcunit, testregistry,
-  PasTree, PScanner, PasResolver,
-  FPPas2Js,
+  PasTree, PScanner, PasResolver, PasResolveEval, PParser,
+  FPPas2Js, Pas2JsFiler,
   tcmodules;
 
 type
@@ -33,17 +33,26 @@ type
   { TCustomTestPrecompile }
 
   TCustomTestPrecompile = Class(TCustomTestModule)
+  private
+    FInitialFlags: TPJUInitialFlags;
+    FPJUReader: TPasToJsReader;
+    FPJUWriter: TPasToJsWriter;
   protected
     procedure SetUp; override;
     procedure TearDown; override;
-    procedure WriteUnit; virtual;
+    procedure WriteReadUnit; virtual;
+    procedure StartParsing; override;
   public
+    property PJUWriter: TPasToJsWriter read FPJUWriter write FPJUWriter;
+    property PJUReader: TPasToJsReader read FPJUReader write FPJUReader;
+    property InitialFlags: TPJUInitialFlags read FInitialFlags;
   end;
 
   { TTestPrecompile }
 
   TTestPrecompile = class(TCustomTestPrecompile)
   published
+    procedure Test_Base256VLQ;
     procedure TestPC_EmptyUnit;
   end;
 
@@ -54,29 +63,137 @@ implementation
 procedure TCustomTestPrecompile.SetUp;
 begin
   inherited SetUp;
-
+  FInitialFlags:=TPJUInitialFlags.Create;
 end;
 
 procedure TCustomTestPrecompile.TearDown;
 begin
-
+  FreeAndNil(FPJUWriter);
+  FreeAndNil(FPJUReader);
+  FreeAndNil(FInitialFlags);
   inherited TearDown;
 end;
 
-procedure TCustomTestPrecompile.WriteUnit;
+procedure TCustomTestPrecompile.WriteReadUnit;
+var
+  ms: TMemoryStream;
+  PJU: string;
+  ReadResolver: TPasResolver;
+  ReadFileResolver: TFileResolver;
+  ReadScanner: TPascalScanner;
+  ReadParser: TPasParser;
 begin
+  FPJUWriter:=TPasToJsWriter.Create;
+  FPJUReader:=TPasToJsReader.Create;
+  ms:=TMemoryStream.Create;
+  try
+    try
+      PJUWriter.WriteModule(Engine,ms,InitialFlags);
+    except
+      on E: Exception do
+      begin
+        {$IFDEF VerbosePas2JS}
+        writeln('TCustomTestPrecompile.WriteReadUnit WRITE failed');
+        {$ENDIF}
+        Fail('Write failed: '+E.Message);
+      end;
+    end;
+
+    try
+      SetLength(PJU,ms.Size);
+      System.Move(ms.Memory^,PJU[1],length(PJU));
+
+      writeln('TCustomTestPrecompile.WriteReadUnit PJU START-----');
+      writeln(dbgmem(PJU));
+      writeln('TCustomTestPrecompile.WriteReadUnit PJU END-------');
+
+      ReadFileResolver:=TFileResolver.Create;
+      ReadScanner:=TPascalScanner.Create(ReadFileResolver);
+      ReadResolver:=TPasResolver.Create;
+      ReadParser:=TPasParser.Create(ReadScanner,ReadFileResolver,ReadResolver);
+      ReadResolver.CurrentParser:=ReadParser;
+      try
+        PJUReader.ReadModule(ReadResolver,PJU);
+      finally
+        ReadParser.Free;
+        ReadScanner.Free;
+        ReadResolver.Free; // free parser before resolver
+        ReadFileResolver.Free;
+      end;
+    except
+      on E: Exception do
+      begin
+        {$IFDEF VerbosePas2JS}
+        writeln('TCustomTestPrecompile.WriteReadUnit READ failed');
+        {$ENDIF}
+        Fail('Read failed: '+E.Message);
+      end;
+    end;
+  finally
+    ms.Free;
+  end;
+end;
 
+procedure TCustomTestPrecompile.StartParsing;
+begin
+  inherited StartParsing;
+  FInitialFlags.ParserOptions:=Parser.Options;
+  FInitialFlags.ModeSwitches:=Scanner.CurrentModeSwitches;
+  FInitialFlags.BoolSwitches:=Scanner.CurrentBoolSwitches;
+  // ToDo: defines
+  FInitialFlags.ResolverOptions:=Engine.Options;
+  FInitialFlags.PasTojsOptions:=Converter.Options;
+  FInitialFlags.TargetPlatform:=Converter.TargetPlatform;
+  FInitialFlags.TargetProcessor:=Converter.TargetProcessor;
 end;
 
 { TTestPrecompile }
 
+procedure TTestPrecompile.Test_Base256VLQ;
+
+  procedure Test(i: MaxPrecInt);
+  var
+    s: String;
+    p: PByte;
+    j: NativeInt;
+  begin
+    s:=EncodeVLQ(i);
+    p:=PByte(s);
+    j:=DecodeVLQ(p);
+    if i<>j then
+      Fail('Encode/DecodeVLQ OrigIndex='+IntToStr(i)+' Code="'+s+'" NewIndex='+IntToStr(j));
+  end;
+
+  procedure TestStr(i: MaxPrecInt; Expected: string);
+  var
+    Actual: String;
+  begin
+    Actual:=EncodeVLQ(i);
+    AssertEquals('EncodeVLQ('+IntToStr(i)+')',Expected,Actual);
+  end;
+
+var
+  i: Integer;
+begin
+  TestStr(0,#0);
+  TestStr(1,#2);
+  TestStr(-1,#3);
+  for i:=-8200 to 8200 do
+    Test(i);
+  Test(High(MaxPrecInt));
+  Test(High(MaxPrecInt)-1);
+  Test(Low(MaxPrecInt)+2);
+  Test(Low(MaxPrecInt)+1);
+  //Test(Low(MaxPrecInt)); such a high number is not needed by pastojs
+end;
+
 procedure TTestPrecompile.TestPC_EmptyUnit;
 begin
   StartUnit(false);
   Add('interface');
   Add('implementation');
   ConvertUnit;
-  WriteUnit;
+  WriteReadUnit;
 end;
 
 Initialization