unit fpDbgSymTableContext;

{$mode objfpc}{$H+}
{$IFDEF INLINE_OFF}{$INLINE OFF}{$ENDIF}

interface

uses
  Classes,
  SysUtils,
  FpDbgLoader,
  FpImgReaderBase,
  DbgIntfBaseTypes,
  fpDbgSymTable,
  FpdMemoryTools,
  FpDbgInfo,
  FpDbgCommon;

type

  { TFpSymbolTableProc }

  TFpSymbolTableProc = class(TFpSymbol)
  private
    FLineSym: TFpSymbol;
  protected
    function GetFlags: TDbgSymbolFlags; override;
    function GetLine: Cardinal; override;
    function GetLineStartAddress: TDBGPtr; override;
    function GetLineEndAddress: TDBGPtr; override;
    function GetFile: String; override;
  public
    constructor Create(const AName: String; AnAddr: TDbgPtr);
    destructor Destroy; override;
    procedure SetLineSym(ASym: TFpSymbol);
  end;

  TFpSymbolInfo = class;

  { TFpSymbolContext }

  TFpSymbolContext = class(TFpDbgSymbolScope)
  private
    FFpSymbolInfo: TFpSymbolInfo;
    FSizeOfAddress: integer;
  protected
    function GetSizeOfAddress: Integer; override;
  public
    constructor Create(ALocationContext: TFpDbgSimpleLocationContext; AFpSymbolInfo: TFpSymbolInfo);
    function FindSymbol(const AName: String; const OnlyUnitName: String = '';
      AFindFlags: TFindExportedSymbolsFlags = []): TFpValue; override;
  end;

  { TFpSymbolInfo }

  TFpSymbolInfo = class(TDbgInfo)
  private
    FSymbolList: TfpSymbolList;
    FLibName: String;
    function GetSymbols(AnIndex: integer): TFpSymbol;
  public
    constructor Create(ALoaderList: TDbgImageLoaderList; AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel); override; overload;
    constructor Create(ALoaderList: TDbgImageLoaderList; AMemManager: TFpDbgMemManager; ALibName: String; AMemModel: TFpDbgMemModel); overload;
    destructor Destroy; override;
    function FindSymbolScope(ALocationContext: TFpDbgSimpleLocationContext; AAddress: TDbgPtr = 0): TFpDbgSymbolScope; override;
    function FindProcSymbol(const AName: String; AIgnoreCase: Boolean = False): TFpSymbol; override; overload;
    function FindProcSymbol(AnAdress: TDbgPtr): TFpSymbol; overload;

    // for debugdump
    function SymbolCount: integer;
    property Symbols[AnIndex: integer]: TFpSymbol read GetSymbols;
  end;

implementation

{ TFpSymbolTableProc }

function TFpSymbolTableProc.GetFlags: TDbgSymbolFlags;
begin
  Result := inherited GetFlags;
  if FLineSym <> nil then
    Result := Result + FLineSym.Flags * [sfHasLine, sfHasLineAddrRng];
end;

function TFpSymbolTableProc.GetLine: Cardinal;
begin
  Result := inherited GetLine;
  if FLineSym <> nil then
    Result := FLineSym.Line;
end;

function TFpSymbolTableProc.GetLineStartAddress: TDBGPtr;
begin
  Result := inherited GetLineStartAddress;
  if FLineSym <> nil then
    Result := FLineSym.LineStartAddress;
end;

function TFpSymbolTableProc.GetLineEndAddress: TDBGPtr;
begin
  Result := inherited GetLineEndAddress;
  if FLineSym <> nil then
    Result := FLineSym.LineEndAddress;
end;

function TFpSymbolTableProc.GetFile: String;
begin
  Result := inherited GetFile;
  if FLineSym <> nil then
    Result := FLineSym.FileName;
end;

constructor TFpSymbolTableProc.Create(const AName: String; AnAddr: TDbgPtr);
begin
  inherited Create(AName);
  SetAddress(TargetLoc(AnAddr));
  SetKind(skProcedure);
  SetSymbolType(stType);
end;

destructor TFpSymbolTableProc.Destroy;
begin
  inherited Destroy;
  FLineSym.ReleaseReference;
end;

procedure TFpSymbolTableProc.SetLineSym(ASym: TFpSymbol);
begin
  FLineSym := ASym;
  if FLineSym <> nil then
    FLineSym.AddReference;
end;

{ TFpSymbolContext }

function TFpSymbolContext.GetSizeOfAddress: Integer;
begin
  result := FSizeOfAddress;
end;

constructor TFpSymbolContext.Create(ALocationContext: TFpDbgSimpleLocationContext;
  AFpSymbolInfo: TFpSymbolInfo);
begin
  inherited create(ALocationContext);
  FFpSymbolInfo:=AFpSymbolInfo;
end;

function TFpSymbolContext.FindSymbol(const AName: String; const OnlyUnitName: String;
  AFindFlags: TFindExportedSymbolsFlags): TFpValue;
var
  i: integer;
  val: TFpDbgMemLocation;
begin
  i := FFpSymbolInfo.FSymbolList.IndexOf(AName);
  if i > -1 then
  begin
    val := Default(TFpDbgMemLocation);
    val.Address:=FFpSymbolInfo.FSymbolList.DataPtr[i]^.Addr;
    val.MType:=mlfTargetMem;
    result := TFpValueConstAddress.Create(val);
  end
  else
    result := nil;
end;

{ TFpSymbolInfo }

function TFpSymbolInfo.GetSymbols(AnIndex: integer): TFpSymbol;
begin
  Result := TFpSymbolTableProc.Create(FSymbolList.Keys[AnIndex], FSymbolList.DataPtr[AnIndex]^.Addr);
end;

constructor TFpSymbolInfo.Create(ALoaderList: TDbgImageLoaderList;
  AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel);

var
  i: Integer;
begin
  inherited Create(ALoaderList, AMemManager, AMemModel);

  FSymbolList := TfpSymbolList.Create;
  for i := 0 to ALoaderList.Count-1 do
    ALoaderList[i].ParseSymbolTable(FSymbolList);
  FTargetInfo := ALoaderList.TargetInfo;
  if FSymbolList.Count > 0 then
    SetHasInfo;
end;

constructor TFpSymbolInfo.Create(ALoaderList: TDbgImageLoaderList;
  AMemManager: TFpDbgMemManager; ALibName: String; AMemModel: TFpDbgMemModel);
begin
  FLibName := ALibName;
  Create(ALoaderList, AMemManager, AMemModel);
end;

destructor TFpSymbolInfo.Destroy;
begin
  FSymbolList.Free;
  inherited Destroy;
end;

function TFpSymbolInfo.FindSymbolScope(ALocationContext: TFpDbgSimpleLocationContext;
  AAddress: TDbgPtr): TFpDbgSymbolScope;
begin
  assert(False, 'TFpSymbolInfo.FindSymbolScope: False');
  Result := TFpSymbolContext.Create(ALocationContext, Self);
end;

function TFpSymbolInfo.FindProcSymbol(const AName: String; AIgnoreCase: Boolean
  ): TFpSymbol;
var
  s: String;
  i: integer;
begin
  if AIgnoreCase then begin
    s := UpperCase(AName);
    i := FSymbolList.Count - 1;
    while i >= 0 do begin
      if UpperCase(FSymbolList.Keys[i]) = s then
        break;
      dec(i);
    end;
  end
  else
    i := FSymbolList.IndexOf(AName);
  if i >= 0 then
    Result := TFpSymbolTableProc.Create(AName, FSymbolList.DataPtr[i]^.Addr)
  else
    result := nil;
end;

function TFpSymbolInfo.FindProcSymbol(AnAdress: TDbgPtr): TFpSymbol;
var
  CheckRange: Boolean;
  i, NearestIdx: integer;
  a, NearestAddr: TDBGPtr;
  NPreFix: String;
  d: PfpLinkerSymbol;
begin
  NPreFix := '';
  if FLibName <> '' then
    NPreFix := FLibName+':';
  CheckRange :=
    (FSymbolList.HighAddr > FSymbolList.LowAddr) and
    (AnAdress >= FSymbolList.LowAddr) and
    (AnAdress < FSymbolList.HighAddr);
  NearestIdx := -1;
  NearestAddr := 0;

  Result := nil;
  i := FSymbolList.Count - 1;
  while i >= 0 do begin
    d := FSymbolList.DataPtr[i];
    a := d^.Addr;
    if (a = AnAdress) then begin
      Result := TFpSymbolTableProc.Create(NPreFix + FSymbolList.Keys[i], a);
      exit;
    end;
    if CheckRange and (a <= AnAdress) and (a > NearestAddr) and
       ( (d^.SectionEnd = 0) or (AnAdress <= d^.SectionEnd) )
    then begin
      NearestIdx := i;
      NearestAddr := a;
    end;
    dec(i);
  end;
  if NearestIdx >= 0 then begin
    Result := TFpSymbolTableProc.Create(NPreFix + FSymbolList.Keys[NearestIdx], FSymbolList.DataPtr[NearestIdx]^.Addr);
  end;
end;

function TFpSymbolInfo.SymbolCount: integer;
begin
  Result := FSymbolList.Count;
end;

end.

