浏览代码

fcl-js: added TSourceMap

git-svn-id: trunk@36492 -
Mattias Gaertner 8 年之前
父节点
当前提交
2df7e116e3

+ 2 - 0
.gitattributes

@@ -2511,11 +2511,13 @@ packages/fcl-js/src/jsbase.pp svneol=native#text/plain
 packages/fcl-js/src/jsminifier.pp svneol=native#text/plain
 packages/fcl-js/src/jsminifier.pp svneol=native#text/plain
 packages/fcl-js/src/jsparser.pp svneol=native#text/plain
 packages/fcl-js/src/jsparser.pp svneol=native#text/plain
 packages/fcl-js/src/jsscanner.pp svneol=native#text/plain
 packages/fcl-js/src/jsscanner.pp svneol=native#text/plain
+packages/fcl-js/src/jssrcmap.pas svneol=native#text/plain
 packages/fcl-js/src/jstoken.pp svneol=native#text/plain
 packages/fcl-js/src/jstoken.pp svneol=native#text/plain
 packages/fcl-js/src/jstree.pp svneol=native#text/plain
 packages/fcl-js/src/jstree.pp svneol=native#text/plain
 packages/fcl-js/src/jswriter.pp svneol=native#text/plain
 packages/fcl-js/src/jswriter.pp svneol=native#text/plain
 packages/fcl-js/tests/tcparser.pp svneol=native#text/plain
 packages/fcl-js/tests/tcparser.pp svneol=native#text/plain
 packages/fcl-js/tests/tcscanner.pp svneol=native#text/plain
 packages/fcl-js/tests/tcscanner.pp svneol=native#text/plain
+packages/fcl-js/tests/tcsrcmap.pas svneol=native#text/plain
 packages/fcl-js/tests/tcwriter.pp svneol=native#text/plain
 packages/fcl-js/tests/tcwriter.pp svneol=native#text/plain
 packages/fcl-js/tests/testjs.ico -text
 packages/fcl-js/tests/testjs.ico -text
 packages/fcl-js/tests/testjs.lpi svneol=native#text/plain
 packages/fcl-js/tests/testjs.lpi svneol=native#text/plain

+ 602 - 0
packages/fcl-js/src/jssrcmap.pas

@@ -0,0 +1,602 @@
+{ *********************************************************************
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2015 Mattias Gaertner.
+
+    Javascript Source Map
+
+    See Source Maps Revision 3:
+    https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?hl=en_US&pli=1&pli=1#
+
+    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.
+
+  **********************************************************************}
+unit JSSrcMap;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, contnrs, fpjson;
+
+const
+  Base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+  DefaultSrcMapHeader = ')]}'+LineEnding;
+
+type
+
+  { TSourceMapSegment }
+
+  TSourceMapSegment = class
+  public
+    Index: integer; // index in FNodes
+    GeneratedLine: integer;
+    GeneratedColumn: integer;
+    SrcFileIndex: integer; // index in FSources
+    SrcLine: integer;
+    SrcColumn: integer;
+    NameIndex: integer; // index in FNames
+  end;
+
+  TSourceMapSrc = class
+  public
+    Filename: string;
+    Source: String;
+  end;
+
+  { TSourceMap }
+
+  TSourceMap = class
+  private
+    type
+
+      { TStringToIndex }
+
+      TStringToIndex = class
+      private
+        FItems: TFPHashList;
+      public
+        constructor Create;
+        destructor Destroy; override;
+        procedure Clear;
+        procedure Add(const Value: String; Index: integer);
+        function FindValue(const Value: String): integer;
+      end;
+  private
+    FAddMonotonous: boolean;
+    FHeader: String;
+    FGeneratedFilename: string;
+    FNames: TStrings; // in adding order
+    FNameToIndex: TStringToIndex; // name to index in FNames
+    FItems: TFPList; // TSourceMapSegment, in adding order
+    FSourceRoot: string;
+    FSources: TFPList; // list of TSourceMapSrc, in adding order
+    FSourceToIndex: TStringToIndex; // srcfile to index in FSources
+    FVersion: integer;
+    function GetNames(Index: integer): string;
+    function GetItems(Index: integer): TSourceMapSegment;
+    function GetSourceContents(Index: integer): String;
+    function GetSourceFiles(Index: integer): String;
+    procedure SetGeneratedFilename(const AValue: string);
+    procedure SetSourceContents(Index: integer; const AValue: String);
+  public
+    constructor Create(const aGeneratedFilename: string);
+    destructor Destroy; override;
+    procedure Clear; virtual;
+    function AddMapping(
+      GeneratedLine: integer; // 1-based
+      GeneratedCol: integer = 0; // 0-based
+      const SourceFile: string = ''; // can be empty ''
+      SrcLine: integer = 1; // 1-based
+      SrcCol: integer = 0; // 0-based
+      const Name: String = ''): TSourceMapSegment; virtual;
+    property AddMonotonous: boolean read FAddMonotonous
+      write FAddMonotonous default true;// true = AddMapping GeneratedLine/Col must be behind last add, false = check all adds for duplicate
+    function CreateMappings: String; virtual;
+    function ToJSON: TJSONObject; virtual;
+    procedure SaveToStream(aStream: TStream); virtual;
+    procedure SaveToFile(Filename: string); virtual;
+    function ToString: string; override;
+    property GeneratedFilename: string read FGeneratedFilename write SetGeneratedFilename;
+    function IndexOfName(const Name: string; AddIfNotExists: boolean = false): integer;
+    function IndexOfSourceFile(const SrcFile: string; AddIfNotExists: boolean = false): integer;
+    function Count: integer;
+    property Items[Index: integer]: TSourceMapSegment read GetItems; default; // segments
+    function SourceCount: integer;
+    property SourceRoot: string read FSourceRoot write FSourceRoot;
+    property SourceFiles[Index: integer]: String read GetSourceFiles;
+    property SourceContents[Index: integer]: String read GetSourceContents write SetSourceContents;
+    function NameCount: integer;
+    property Names[Index: integer]: string read GetNames;
+    property Version: integer read FVersion; // 3
+    property Header: String read FHeader write FHeader; // DefaultSrcMapHeader
+  end;
+
+function EncodeBase64VLQ(i: NativeInt): String; // base64 Variable Length Quantity
+function DecodeBase64VLQ(const s: string): NativeInt; // base64 Variable Length Quantity
+function DecodeBase64VLQ(var p: PChar): NativeInt; // base64 Variable Length Quantity
+
+implementation
+
+function EncodeBase64VLQ(i: NativeInt): String;
+{ Convert signed number to base64-VLQ:
+  Each base64 has 6bit, where the most significant bit is the continuation bit
+  (1=there is a next base64 character).
+  The first character contains the 5 least significant bits of the number.
+  The last bit of the first character is the sign bit (1=negative).
+  For example:
+  A = 0 = %000000 => 0
+  B = 1 = %000001 => -0
+  C = 2 = %000010 => 1
+  iF = 34 5 = %100010 %000101 = 00010 00101 = 1000101 = 69
+}
+
+  procedure RaiseRange;
+  begin
+    raise ERangeError.Create('EncodeBase64VLQ');
+  end;
+
+var
+  digits: NativeInt;
+begin
+  Result:='';
+  if i<0 then
+    begin
+    i:=-i;
+    if i>(High(NativeInt)-1) shr 1 then
+      RaiseRange;
+    i:=(i shl 1)+1;
+    end
+  else
+    begin
+    if i>High(NativeInt) shr 1 then
+      RaiseRange;
+    i:=i shl 1;
+    end;
+  repeat
+    digits:=i and %11111;
+    i:=i shr 5;
+    if i>0 then
+      inc(digits,%100000); // need another char -> set continuation bit
+    Result:=Result+Base64Chars[digits+1];
+  until i=0;
+end;
+
+function DecodeBase64VLQ(const s: string): NativeInt;
+var
+  p: PChar;
+begin
+  if s='' then
+    raise EConvertError.Create('DecodeBase64VLQ empty');
+  p:=PChar(s);
+  Result:=DecodeBase64VLQ(p);
+  if p-PChar(s)<>length(s) then
+    raise EConvertError.Create('DecodeBase64VLQ waste');
+end;
+
+function DecodeBase64VLQ(var p: PChar): NativeInt;
+{ Convert base64-VLQ to signed number,
+  For the fomat see EncodeBase64VLQ
+}
+
+  procedure RaiseInvalid;
+  begin
+    raise ERangeError.Create('DecodeBase64VLQ');
+  end;
+
+const
+  MaxShift = 63-5; // actually log2(High(NativeInt))-5
+var
+  c: Char;
+  digit, Shift: Integer;
+begin
+  Result:=0;
+  Shift:=0;
+  repeat
+    c:=p^;
+    case c of
+    'A'..'Z': digit:=ord(c)-ord('A');
+    'a'..'z': digit:=ord(c)-ord('a')+26;
+    '0'..'9': digit:=ord(c)-ord('0')+52;
+    '+': digit:=62;
+    '/': digit:=63;
+    else RaiseInvalid;
+    end;
+    inc(p);
+    if Shift>MaxShift then
+      RaiseInvalid;
+    inc(Result,(digit and %11111) shl Shift);
+    inc(Shift,5);
+  until digit<%100000;
+  if (Result and 1)>0 then
+    Result:=-(Result shr 1)
+  else
+    Result:=Result shr 1;
+end;
+
+{ TSourceMap.TStringToIndex }
+
+constructor TSourceMap.TStringToIndex.Create;
+begin
+  FItems:=TFPHashList.Create;
+end;
+
+destructor TSourceMap.TStringToIndex.Destroy;
+begin
+  FItems.Clear;
+  FreeAndNil(FItems);
+  inherited Destroy;
+end;
+
+procedure TSourceMap.TStringToIndex.Clear;
+begin
+  FItems.Clear;
+end;
+
+procedure TSourceMap.TStringToIndex.Add(const Value: String; Index: integer);
+begin
+  // Note: nil=0 means not found in TFPHashList
+  FItems.Add(Value,{%H-}Pointer(PtrInt(Index+1)));
+end;
+
+function TSourceMap.TStringToIndex.FindValue(const Value: String
+  ): integer;
+begin
+  // Note: nil=0 means not found in TFPHashList
+  Result:=integer({%H-}PtrInt(FItems.Find(Value)))-1;
+end;
+
+{ TSourceMap }
+
+procedure TSourceMap.SetGeneratedFilename(const AValue: string);
+begin
+  if FGeneratedFilename=AValue then Exit;
+  FGeneratedFilename:=AValue;
+end;
+
+procedure TSourceMap.SetSourceContents(Index: integer; const AValue: String);
+begin
+  TSourceMapSrc(FSources[Index]).Source:=AValue;
+end;
+
+function TSourceMap.GetItems(Index: integer): TSourceMapSegment;
+begin
+  Result:=TSourceMapSegment(FItems[Index]);
+end;
+
+function TSourceMap.GetSourceContents(Index: integer): String;
+begin
+  Result:=TSourceMapSrc(FSources[Index]).Source;
+end;
+
+function TSourceMap.GetNames(Index: integer): string;
+begin
+  Result:=FNames[Index];
+end;
+
+function TSourceMap.GetSourceFiles(Index: integer): String;
+begin
+  Result:=TSourceMapSrc(FSources[Index]).Filename;
+end;
+
+constructor TSourceMap.Create(const aGeneratedFilename: string);
+begin
+  FVersion:=3;
+  FNames:=TStringList.Create;
+  FNameToIndex:=TStringToIndex.Create;
+  FItems:=TFPList.Create;
+  FSources:=TFPList.Create;
+  FSourceToIndex:=TStringToIndex.Create;
+  FAddMonotonous:=true;
+  FHeader:=DefaultSrcMapHeader;
+  GeneratedFilename:=aGeneratedFilename;
+end;
+
+destructor TSourceMap.Destroy;
+begin
+  Clear;
+  FreeAndNil(FSourceToIndex);
+  FreeAndNil(FSources);
+  FreeAndNil(FItems);
+  FreeAndNil(FNameToIndex);
+  FreeAndNil(FNames);
+  inherited Destroy;
+end;
+
+procedure TSourceMap.Clear;
+var
+  i: Integer;
+begin
+  FSourceToIndex.Clear;
+  for i:=0 to FSources.Count-1 do
+    TObject(FSources[i]).Free;
+  FSources.Clear;
+  for i:=0 to FItems.Count-1 do
+    TObject(FItems[i]).Free;
+  FItems.Clear;
+  FNameToIndex.Clear;
+  FNames.Clear;
+end;
+
+function TSourceMap.AddMapping(GeneratedLine: integer; GeneratedCol: integer;
+  const SourceFile: string; SrcLine: integer; SrcCol: integer;
+  const Name: String): TSourceMapSegment;
+
+  procedure RaiseInvalid(Msg: string);
+  begin
+    raise Exception.CreateFmt('%s (GeneratedLine=%d GeneratedCol=%d SrcFile="%s" SrcLine=%d SrcCol=%d Name="%s")',
+      [Msg,GeneratedLine,GeneratedCol,SourceFile,SrcLine,SrcCol,Name]);
+  end;
+
+var
+  NodeCnt, i: Integer;
+  OtherNode: TSourceMapSegment;
+begin
+  if GeneratedLine<1 then
+    RaiseInvalid('invalid GeneratedLine');
+  if GeneratedCol<0 then
+    RaiseInvalid('invalid GeneratedCol');
+  if SourceFile='' then
+    begin
+    if Count=0 then
+      RaiseInvalid('missing source file');
+    if SrcLine<>1 then
+      RaiseInvalid('invalid SrcLine');
+    if SrcCol<>0 then
+      RaiseInvalid('invalid SrcCol');
+    if Name<>'' then
+      RaiseInvalid('invalid Name');
+    end
+  else
+    begin
+    if SrcLine<1 then
+      RaiseInvalid('invalid SrcLine');
+    if SrcCol<0 then
+      RaiseInvalid('invalid SrcCol');
+    end;
+
+  // check if generated line/col already exists
+  NodeCnt:=Count;
+  if AddMonotonous then
+    begin
+    if NodeCnt>0 then
+      begin
+      OtherNode:=Items[NodeCnt-1];
+      if (OtherNode.GeneratedLine>GeneratedLine)
+          or ((OtherNode.GeneratedLine=GeneratedLine)
+            and (OtherNode.GeneratedColumn>GeneratedCol)) then
+        RaiseInvalid('GeneratedLine/Col not monotonous');
+      // Note: same line/col is allowed
+      end;
+    end
+  else
+    begin
+    for i:=0 to NodeCnt-1 do
+      begin
+      OtherNode:=Items[i];
+      if (OtherNode.GeneratedLine=GeneratedLine) and (OtherNode.GeneratedColumn=GeneratedCol) then
+        RaiseInvalid('duplicate GeneratedLine/Col');
+      end;
+    end;
+
+  // add
+  Result:=TSourceMapSegment.Create;
+  Result.Index:=FItems.Count;
+  Result.GeneratedLine:=GeneratedLine;
+  Result.GeneratedColumn:=GeneratedCol;
+  if SourceFile='' then
+    Result.SrcFileIndex:=-1
+  else
+    Result.SrcFileIndex:=IndexOfSourceFile(SourceFile,true);
+  Result.SrcLine:=SrcLine;
+  Result.SrcColumn:=SrcCol;
+  if Name<>'' then
+    Result.NameIndex:=IndexOfName(Name,true)
+  else
+    Result.NameIndex:=-1;
+  FItems.Add(Result);
+end;
+
+function TSourceMap.CreateMappings: String;
+
+  procedure Add(ms: TMemoryStream; const s: string);
+  begin
+    if s<>'' then
+      ms.Write(s[1],length(s));
+  end;
+
+var
+  ms: TMemoryStream;
+  i, LastGeneratedLine, LastGeneratedColumn, j, LastSrcFileIndex, LastSrcLine,
+    LastSrcColumn, SrcLine, LastNameIndex: Integer;
+  Item: TSourceMapSegment;
+begin
+  Result:='';
+  LastGeneratedLine:=1;
+  LastGeneratedColumn:=0;
+  LastSrcFileIndex:=0;
+  LastSrcLine:=0;
+  LastSrcColumn:=0;
+  LastNameIndex:=0;
+  ms:=TMemoryStream.Create;
+  try
+    for i:=0 to Count-1 do
+      begin
+      Item:=Items[i];
+      if LastGeneratedLine<Item.GeneratedLine then
+        begin
+        // new line
+        LastGeneratedColumn:=0;
+        for j:=LastGeneratedLine+1 to Item.GeneratedLine do
+          ms.WriteByte(ord(';'));
+        LastGeneratedLine:=Item.GeneratedLine;
+        end
+      else if i>0 then
+        begin
+        // not the first segment
+        if (LastGeneratedLine=Item.GeneratedLine)
+            and (LastGeneratedColumn=Item.GeneratedColumn) then
+          continue;
+        ms.WriteByte(ord(','));
+        end;
+      // column diff
+      Add(ms,EncodeBase64VLQ(Item.GeneratedColumn-LastGeneratedColumn));
+      LastGeneratedColumn:=Item.GeneratedColumn;
+
+      if Item.SrcFileIndex<0 then
+        continue; // no source -> segment length 1
+      // src file index diff
+      Add(ms,EncodeBase64VLQ(Item.SrcFileIndex-LastSrcFileIndex));
+      LastSrcFileIndex:=Item.SrcFileIndex;
+      // src line diff
+      SrcLine:=Item.SrcLine-1; // 0 based in version 3
+      Add(ms,EncodeBase64VLQ(SrcLine-LastSrcLine));
+      LastSrcLine:=SrcLine;
+      // src column diff
+      Add(ms,EncodeBase64VLQ(Item.SrcColumn-LastSrcColumn));
+      LastSrcColumn:=Item.SrcColumn;
+      // name index
+      if Item.NameIndex<0 then
+        continue; // no name -> segment length 4
+      Add(ms,EncodeBase64VLQ(Item.NameIndex-LastNameIndex));
+      LastNameIndex:=Item.NameIndex;
+      end;
+    SetLength(Result,ms.Size);
+    if Result<>'' then
+      Move(ms.Memory^,Result[1],ms.Size);
+  finally
+    ms.Free;
+  end;
+end;
+
+function TSourceMap.ToJSON: TJSONObject;
+var
+  Obj: TJSONObject;
+  i: Integer;
+  Arr: TJSONArray;
+  Mappings: String;
+begin
+  Result:=nil;
+  Mappings:=CreateMappings;
+
+  Obj:=TJSONObject.Create;
+  try
+    // "version" - integer
+    Obj.Add('version',Version);
+
+    // "file" - GeneratedFilename
+    if GeneratedFilename<>'' then
+      Obj.Add('file',GeneratedFilename);
+
+    // "sourceRoot" - SourceRoot
+    if SourceRoot<>'' then
+      Obj.Add('sourceRoot',SourceRoot);
+
+    // "sources" - array of filenames
+    Arr:=TJSONArray.Create;
+    Obj.Add('sources',Arr);
+    for i:=0 to SourceCount-1 do
+      Arr.Add(SourceFiles[i]);
+
+    // "names" - array of names
+    Arr:=TJSONArray.Create;
+    Obj.Add('names',Arr);
+    for i:=0 to NameCount-1 do
+      Arr.Add(Names[i]);
+
+    // "mappings" - string
+    Obj.Add('mappings',Mappings);
+
+    Result:=Obj;
+  finally
+    if Result=nil then
+      Obj.Free;
+  end;
+end;
+
+procedure TSourceMap.SaveToStream(aStream: TStream);
+var
+  Obj: TJSONObject;
+begin
+  Obj:=ToJSON;
+  try
+    if Header<>'' then
+      aStream.Write(Header[1],length(Header));
+    Obj.DumpJSON(aStream);
+  finally
+    Obj.Free;
+  end;
+end;
+
+procedure TSourceMap.SaveToFile(Filename: string);
+var
+  TheStream: TMemoryStream;
+begin
+  TheStream:=TMemoryStream.Create;
+  try
+    SaveToStream(TheStream);
+    TheStream.Position:=0;
+    TheStream.SaveToFile(Filename);
+  finally
+    TheStream.Free;
+  end;
+end;
+
+function TSourceMap.ToString: string;
+var
+  Obj: TJSONObject;
+begin
+  Obj:=ToJSON;
+  try
+    Result:=Header+Obj.AsJSON;
+  finally
+    Obj.Free;
+  end;
+end;
+
+function TSourceMap.IndexOfName(const Name: string; AddIfNotExists: boolean
+  ): integer;
+begin
+  Result:=FNameToIndex.FindValue(Name);
+  if (Result>=0) or not AddIfNotExists then exit;
+  Result:=FNames.Count;
+  FNames.Add(Name);
+  FNameToIndex.Add(Name,Result);
+end;
+
+function TSourceMap.IndexOfSourceFile(const SrcFile: string;
+  AddIfNotExists: boolean): integer;
+var
+  Src: TSourceMapSrc;
+begin
+  Result:=FSourceToIndex.FindValue(SrcFile);
+  if (Result>=0) or not AddIfNotExists then exit;
+  Src:=TSourceMapSrc.Create;
+  Src.Filename:=SrcFile;
+  Result:=FSources.Count;
+  FSources.Add(Src);
+  FSourceToIndex.Add(SrcFile,Result);
+end;
+
+function TSourceMap.Count: integer;
+begin
+  Result:=FItems.Count;
+end;
+
+function TSourceMap.SourceCount: integer;
+begin
+  Result:=FSources.Count;
+end;
+
+function TSourceMap.NameCount: integer;
+begin
+  Result:=FNames.Count;
+end;
+
+end.
+

+ 126 - 25
packages/fcl-js/src/jswriter.pp

@@ -23,15 +23,25 @@ uses
   SysUtils, jstoken, jsbase, jstree;
   SysUtils, jstoken, jsbase, jstree;
 
 
 Type
 Type
+  TTextWriter = class;
+
+  TTextWriterWriting = procedure(Sender: TTextWriter) of object;
 
 
   { TTextWriter }
   { TTextWriter }
 
 
   TTextWriter = Class(TObject)
   TTextWriter = Class(TObject)
+  private
+    FCurElement: TJSElement;
+    FCurLine: integer;
+    FCurColumn: integer;
+    FOnWriting: TTextWriterWriting;
   protected
   protected
     Function DoWrite(Const S : AnsiString) : Integer; virtual; abstract;
     Function DoWrite(Const S : AnsiString) : Integer; virtual; abstract;
     Function DoWrite(Const S : UnicodeString) : Integer; virtual; abstract;
     Function DoWrite(Const S : UnicodeString) : Integer; virtual; abstract;
+    Procedure Writing; // called before adding new characters
   Public
   Public
     // All functions return the number of bytes copied to output stream.
     // All functions return the number of bytes copied to output stream.
+    constructor Create;
     Function Write(Const S : UnicodeString) : Integer;
     Function Write(Const S : UnicodeString) : Integer;
     Function Write(Const S : AnsiString) : Integer;
     Function Write(Const S : AnsiString) : Integer;
     Function WriteLn(Const S : AnsiString) : Integer;
     Function WriteLn(Const S : AnsiString) : Integer;
@@ -39,6 +49,10 @@ Type
     Function WriteLn(Const Fmt : AnsiString; Args : Array of const) : Integer;
     Function WriteLn(Const Fmt : AnsiString; Args : Array of const) : Integer;
     Function Write(Const Args : Array of const) : Integer;
     Function Write(Const Args : Array of const) : Integer;
     Function WriteLn(Const Args : Array of const) : Integer;
     Function WriteLn(Const Args : Array of const) : Integer;
+    Property CurLine: integer read FCurLine write FCurLine;
+    Property CurColumn: integer read FCurColumn write FCurColumn;// char index, not codepoint
+    Property CurElement: TJSElement read FCurElement write FCurElement;
+    Property OnWriting: TTextWriterWriting read FOnWriting write FOnWriting;
   end;
   end;
 
 
   { TFileWriter }
   { TFileWriter }
@@ -105,14 +119,14 @@ Type
   TJSWriter = Class
   TJSWriter = Class
   private
   private
     FCurIndent : Integer;
     FCurIndent : Integer;
-    FLinePos : Integer;
-    FIndentSize: Byte;
+    FFreeWriter : Boolean;
     FIndentChar : Char;
     FIndentChar : Char;
+    FIndentSize: Byte;
+    FLinePos : Integer;
     FOptions: TWriteOptions;
     FOptions: TWriteOptions;
-    FWriter: TTextWriter;
-    FFreeWriter : Boolean;
     FSkipCurlyBrackets : Boolean;
     FSkipCurlyBrackets : Boolean;
     FSkipRoundBrackets : Boolean;
     FSkipRoundBrackets : Boolean;
+    FWriter: TTextWriter;
     function GetUseUTF8: Boolean;
     function GetUseUTF8: Boolean;
     procedure SetOptions(AValue: TWriteOptions);
     procedure SetOptions(AValue: TWriteOptions);
   Protected
   Protected
@@ -254,7 +268,7 @@ begin
   MinLen:=Result+FBufPos;
   MinLen:=Result+FBufPos;
   If (MinLen>Capacity) then
   If (MinLen>Capacity) then
     begin
     begin
-    DesLen:=Round(FCapacity*1.25);
+    DesLen:=(FCapacity*5) div 4;
     if DesLen>MinLen then
     if DesLen>MinLen then
       MinLen:=DesLen;
       MinLen:=DesLen;
     Capacity:=MinLen;
     Capacity:=MinLen;
@@ -274,7 +288,7 @@ begin
   MinLen:=Result+FBufPos;
   MinLen:=Result+FBufPos;
   If (MinLen>Capacity) then
   If (MinLen>Capacity) then
     begin
     begin
-    DesLen:=Round(FCapacity*1.25);
+    DesLen:=(FCapacity*5) div 4;
     if DesLen>MinLen then
     if DesLen>MinLen then
       MinLen:=DesLen;
       MinLen:=DesLen;
     Capacity:=MinLen;
     Capacity:=MinLen;
@@ -285,6 +299,7 @@ end;
 
 
 Constructor TBufferWriter.Create(Const ACapacity: Cardinal);
 Constructor TBufferWriter.Create(Const ACapacity: Cardinal);
 begin
 begin
+  inherited Create;
   Capacity:=ACapacity;
   Capacity:=ACapacity;
 end;
 end;
 
 
@@ -673,7 +688,9 @@ begin
   if El is TJSPrimaryExpressionThis then
   if El is TJSPrimaryExpressionThis then
     Write('this')
     Write('this')
   else if El is TJSPrimaryExpressionIdent then
   else if El is TJSPrimaryExpressionIdent then
-    Write(TJSPrimaryExpressionIdent(El).Name);
+    Write(TJSPrimaryExpressionIdent(El).Name)
+  else
+    Error(SErrUnknownJSClass,[El.ClassName]);
 end;
 end;
 
 
 procedure TJSWriter.WriteArrayLiteral(El: TJSArrayLiteral);
 procedure TJSWriter.WriteArrayLiteral(El: TJSArrayLiteral);
@@ -777,6 +794,7 @@ procedure TJSWriter.WriteMemberExpression(El: TJSMemberExpression);
 
 
 var
 var
   MExpr: TJSElement;
   MExpr: TJSElement;
+  Args: TJSArguments;
 begin
 begin
   if El is TJSNewMemberExpression then
   if El is TJSNewMemberExpression then
     Write('new ');
     Write('new ');
@@ -809,8 +827,12 @@ begin
     end
     end
   else if (El is TJSNewMemberExpression) then
   else if (El is TJSNewMemberExpression) then
     begin
     begin
-    if (Assigned(TJSNewMemberExpression(El).Args)) then
-      WriteArrayLiteral(TJSNewMemberExpression(El).Args)
+    Args:=TJSNewMemberExpression(El).Args;
+    if Assigned(Args) then
+      begin
+      Writer.CurElement:=Args;
+      WriteArrayLiteral(Args);
+      end
     else
     else
       Write('()');
       Write('()');
     end;
     end;
@@ -821,7 +843,10 @@ procedure TJSWriter.WriteCallExpression(El: TJSCallExpression);
 begin
 begin
   WriteJS(El.Expr);
   WriteJS(El.Expr);
   if Assigned(El.Args) then
   if Assigned(El.Args) then
-    WriteArrayLiteral(El.Args)
+    begin
+    Writer.CurElement:=El.Args;
+    WriteArrayLiteral(El.Args);
+    end
   else
   else
     Write('()');
     Write('()');
 end;
 end;
@@ -1219,23 +1244,23 @@ Var
   TN : TJSString;
   TN : TJSString;
 
 
 begin
 begin
-  TN:=EL.TargetName;
+  TN:=El.TargetName;
   if (El is TJSForStatement) then
   if (El is TJSForStatement) then
     WriteForStatement(TJSForStatement(El))
     WriteForStatement(TJSForStatement(El))
   else if (El is TJSSwitchStatement) then
   else if (El is TJSSwitchStatement) then
     WriteSwitchStatement(TJSSwitchStatement(El))
     WriteSwitchStatement(TJSSwitchStatement(El))
   else if (El is TJSForInStatement) then
   else if (El is TJSForInStatement) then
     WriteForInStatement(TJSForInStatement(El))
     WriteForInStatement(TJSForInStatement(El))
-  else if EL is TJSWhileStatement then
+  else if El is TJSWhileStatement then
     WriteWhileStatement(TJSWhileStatement(El))
     WriteWhileStatement(TJSWhileStatement(El))
-  else if (EL is TJSContinueStatement) then
+  else if (El is TJSContinueStatement) then
     begin
     begin
     if (TN<>'') then
     if (TN<>'') then
       Write('continue '+TN)
       Write('continue '+TN)
     else
     else
       Write('continue');
       Write('continue');
     end
     end
-  else if (EL is TJSBreakStatement) then
+  else if (El is TJSBreakStatement) then
     begin
     begin
    if (TN<>'') then
    if (TN<>'') then
       Write('break '+TN)
       Write('break '+TN)
@@ -1243,7 +1268,7 @@ begin
       Write('break');
       Write('break');
     end
     end
   else
   else
-    Error('Unknown target statement class: "%s"',[EL.ClassName])
+    Error('Unknown target statement class: "%s"',[El.ClassName])
 end;
 end;
 
 
 procedure TJSWriter.WriteReturnStatement(El: TJSReturnStatement);
 procedure TJSWriter.WriteReturnStatement(El: TJSReturnStatement);
@@ -1384,6 +1409,8 @@ begin
 end;
 end;
 
 
 procedure TJSWriter.WriteJS(El: TJSElement);
 procedure TJSWriter.WriteJS(El: TJSElement);
+var
+  LastWritingEl: TJSElement;
 begin
 begin
 {$IFDEF DEBUGJSWRITER}
 {$IFDEF DEBUGJSWRITER}
   if (EL<>Nil) then
   if (EL<>Nil) then
@@ -1391,6 +1418,8 @@ begin
   else
   else
     system.Writeln('WriteJS : El = Nil');
     system.Writeln('WriteJS : El = Nil');
 {$ENDIF}
 {$ENDIF}
+  LastWritingEl:=Writer.CurElement;
+  Writer.CurElement:=El;
   if (El is TJSEmptyBlockStatement ) then
   if (El is TJSEmptyBlockStatement ) then
     WriteEmptyBlockStatement(TJSEmptyBlockStatement(El))
     WriteEmptyBlockStatement(TJSEmptyBlockStatement(El))
   else if (El is TJSEmptyStatement) then
   else if (El is TJSEmptyStatement) then
@@ -1449,6 +1478,7 @@ begin
     Error(SErrUnknownJSClass,[El.ClassName]);
     Error(SErrUnknownJSClass,[El.ClassName]);
 //  Write('/* '+El.ClassName+' */');
 //  Write('/* '+El.ClassName+' */');
   FSkipCurlyBrackets:=False;
   FSkipCurlyBrackets:=False;
+  Writer.CurElement:=LastWritingEl;
 end;
 end;
 
 
 { TFileWriter }
 { TFileWriter }
@@ -1467,6 +1497,7 @@ end;
 
 
 Constructor TFileWriter.Create(Const AFileNAme: String);
 Constructor TFileWriter.Create(Const AFileNAme: String);
 begin
 begin
+  inherited Create;
   FFileName:=AFileName;
   FFileName:=AFileName;
   Assign(FFile,AFileName);
   Assign(FFile,AFileName);
   Rewrite(FFile);
   Rewrite(FFile);
@@ -1490,33 +1521,103 @@ end;
 
 
 { TTextWriter }
 { TTextWriter }
 
 
-Function TTextWriter.Write(Const S: UnicodeString) : Integer;
+procedure TTextWriter.Writing;
 begin
 begin
+  if Assigned(OnWriting) then
+    OnWriting(Self);
+end;
+
+constructor TTextWriter.Create;
+begin
+  FCurLine:=1;
+  FCurColumn:=1;
+end;
+
+function TTextWriter.Write(const S: UnicodeString): Integer;
+var
+  p: PWideChar;
+  c: WideChar;
+begin
+  if S='' then exit;
+  Writing;
   Result:=DoWrite(S);
   Result:=DoWrite(S);
+  p:=PWideChar(S);
+  repeat
+    c:=p^;
+    case c of
+    #0:
+      if p-PWideChar(S)=length(S)*2 then
+        break
+      else
+        inc(FCurColumn);
+    #10,#13:
+      begin
+      FCurColumn:=1;
+      inc(FCurLine);
+      inc(p);
+      if (p^ in [#10,#13]) and (c<>p^) then inc(p);
+      continue;
+      end;
+    else
+      // ignore low/high surrogate, CurColumn is char index, not codepoint
+      inc(FCurColumn);
+    end;
+    inc(p);
+  until false;
 end;
 end;
 
 
-Function TTextWriter.Write(Const S: AnsiString) : integer;
+function TTextWriter.Write(const S: AnsiString): Integer;
+var
+  p: PChar;
+  c: Char;
 begin
 begin
+  if S='' then exit;
+  Writing;
   Result:=DoWrite(S);
   Result:=DoWrite(S);
+  p:=PChar(S);
+  repeat
+    c:=p^;
+    case c of
+    #0:
+      if p-PChar(S)=length(S) then
+        break
+      else
+        inc(FCurColumn);
+    #10,#13:
+      begin
+      FCurColumn:=1;
+      inc(FCurLine);
+      inc(p);
+      if (p^ in [#10,#13]) and (c<>p^) then inc(p);
+      continue;
+      end;
+    else
+      // ignore UTF-8 multibyte chars, CurColumn is char index, not codepoint
+      inc(FCurColumn);
+    end;
+    inc(p);
+  until false;
 end;
 end;
 
 
-Function TTextWriter.WriteLn(Const S: AnsiString) : Integer;
+function TTextWriter.WriteLn(const S: AnsiString): Integer;
 begin
 begin
-  Result:=DoWrite(S)+DoWrite(sLineBreak);
+  Result:=Write(S)+Write(sLineBreak);
 end;
 end;
 
 
-Function TTextWriter.Write(Const Fmt: AnsiString; Args: Array of const) : Integer;
+function TTextWriter.Write(const Fmt: AnsiString;
+  Args: array of const): Integer;
 
 
 begin
 begin
-  Result:=DoWrite(Format(Fmt,Args));
+  Result:=Write(Format(Fmt,Args));
 end;
 end;
 
 
-Function TTextWriter.WriteLn(Const Fmt: AnsiString; Args: Array of const) : integer;
+function TTextWriter.WriteLn(const Fmt: AnsiString;
+  Args: array of const): Integer;
 begin
 begin
   Result:=WriteLn(Format(Fmt,Args));
   Result:=WriteLn(Format(Fmt,Args));
 end;
 end;
 
 
-Function TTextWriter.Write(Const Args: Array of const) : Integer;
+function TTextWriter.Write(const Args: array of const): Integer;
 
 
 Var
 Var
   I : Integer;
   I : Integer;
@@ -1552,11 +1653,11 @@ begin
     if (U<>'') then
     if (U<>'') then
       Result:=Result+Write(u)
       Result:=Result+Write(u)
     else if (S<>'') then
     else if (S<>'') then
-      Result:=Result+write(s);
+      Result:=Result+Write(s);
     end;
     end;
 end;
 end;
 
 
-Function TTextWriter.WriteLn(Const Args: Array of const) : integer;
+function TTextWriter.WriteLn(const Args: array of const): Integer;
 begin
 begin
   Result:=Write(Args)+Writeln('');
   Result:=Write(Args)+Writeln('');
 end;
 end;

+ 175 - 0
packages/fcl-js/tests/tcsrcmap.pas

@@ -0,0 +1,175 @@
+unit TCSrcMap;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry, fpjson, JSSrcMap;
+
+type
+
+  { TCustomTestSrcMap }
+
+  TCustomTestSrcMap = class(TTestCase)
+  protected
+    procedure CheckEl(aName: String; El: TJSONData; aClass: TClass);
+    function GetEl(Obj: TJSONObject; aName: String; aClass: TClass): TJSONData;
+  end;
+
+  { TTestSrcMap }
+
+  TTestSrcMap = class(TCustomTestSrcMap)
+  published
+    procedure Test_Base64VLQ;
+    procedure TestSrcMapIgnoreDuplicate;
+    procedure TestSrcMapNames;
+  end;
+
+implementation
+
+{ TCustomTestSrcMap }
+
+procedure TCustomTestSrcMap.CheckEl(aName: String; El: TJSONData; aClass: TClass);
+begin
+  AssertNotNull('json "'+aName+'" exists',El);
+  AssertEquals('json "'+aName+'" class',El.ClassType,aClass);
+end;
+
+function TCustomTestSrcMap.GetEl(Obj: TJSONObject; aName: String; aClass: TClass): TJSONData;
+begin
+  Result:=Obj.Elements[aName];
+  CheckEl(aName,Result,aClass);
+end;
+
+{ TTestSrcMap }
+
+procedure TTestSrcMap.Test_Base64VLQ;
+var
+  i: Integer;
+  s: String;
+  p: PChar;
+  j: NativeInt;
+begin
+  for i:=-511 to 511 do
+  begin
+    s:=EncodeBase64VLQ(i);
+    p:=PChar(s);
+    j:=DecodeBase64VLQ(p);
+    if i<>j then
+      Fail('Encode/DecodeBase64VLQ OrigIndex='+IntToStr(i)+' Code="'+s+'" NewIndex='+IntToStr(j));
+  end;
+end;
+
+procedure TTestSrcMap.TestSrcMapIgnoreDuplicate;
+var
+  sm: TSourceMap;
+  Obj: TJSONObject;
+  El: TJSONData;
+  Arr: TJSONArray;
+begin
+  Obj:=nil;
+  sm:=TSourceMap.Create('generated.js');
+  try
+    sm.AddMapping(1,0,'a.js',1,0);
+    sm.AddMapping(2,0);
+    sm.AddMapping(2,0);
+    sm.AddMapping(3,0,'a.js',2,0);
+
+    //writeln(sm.ToString);
+    {
+      version: 3,
+      file: 'generated.js',
+      sources: ['a.js'],
+      names: [],
+      mappings: 'AAAA;A;AACA'
+    }
+    Obj:=sm.ToJSON;
+
+    // version
+    El:=GetEl(Obj,'version',TJSONIntegerNumber);
+    AssertEquals('json "version" value',El.AsInt64,3);
+
+    // file
+    El:=GetEl(Obj,'file',TJSONString);
+    AssertEquals('json "file" value',El.AsString,'generated.js');
+
+    // sources
+    Arr:=TJSONArray(GetEl(Obj,'sources',TJSONArray));
+    AssertEquals('json "sources".count',Arr.Count,1);
+    El:=Arr[0];
+    CheckEl('sources[0]',El,TJSONString);
+    AssertEquals('json "sources[0]" value',El.AsString,'a.js');
+
+    // names
+    Arr:=TJSONArray(GetEl(Obj,'names',TJSONArray));
+    AssertEquals('json "names".count',Arr.Count,0);
+
+    // mappings
+    El:=GetEl(Obj,'mappings',TJSONString);
+    AssertEquals('json "mappings" value',El.AsString,'AAAA;A;AACA');
+
+  finally
+    Obj.Free;
+    sm.Free;
+  end;
+end;
+
+procedure TTestSrcMap.TestSrcMapNames;
+var
+  sm: TSourceMap;
+  Obj: TJSONObject;
+  El: TJSONData;
+  Arr: TJSONArray;
+begin
+  Obj:=nil;
+  sm:=TSourceMap.Create('generated.js');
+  try
+    sm.AddMapping(1,1,'a.js',2,2,'foo');
+    sm.AddMapping(3,3,'a.js',4,4,'foo');
+    writeln(sm.ToString);
+    {
+      version: 3,
+      file: 'generated.js',
+      sources: ['a.js'],
+      names: ['foo'],
+      mappings: 'CACEA;;GAEEA'
+    }
+    Obj:=sm.ToJSON;
+
+    // version
+    El:=GetEl(Obj,'version',TJSONIntegerNumber);
+    AssertEquals('json "version" value',El.AsInt64,3);
+
+    // file
+    El:=GetEl(Obj,'file',TJSONString);
+    AssertEquals('json "file" value',El.AsString,'generated.js');
+
+    // sources
+    Arr:=TJSONArray(GetEl(Obj,'sources',TJSONArray));
+    AssertEquals('json "sources".count',Arr.Count,1);
+    El:=Arr[0];
+    CheckEl('sources[0]',El,TJSONString);
+    AssertEquals('json "sources[0]" value',El.AsString,'a.js');
+
+    // names
+    Arr:=TJSONArray(GetEl(Obj,'names',TJSONArray));
+    AssertEquals('json "names".count',Arr.Count,1);
+    El:=Arr[0];
+    CheckEl('names[0]',El,TJSONString);
+    AssertEquals('json "names[0]" value',El.AsString,'foo');
+
+    // mappings
+    El:=GetEl(Obj,'mappings',TJSONString);
+    AssertEquals('json "mappings" value',El.AsString,'CACEA;;GAEEA');
+
+  finally
+    Obj.Free;
+    sm.Free;
+  end;
+end;
+
+initialization
+  RegisterTests([TTestSrcMap]);
+end.
+

+ 20 - 208
packages/fcl-js/tests/testjs.lpi

@@ -1,32 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
 <CONFIG>
   <ProjectOptions>
   <ProjectOptions>
-    <Version Value="9"/>
+    <Version Value="10"/>
     <General>
     <General>
+      <SessionStorage Value="InProjectDir"/>
       <MainUnit Value="0"/>
       <MainUnit Value="0"/>
-      <UseXPManifest Value="True"/>
-      <Icon Value="0"/>
-      <ActiveWindowIndexAtStart Value="0"/>
+      <UseAppBundle Value="False"/>
     </General>
     </General>
-    <VersionInfo>
-      <Language Value=""/>
-      <CharSet Value=""/>
-      <StringTable ProductVersion=""/>
-    </VersionInfo>
     <BuildModes Count="1">
     <BuildModes Count="1">
       <Item1 Name="default" Default="True"/>
       <Item1 Name="default" Default="True"/>
     </BuildModes>
     </BuildModes>
     <PublishOptions>
     <PublishOptions>
       <Version Value="2"/>
       <Version Value="2"/>
-      <IgnoreBinaries Value="False"/>
-      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
-      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
     </PublishOptions>
     </PublishOptions>
     <RunParams>
     <RunParams>
       <local>
       <local>
         <FormatVersion Value="1"/>
         <FormatVersion Value="1"/>
-        <CommandLineParams Value="--suite=TTestStatementWriter"/>
-        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
       </local>
       </local>
     </RunParams>
     </RunParams>
     <RequiredPackages Count="1">
     <RequiredPackages Count="1">
@@ -34,260 +23,84 @@
         <PackageName Value="FCL"/>
         <PackageName Value="FCL"/>
       </Item1>
       </Item1>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="16">
+    <Units Count="13">
       <Unit0>
       <Unit0>
         <Filename Value="testjs.lpr"/>
         <Filename Value="testjs.lpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="testjs"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="1"/>
-        <CursorPos X="48" Y="3"/>
-        <UsageCount Value="201"/>
       </Unit0>
       </Unit0>
       <Unit1>
       <Unit1>
         <Filename Value="tcscanner.pp"/>
         <Filename Value="tcscanner.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="tcscanner"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="1"/>
-        <CursorPos X="17" Y="22"/>
-        <UsageCount Value="201"/>
       </Unit1>
       </Unit1>
       <Unit2>
       <Unit2>
         <Filename Value="../src/jsbase.pp"/>
         <Filename Value="../src/jsbase.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="jsbase"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="1"/>
-        <CursorPos X="1" Y="12"/>
-        <UsageCount Value="200"/>
       </Unit2>
       </Unit2>
       <Unit3>
       <Unit3>
         <Filename Value="../src/jsparser.pp"/>
         <Filename Value="../src/jsparser.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="jsparser"/>
-        <EditorIndex Value="3"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="67"/>
-        <CursorPos X="14" Y="85"/>
-        <UsageCount Value="201"/>
-        <Loaded Value="True"/>
       </Unit3>
       </Unit3>
       <Unit4>
       <Unit4>
         <Filename Value="../src/jsscanner.pp"/>
         <Filename Value="../src/jsscanner.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="JSScanner"/>
         <UnitName Value="JSScanner"/>
-        <EditorIndex Value="6"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="342"/>
-        <CursorPos X="76" Y="345"/>
-        <UsageCount Value="201"/>
-        <Loaded Value="True"/>
       </Unit4>
       </Unit4>
       <Unit5>
       <Unit5>
         <Filename Value="../src/jstree.pp"/>
         <Filename Value="../src/jstree.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="jstree"/>
-        <EditorIndex Value="5"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="739"/>
-        <CursorPos X="3" Y="757"/>
-        <UsageCount Value="200"/>
-        <Loaded Value="True"/>
       </Unit5>
       </Unit5>
       <Unit6>
       <Unit6>
         <Filename Value="tcparser.pp"/>
         <Filename Value="tcparser.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="tcparser"/>
-        <EditorIndex Value="4"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="1878"/>
-        <CursorPos X="3" Y="1883"/>
-        <UsageCount Value="201"/>
-        <Loaded Value="True"/>
       </Unit6>
       </Unit6>
       <Unit7>
       <Unit7>
         <Filename Value="../src/jswriter.pp"/>
         <Filename Value="../src/jswriter.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="jswriter"/>
-        <EditorIndex Value="0"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="8"/>
-        <CursorPos X="28" Y="15"/>
-        <UsageCount Value="202"/>
-        <Loaded Value="True"/>
       </Unit7>
       </Unit7>
       <Unit8>
       <Unit8>
         <Filename Value="tctextwriter.pp"/>
         <Filename Value="tctextwriter.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="tctextwriter"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="4"/>
-        <CursorPos X="15" Y="22"/>
-        <UsageCount Value="201"/>
       </Unit8>
       </Unit8>
       <Unit9>
       <Unit9>
-        <Filename Value="../../../../../projects/lazarus/components/fpcunit/console/consoletestrunner.pas"/>
-        <UnitName Value="consoletestrunner"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="157"/>
-        <CursorPos X="1" Y="175"/>
-        <UsageCount Value="4"/>
+        <Filename Value="tcwriter.pp"/>
+        <IsPartOfProject Value="True"/>
       </Unit9>
       </Unit9>
       <Unit10>
       <Unit10>
-        <Filename Value="tcwriter.pp"/>
+        <Filename Value="../src/jstoken.pp"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="tcwriter"/>
-        <IsVisibleTab Value="True"/>
-        <EditorIndex Value="2"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="668"/>
-        <CursorPos X="45" Y="698"/>
-        <UsageCount Value="220"/>
-        <Loaded Value="True"/>
       </Unit10>
       </Unit10>
       <Unit11>
       <Unit11>
-        <Filename Value="../../../../released/packages/fcl-json/src/fpjson.pp"/>
-        <UnitName Value="fpjson"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="558"/>
-        <CursorPos X="21" Y="580"/>
-        <UsageCount Value="61"/>
+        <Filename Value="tcsrcmap.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="TCSrcMap"/>
       </Unit11>
       </Unit11>
       <Unit12>
       <Unit12>
-        <Filename Value="../src/jstoken.pp"/>
+        <Filename Value="../src/jssrcmap.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="jstoken"/>
-        <EditorIndex Value="1"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="1"/>
-        <CursorPos X="18" Y="8"/>
-        <UsageCount Value="200"/>
-        <Loaded Value="True"/>
+        <UnitName Value="JSSrcMap"/>
       </Unit12>
       </Unit12>
-      <Unit13>
-        <Filename Value="../../../../released/packages/fcl-fpcunit/src/testregistry.pp"/>
-        <UnitName Value="testregistry"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="106"/>
-        <CursorPos X="22" Y="108"/>
-        <UsageCount Value="13"/>
-      </Unit13>
-      <Unit14>
-        <Filename Value="../../../rtl/tests/punit.pp"/>
-        <UnitName Value="punit"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="405"/>
-        <CursorPos X="41" Y="415"/>
-        <UsageCount Value="18"/>
-      </Unit14>
-      <Unit15>
-        <Filename Value="../../../../released/rtl/inc/mathh.inc"/>
-        <WindowIndex Value="1"/>
-        <TopLine Value="60"/>
-        <CursorPos X="14" Y="78"/>
-        <UsageCount Value="13"/>
-      </Unit15>
     </Units>
     </Units>
-    <JumpHistory Count="6" HistoryIndex="5">
-      <Position1>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1" Column="1" TopLine="1"/>
-      </Position1>
-      <Position2>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1732" Column="55" TopLine="1713"/>
-      </Position2>
-      <Position3>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1883" Column="3" TopLine="1878"/>
-      </Position3>
-      <Position4>
-        <Filename Value="tcwriter.pp"/>
-        <Caret Line="66" Column="43" TopLine="51"/>
-      </Position4>
-      <Position5>
-        <Filename Value="tcwriter.pp"/>
-        <Caret Line="76" Column="43" TopLine="48"/>
-      </Position5>
-      <Position6>
-        <Filename Value="tcwriter.pp"/>
-        <Caret Line="251" Column="31" TopLine="232"/>
-      </Position6>
-    </JumpHistory>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>
     <Version Value="11"/>
     <Version Value="11"/>
     <SearchPaths>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="/home/michael/source/fcl-js/;..;../src"/>
+      <OtherUnitFiles Value="../src"/>
     </SearchPaths>
     </SearchPaths>
     <CodeGeneration>
     <CodeGeneration>
+      <Checks>
+        <IOChecks Value="True"/>
+        <RangeChecks Value="True"/>
+        <OverflowChecks Value="True"/>
+        <StackChecks Value="True"/>
+      </Checks>
+      <VerifyObjMethodCallValidity Value="True"/>
       <Optimizations>
       <Optimizations>
         <OptimizationLevel Value="0"/>
         <OptimizationLevel Value="0"/>
       </Optimizations>
       </Optimizations>
     </CodeGeneration>
     </CodeGeneration>
-    <Linking>
-      <Debugging>
-        <UseHeaptrc Value="True"/>
-      </Debugging>
-    </Linking>
-    <Other>
-      <CompilerPath Value="$(CompPath)"/>
-    </Other>
   </CompilerOptions>
   </CompilerOptions>
   <Debugging>
   <Debugging>
-    <BreakPoints Count="7">
-      <Item1>
-        <Kind Value="bpkSource"/>
-        <WatchScope Value="wpsGlobal"/>
-        <WatchKind Value="wpkWrite"/>
-        <Source Value="../jsscanner.pp"/>
-        <Line Value="717"/>
-      </Item1>
-      <Item2>
-        <Kind Value="bpkSource"/>
-        <WatchScope Value="wpsLocal"/>
-        <WatchKind Value="wpkWrite"/>
-        <Source Value="tcparser.pp"/>
-        <Line Value="2086"/>
-      </Item2>
-      <Item3>
-        <Kind Value="bpkSource"/>
-        <WatchScope Value="wpsLocal"/>
-        <WatchKind Value="wpkWrite"/>
-        <Source Value="tcparser.pp"/>
-        <Line Value="2566"/>
-      </Item3>
-      <Item4>
-        <Kind Value="bpkSource"/>
-        <WatchScope Value="wpsLocal"/>
-        <WatchKind Value="wpkWrite"/>
-        <Source Value="../src/jsparser.pp"/>
-        <Line Value="845"/>
-      </Item4>
-      <Item5>
-        <Kind Value="bpkSource"/>
-        <WatchScope Value="wpsLocal"/>
-        <WatchKind Value="wpkWrite"/>
-        <Source Value="../src/jsparser.pp"/>
-        <Line Value="754"/>
-      </Item5>
-      <Item6>
-        <Kind Value="bpkSource"/>
-        <WatchScope Value="wpsLocal"/>
-        <WatchKind Value="wpkWrite"/>
-        <Source Value="../src/jsparser.pp"/>
-        <Line Value="1287"/>
-      </Item6>
-      <Item7>
-        <Kind Value="bpkSource"/>
-        <WatchScope Value="wpsLocal"/>
-        <WatchKind Value="wpkWrite"/>
-        <Source Value="tcparser.pp"/>
-        <Line Value="2253"/>
-      </Item7>
-    </BreakPoints>
     <Exceptions Count="3">
     <Exceptions Count="3">
       <Item1>
       <Item1>
         <Name Value="EAbort"/>
         <Name Value="EAbort"/>
@@ -300,5 +113,4 @@
       </Item3>
       </Item3>
     </Exceptions>
     </Exceptions>
   </Debugging>
   </Debugging>
-  <EditorMacros Count="0"/>
 </CONFIG>
 </CONFIG>

+ 1 - 1
packages/fcl-js/tests/testjs.lpr

@@ -7,7 +7,7 @@ uses
   cwstring,
   cwstring,
   {$ENDIF}
   {$ENDIF}
   Classes, consoletestrunner, tcscanner, jsparser, jsscanner, jstree, jsbase,
   Classes, consoletestrunner, tcscanner, jsparser, jsscanner, jstree, jsbase,
-  tcparser, jswriter, tcwriter, jstoken;
+  tcparser, jswriter, tcwriter, jstoken, JSSrcMap, TCSrcMap;
 
 
 var
 var
   Application: TTestRunner;
   Application: TTestRunner;