|
@@ -35,9 +35,9 @@ type
|
|
|
|
|
|
TSourceMapSegment = class
|
|
TSourceMapSegment = class
|
|
public
|
|
public
|
|
- Index: integer; // index in FNodes
|
|
|
|
- GeneratedLine: integer;
|
|
|
|
- GeneratedColumn: integer;
|
|
|
|
|
|
+ Index: integer; // index in Items
|
|
|
|
+ GeneratedLine: integer; // 1-based
|
|
|
|
+ GeneratedColumn: integer; // 0-based
|
|
SrcFileIndex: integer; // index in FSources
|
|
SrcFileIndex: integer; // index in FSources
|
|
SrcLine: integer;
|
|
SrcLine: integer;
|
|
SrcColumn: integer;
|
|
SrcColumn: integer;
|
|
@@ -54,7 +54,7 @@ type
|
|
TSourceMapOption = (
|
|
TSourceMapOption = (
|
|
smoAddMonotonous, // true = AddMapping GeneratedLine/Col must be behind last add, false = check all adds for duplicate
|
|
smoAddMonotonous, // true = AddMapping GeneratedLine/Col must be behind last add, false = check all adds for duplicate
|
|
smoAutoLineStart, // automatically add a first column mapping, repeating last mapping
|
|
smoAutoLineStart, // automatically add a first column mapping, repeating last mapping
|
|
- smoSafetyHeader // add ')]}'
|
|
|
|
|
|
+ smoSafetyHeader // insert ')]}' at start
|
|
);
|
|
);
|
|
TSourceMapOptions = set of TSourceMapOption;
|
|
TSourceMapOptions = set of TSourceMapOption;
|
|
const
|
|
const
|
|
@@ -85,6 +85,7 @@ type
|
|
FNameToIndex: TStringToIndex; // name to index in FNames
|
|
FNameToIndex: TStringToIndex; // name to index in FNames
|
|
FItems: TFPList; // TSourceMapSegment, in adding order
|
|
FItems: TFPList; // TSourceMapSegment, in adding order
|
|
FOptions: TSourceMapOptions;
|
|
FOptions: TSourceMapOptions;
|
|
|
|
+ FSorted: boolean;
|
|
FSourceRoot: string;
|
|
FSourceRoot: string;
|
|
FSources: TFPList; // list of TSourceMapSrc, in adding order
|
|
FSources: TFPList; // list of TSourceMapSrc, in adding order
|
|
FSourceToIndex: TStringToIndex; // srcfile to index in FSources
|
|
FSourceToIndex: TStringToIndex; // srcfile to index in FSources
|
|
@@ -95,8 +96,10 @@ type
|
|
function GetSourceFiles(Index: integer): String;
|
|
function GetSourceFiles(Index: integer): String;
|
|
function GetSourceTranslatedFiles(Index: integer): String;
|
|
function GetSourceTranslatedFiles(Index: integer): String;
|
|
procedure SetGeneratedFilename(const AValue: string);
|
|
procedure SetGeneratedFilename(const AValue: string);
|
|
|
|
+ procedure SetSorted(const AValue: boolean);
|
|
procedure SetSourceContents(Index: integer; const AValue: String);
|
|
procedure SetSourceContents(Index: integer; const AValue: String);
|
|
procedure SetSourceTranslatedFiles(Index: integer; const AValue: String);
|
|
procedure SetSourceTranslatedFiles(Index: integer; const AValue: String);
|
|
|
|
+ procedure Sort;
|
|
public
|
|
public
|
|
constructor Create(const aGeneratedFilename: string);
|
|
constructor Create(const aGeneratedFilename: string);
|
|
destructor Destroy; override;
|
|
destructor Destroy; override;
|
|
@@ -120,6 +123,7 @@ type
|
|
property GeneratedFilename: string read FGeneratedFilename write SetGeneratedFilename;
|
|
property GeneratedFilename: string read FGeneratedFilename write SetGeneratedFilename;
|
|
function IndexOfName(const Name: string; AddIfNotExists: boolean = false): integer;
|
|
function IndexOfName(const Name: string; AddIfNotExists: boolean = false): integer;
|
|
function IndexOfSourceFile(const SrcFile: string; AddIfNotExists: boolean = false): integer;
|
|
function IndexOfSourceFile(const SrcFile: string; AddIfNotExists: boolean = false): integer;
|
|
|
|
+ function IndexOfSegmentAt(GeneratedLine, GeneratedCol: integer): integer;
|
|
function Count: integer; // segments
|
|
function Count: integer; // segments
|
|
property Items[Index: integer]: TSourceMapSegment read GetItems; default; // segments
|
|
property Items[Index: integer]: TSourceMapSegment read GetItems; default; // segments
|
|
function SourceCount: integer;
|
|
function SourceCount: integer;
|
|
@@ -132,12 +136,15 @@ type
|
|
property Names[Index: integer]: string read GetNames;
|
|
property Names[Index: integer]: string read GetNames;
|
|
property Version: integer read FVersion; // 3
|
|
property Version: integer read FVersion; // 3
|
|
property Options: TSourceMapOptions read FOptions write FOptions;
|
|
property Options: TSourceMapOptions read FOptions write FOptions;
|
|
|
|
+ property Sorted: boolean read FSorted write SetSorted; // Segments are sorted for GeneratedLine/Col
|
|
end;
|
|
end;
|
|
|
|
|
|
function EncodeBase64VLQ(i: NativeInt): String; // base64 Variable Length Quantity
|
|
function EncodeBase64VLQ(i: NativeInt): String; // base64 Variable Length Quantity
|
|
function DecodeBase64VLQ(const s: string): NativeInt; // base64 Variable Length Quantity
|
|
function DecodeBase64VLQ(const s: string): NativeInt; // base64 Variable Length Quantity
|
|
function DecodeBase64VLQ(var p: PChar): NativeInt; // base64 Variable Length Quantity
|
|
function DecodeBase64VLQ(var p: PChar): NativeInt; // base64 Variable Length Quantity
|
|
|
|
|
|
|
|
+function CompareSegmentWithGeneratedLineCol(Item1, Item2: Pointer): Integer;
|
|
|
|
+
|
|
implementation
|
|
implementation
|
|
|
|
|
|
function EncodeBase64VLQ(i: NativeInt): String;
|
|
function EncodeBase64VLQ(i: NativeInt): String;
|
|
@@ -236,6 +243,28 @@ begin
|
|
Result:=Result shr 1;
|
|
Result:=Result shr 1;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function CompareSegmentWithGeneratedLineCol(Item1, Item2: Pointer): Integer;
|
|
|
|
+var
|
|
|
|
+ Seg1: TSourceMapSegment absolute Item1;
|
|
|
|
+ Seg2: TSourceMapSegment absolute Item2;
|
|
|
|
+begin
|
|
|
|
+ if Seg1.GeneratedLine<Seg2.GeneratedLine then
|
|
|
|
+ Result:=-1
|
|
|
|
+ else if Seg1.GeneratedLine>Seg2.GeneratedLine then
|
|
|
|
+ Result:=1
|
|
|
|
+ else if Seg1.GeneratedColumn<Seg2.GeneratedColumn then
|
|
|
|
+ Result:=-1
|
|
|
|
+ else if Seg1.GeneratedColumn>Seg2.GeneratedColumn then
|
|
|
|
+ Result:=1
|
|
|
|
+ // compare Index to keep adding order
|
|
|
|
+ else if Seg1.Index<Seg2.Index then
|
|
|
|
+ Result:=-1
|
|
|
|
+ else if Seg1.Index>Seg2.Index then
|
|
|
|
+ Result:=1
|
|
|
|
+ else
|
|
|
|
+ Result:=0;
|
|
|
|
+end;
|
|
|
|
+
|
|
{ TSourceMap.TStringToIndex }
|
|
{ TSourceMap.TStringToIndex }
|
|
|
|
|
|
constructor TSourceMap.TStringToIndex.Create;
|
|
constructor TSourceMap.TStringToIndex.Create;
|
|
@@ -276,6 +305,15 @@ begin
|
|
FGeneratedFilename:=AValue;
|
|
FGeneratedFilename:=AValue;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+procedure TSourceMap.SetSorted(const AValue: boolean);
|
|
|
|
+begin
|
|
|
|
+ if FSorted=AValue then Exit;
|
|
|
|
+ if AValue then
|
|
|
|
+ Sort
|
|
|
|
+ else
|
|
|
|
+ FSorted:=false;
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TSourceMap.SetSourceContents(Index: integer; const AValue: String);
|
|
procedure TSourceMap.SetSourceContents(Index: integer; const AValue: String);
|
|
begin
|
|
begin
|
|
TSourceMapSrc(FSources[Index]).Source:=AValue;
|
|
TSourceMapSrc(FSources[Index]).Source:=AValue;
|
|
@@ -287,6 +325,17 @@ begin
|
|
TSourceMapSrc(FSources[Index]).TranslatedFilename:=AValue;
|
|
TSourceMapSrc(FSources[Index]).TranslatedFilename:=AValue;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+procedure TSourceMap.Sort;
|
|
|
|
+var
|
|
|
|
+ i: Integer;
|
|
|
|
+begin
|
|
|
|
+ if FSorted then exit;
|
|
|
|
+ FItems.Sort(@CompareSegmentWithGeneratedLineCol);
|
|
|
|
+ for i:=0 to Count-1 do
|
|
|
|
+ Items[i].Index:=i;
|
|
|
|
+ FSorted:=true;
|
|
|
|
+end;
|
|
|
|
+
|
|
function TSourceMap.GetItems(Index: integer): TSourceMapSegment;
|
|
function TSourceMap.GetItems(Index: integer): TSourceMapSegment;
|
|
begin
|
|
begin
|
|
Result:=TSourceMapSegment(FItems[Index]);
|
|
Result:=TSourceMapSegment(FItems[Index]);
|
|
@@ -322,6 +371,7 @@ begin
|
|
FSources:=TFPList.Create;
|
|
FSources:=TFPList.Create;
|
|
FSourceToIndex:=TStringToIndex.Create;
|
|
FSourceToIndex:=TStringToIndex.Create;
|
|
GeneratedFilename:=aGeneratedFilename;
|
|
GeneratedFilename:=aGeneratedFilename;
|
|
|
|
+ FSorted:=true;
|
|
end;
|
|
end;
|
|
|
|
|
|
destructor TSourceMap.Destroy;
|
|
destructor TSourceMap.Destroy;
|
|
@@ -350,6 +400,7 @@ begin
|
|
FNameToIndex.Clear;
|
|
FNameToIndex.Clear;
|
|
FNames.Clear;
|
|
FNames.Clear;
|
|
FSourceRoot:='';
|
|
FSourceRoot:='';
|
|
|
|
+ FSorted:=true;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TSourceMap.AddMapping(GeneratedLine: integer; GeneratedCol: integer;
|
|
function TSourceMap.AddMapping(GeneratedLine: integer; GeneratedCol: integer;
|
|
@@ -363,7 +414,7 @@ function TSourceMap.AddMapping(GeneratedLine: integer; GeneratedCol: integer;
|
|
end;
|
|
end;
|
|
|
|
|
|
var
|
|
var
|
|
- NodeCnt, i: Integer;
|
|
|
|
|
|
+ NodeCnt: Integer;
|
|
OtherNode: TSourceMapSegment;
|
|
OtherNode: TSourceMapSegment;
|
|
begin
|
|
begin
|
|
{$IFDEF VerboseSrcMap}
|
|
{$IFDEF VerboseSrcMap}
|
|
@@ -393,27 +444,19 @@ begin
|
|
RaiseInvalid('invalid SrcCol');
|
|
RaiseInvalid('invalid SrcCol');
|
|
end;
|
|
end;
|
|
|
|
|
|
- // check if generated line/col already exists
|
|
|
|
|
|
+ // Note: same line/col is allowed
|
|
NodeCnt:=Count;
|
|
NodeCnt:=Count;
|
|
- if smoAddMonotonous in FOptions then
|
|
|
|
|
|
+ if (NodeCnt>0) then
|
|
begin
|
|
begin
|
|
- if NodeCnt>0 then
|
|
|
|
|
|
+ OtherNode:=Items[NodeCnt-1];
|
|
|
|
+ if (OtherNode.GeneratedLine>GeneratedLine)
|
|
|
|
+ or ((OtherNode.GeneratedLine=GeneratedLine)
|
|
|
|
+ and (OtherNode.GeneratedColumn>GeneratedCol)) then
|
|
begin
|
|
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');
|
|
|
|
|
|
+ if smoAddMonotonous in FOptions then
|
|
|
|
+ RaiseInvalid('GeneratedLine/Col not monotonous')
|
|
|
|
+ else
|
|
|
|
+ FSorted:=false;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -870,6 +913,54 @@ begin
|
|
FSourceToIndex.Add(SrcFile,Result);
|
|
FSourceToIndex.Add(SrcFile,Result);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+function TSourceMap.IndexOfSegmentAt(GeneratedLine, GeneratedCol: integer
|
|
|
|
+ ): integer;
|
|
|
|
+var
|
|
|
|
+ l, r, m: Integer;
|
|
|
|
+ aSeg: TSourceMapSegment;
|
|
|
|
+begin
|
|
|
|
+ Sort;
|
|
|
|
+ l:=0;
|
|
|
|
+ r:=Count-1;
|
|
|
|
+ aSeg:=nil;
|
|
|
|
+ while l<=r do
|
|
|
|
+ begin
|
|
|
|
+ m:=(l+r) div 2;
|
|
|
|
+ aSeg:=Items[m];
|
|
|
|
+ if aSeg.GeneratedLine<GeneratedLine then
|
|
|
|
+ l:=m+1
|
|
|
|
+ else if aSeg.GeneratedLine>GeneratedLine then
|
|
|
|
+ r:=m-1
|
|
|
|
+ else if aSeg.GeneratedColumn<GeneratedCol then
|
|
|
|
+ l:=m+1
|
|
|
|
+ else if aSeg.GeneratedColumn>GeneratedCol then
|
|
|
|
+ r:=m-1
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ // exact match found
|
|
|
|
+ Result:=m;
|
|
|
|
+ // -> return the leftmost exact match
|
|
|
|
+ while Result>0 do
|
|
|
|
+ begin
|
|
|
|
+ aSeg:=Items[Result-1];
|
|
|
|
+ if (aSeg.GeneratedLine<>GeneratedLine)
|
|
|
|
+ or (aSeg.GeneratedColumn<>GeneratedCol) then
|
|
|
|
+ exit;
|
|
|
|
+ dec(Result);
|
|
|
|
+ end;
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ // no exact match found
|
|
|
|
+ if aSeg=nil then
|
|
|
|
+ exit(-1);
|
|
|
|
+ // return the next lower. Note: there may be no such segment
|
|
|
|
+ if (aSeg.GeneratedLine>GeneratedLine)
|
|
|
|
+ or ((aSeg.GeneratedLine=GeneratedLine) and (aSeg.GeneratedColumn>GeneratedCol)) then
|
|
|
|
+ dec(m);
|
|
|
|
+ Result:=m;
|
|
|
|
+end;
|
|
|
|
+
|
|
function TSourceMap.Count: integer;
|
|
function TSourceMap.Count: integer;
|
|
begin
|
|
begin
|
|
Result:=FItems.Count;
|
|
Result:=FItems.Count;
|