lazarus-ccr/applications/gobject-introspection/girnamespaces.pas
drewski207 ae7df38f31 Further fixes for gir2pascal instance-parameter
Improved PointerLevel detection
Added IsConst Property to Parameter types



git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2877 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2014-01-06 04:39:27 +00:00

603 lines
17 KiB
ObjectPascal

{
girnamespaces.pas
Copyright (C) 2011 Andrew Haines andrewd207@aol.com
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
}
unit girNameSpaces;
{$mode objfpc}{$H+}
{$INTERFACES CORBA}
interface
uses
Classes, DOM, girParser, girTokens, girObjects, contnrs;
type
TgirNeedGirFileEvent = function (AGirFile: TObject; BaseNamespaceName: String) : TXMLDocument of object;
{ TgirNamespace }
TgirNamespace = class(IgirParser)
private
FCIncludeName: String;
FConstants: TList;
FCPackageName: String;
FFunctions: TList;
FNameSpace: String;
FOnlyImplied: Boolean;
FOnNeedGirFile: TgirNeedGirFileEvent;
FOwner: TObject;
FRequiredNameSpaces: TList;
FSharedLibrary: String;
FTypes: TFPHashObjectList;
FUnresolvedTypes: TList;
FVersion: String;
procedure SetOnNeedGirFile(AValue: TgirNeedGirFileEvent);
protected
function AddFuzzyType(AName: String; ACType: String): TGirBaseType;
procedure HandleAlias(ANode: TDomNode);
procedure HandleConstant(ANode: TDomNode);
procedure HandleEnumeration(ANode: TDomNode);
procedure HandleBitField(ANode: TDomNode);
procedure HandleCallback(ANode: TDOMNode);
procedure HandleFunction(ANode: TDOMNode);
procedure HandleUnion(ANode: TDOMNode);
{
Some 'records' have methods these corelate to pascal 'object'
GType extends this 'object' type to have a sort of vmt
GObject and subclasses extend gtype and adds more vmt method entries and method entries to the instance itself.
}
procedure HandleRecord(ANode: TDomNode); //could be struct, object, gtype, gobject, or gobject descendant
procedure HandlePlainObject(ANode: TDomNode); // is a record/object with methods but no gtype
procedure HandleGType(ANode: TDomNode); // one step above plain object
procedure HandleClassStruct(ANode: TDomNode); // one step above GType. Is the 'Virtual' part of an object (VMT)
procedure HandleClass(ANode: TDomNode); // one step above GType. Is the object structure and it's methods. ClassStruct is like the VMT
procedure HandleInterface(ANode: TDomNode);
procedure AddGLibBaseTypes;
procedure AddType(AType: TGirBaseType);
public
function LookupTypeByName(AName: String; const ACType: String; SearchOnly: Boolean = False): TGirBaseType;
function ResolveFuzzyType(AFuzzyType: TgirFuzzyType): TGirBaseType;
function UsesGLib: Boolean;
procedure ResolveFuzzyTypes; // called after done
procedure ParseNode(ANode: TDomNode);
procedure ParseSubNode(ANode: TDomNode); // generally do not use outside of TgirNameSpace
constructor Create(AOwner:TObject; AImpliedNamespace: String);
constructor CreateFromRepositoryNode(AOwner:TObject; ANode: TDOMNode; AIncludes: TList);
destructor Destroy; override;
property NameSpace: String read FNameSpace;
property CIncludeName: String read FCIncludeName;
property CPackageName: String read FCPackageName;
property RequiredNameSpaces: TList Read FRequiredNameSpaces;
property SharedLibrary: String read FSharedLibrary;
property Version: String read FVersion;
property OnlyImplied: Boolean read FOnlyImplied;
property Owner: TObject Read FOwner;
// has all types in it (records classes classstructs bitfields callbacks gtypes unions etc) does not contain consts or functions
property Types: TFPHashObjectList read FTypes;
property Functions: TList read FFunctions;
property Constants: TList read FConstants;
property UnresolvedTypes: TList read FUnresolvedTypes write FUnresolvedTypes;
end;
{ TgirNamespaces }
TgirNamespaces = class(TList)
private
FOnNeedGirFile: TgirNeedGirFileEvent;
FOwner: TObject;
function GetNameSpace(AIndex: Integer): TgirNamespace;
procedure SetNameSpace(AIndex: Integer; const AValue: TgirNamespace);
procedure SetOnNeedGirFile(AValue: TgirNeedGirFileEvent);
public
constructor Create(AOwner: TObject);
function FindNameSpace(AName: String; Version: String = ''): TgirNamespace;
property NameSpace[AIndex: Integer]: TgirNamespace read GetNameSpace write SetNameSpace;
property Owner: TObject read FOwner;
property OnNeedGirFile: TgirNeedGirFileEvent read FOnNeedGirFile write SetOnNeedGirFile;
end;
implementation
uses
girErrors, SysUtils, girCTypesMapping;
{ TgirNamespaces }
function TgirNamespaces.GetNameSpace(AIndex: Integer): TgirNamespace;
begin
Result := TgirNamespace(Items[AIndex]);
end;
procedure TgirNamespaces.SetNameSpace(AIndex: Integer;
const AValue: TgirNamespace);
begin
Items[AIndex] := AValue;
end;
procedure TgirNamespaces.SetOnNeedGirFile(AValue: TgirNeedGirFileEvent);
begin
if FOnNeedGirFile=AValue then Exit;
FOnNeedGirFile:=AValue;
end;
constructor TgirNamespaces.Create(AOwner: TObject);
begin
FOwner := AOwner;
inherited Create;
end;
function TgirNamespaces.FindNameSpace(AName: String; Version: String=''): TgirNamespace;
var
i: Integer;
NameSpaceSearchedFor: Boolean;
Doc: TXMLDocument;
begin
Result := nil;
NameSpaceSearchedFor := False;
while Result = nil do
begin
for i := 0 to Count-1 do
begin
if NameSpace[i].NameSpace = AName then
Exit(NameSpace[i]);
end;
if NameSpaceSearchedFor then
Exit;
NameSpaceSearchedFor := True;
if Assigned(FOnNeedGirFile) then
begin
Doc := FOnNeedGirFile(Owner, AName+'-'+Version);
if Doc <> nil then
begin
(Owner as IgirParser).ParseNode(Doc.DocumentElement);
Doc.Free;
end;
end;
end;
end;
{ TgirNamespace }
procedure TgirNamespace.ParseNode(ANode: TDomNode);
begin
ANode := ANode.FirstChild;
while ANode <> nil do
begin
//girError(geDebug, 'Parsing Node "'+ANode.NodeName+'"');
ParseSubNode(ANode);
ANode := ANode.NextSibling;
end;
ResolveFuzzyTypes;
end;
procedure TgirNamespace.SetOnNeedGirFile(AValue: TgirNeedGirFileEvent);
begin
if FOnNeedGirFile=AValue then Exit;
FOnNeedGirFile:=AValue;
end;
function TgirNamespace.AddFuzzyType(AName: String; ACType: String
): TGirBaseType;
begin
Result := TgirFuzzyType.Create(Self, AName, ACType);
AddType(Result);
FUnresolvedTypes.Add(Result);
end;
procedure TgirNamespace.HandleAlias(ANode: TDomNode);
var
Item: TgirAlias;
begin
Item := TgirAlias.Create(Self, ANode);
Types.Add(Item.Name, Item);
end;
procedure TgirNamespace.HandleConstant(ANode: TDomNode);
var
Item: TgirConstant;
begin
Item := TgirConstant.Create(Self, ANode);
FConstants.Add(Item);
end;
procedure TgirNamespace.HandleEnumeration(ANode: TDomNode);
var
Item : TgirEnumeration;
begin
Item := TgirEnumeration.Create(Self, ANode);
Types.Add(Item.Name, Item);
end;
procedure TgirNamespace.HandleBitField(ANode: TDomNode);
var
Item : TgirBitField;
begin
Item := TgirBitField.Create(Self, ANode);
Types.Add(Item.Name, Item);
end;
procedure TgirNamespace.HandleCallback(ANode: TDOMNode);
var
Item: TgirCallback;
begin
Item := TgirCallback.Create(Self, ANode);
Types.Add(Item.Name, Item);
end;
procedure TgirNamespace.HandleFunction(ANode: TDOMNode);
var
Item: TgirFunction;
begin
Item := TgirFunction.Create(Self, ANode);
Functions.Add(Item);
end;
procedure TgirNamespace.HandleUnion(ANode: TDOMNode);
var
Item: TgirUnion;
begin
Item := TgirUnion.Create(Self, ANode);
Types.Add(Item.Name, Item);
end;
procedure TgirNamespace.HandleRecord(ANode: TDomNode);
var
Item: tgirRecord;
begin
if TDOMElement(ANode).GetAttribute('glib:is-gtype-struct-for') <> '' then // is gobject class
begin
HandleClassStruct(ANode);
end
else if TDOMElement(ANode).GetAttribute('glib:get-type') <> '' then // is GType
HandleGType(ANode)
else if (ANode.FindNode('method') <> nil) or (ANode.FindNode('constructor') <> nil) or (ANode.FindNode('function') <> nil) then // is Plain object that is not gtype
HandlePlainObject(ANode)
else
begin
Item := tgirRecord.Create(Self, ANode);
Types.Add(Item.Name, Item);
end;
end;
procedure TgirNamespace.HandlePlainObject(ANode: TDomNode);
var
Item: TgirObject;
begin
Item := TgirObject.Create(Self, ANode);
Types.Add(Item.Name, Item);
end;
procedure TgirNamespace.HandleGType(ANode: TDomNode);
var
Item: TgirGType;
begin
Item := TgirGType.Create(Self, ANode);
Types.Add(Item.Name, Item);
end;
procedure TgirNamespace.HandleClassStruct(ANode: TDomNode);
var
Item: TgirClassStruct;
begin
Item := TgirClassStruct.Create(Self, ANode);
AddType(Item);
end;
procedure TgirNamespace.HandleClass(ANode: TDomNode);
var
Item: TgirClass;
begin
Item := TgirClass.Create(Self, ANode);
AddType(Item);
end;
procedure TgirNamespace.HandleInterface(ANode: TDomNode);
var
Item: TgirInterface;
begin
Item := TgirInterface.Create(Self, ANode);
AddType(Item);
end;
procedure TgirNamespace.AddGLibBaseTypes;
function AddNativeTypeDef(GType: String; PascalCName: String; TranslatedName: String): TgirNativeTypeDef;
var
NativeType: TgirNativeTypeDef;
begin
NativeType:= TgirNativeTypeDef.Create(Self, GType, PascalCName);
if TranslatedName <> '' then
NativeType.TranslatedName:=TranslatedName;
NativeType.ImpliedPointerLevel:=3;
Types.Add(NativeType.Name, NativeType);
Result := NativeType;
end;
var
i: Integer;
begin
for i := 0 to CTypesMax-1 do
AddNativeTypeDef(TypesGTypes[i], TypesPascalCTypes[i], '');
end;
procedure TgirNamespace.AddType(AType: TGirBaseType);
var
PrevFound: TGirBaseType = nil;
begin
PrevFound := TGirBaseType(FTypes.Find(AType.Name));
if (PrevFound <> nil) and (PrevFound.ObjectType = otFuzzyType) then
begin
(PrevFound as TgirFuzzyType).ResolvedType := AType;
end;
//if PrevFound <> nil then WriteLn('Found Name Already Added: ', AType.Name, ' ', PrevFound.ObjectType, ' ', AType.ObjectType);
if PrevFound = nil then
FTypes.Add(AType.Name, AType);
end;
procedure TgirNamespace.ResolveFuzzyTypes;
var
i: Integer;
FuzzyI: Integer;
Fuzzy: TgirFuzzyType;
FuzzyP: Pointer absolute Fuzzy;
Tmp: TGirBaseType;
StillFuzzy: TList;
Current: TGirBaseType;
ReqNS: TgirNamespace;
begin
i:= 0;
FuzzyI := 0;
Fuzzy := nil;
StillFuzzy := TList.Create;
while (i < FTypes.Count) or (Fuzzy <> nil) do
begin
// make our loop safe
if i >= FTypes.Count then
begin
i := FuzzyI+1;
StillFuzzy.Add(Fuzzy);
Fuzzy := nil;
continue;
end;
Tmp := TGirBaseType(FTypes.Items[i]);
if Fuzzy <> nil then
begin
if {(Tmp.CType = Fuzzy.CType) or} (Tmp.Name = Fuzzy.Name) then
begin
Fuzzy.ResolvedType := Tmp;
Tmp.ImpliedPointerLevel:=Fuzzy.ImpliedPointerLevel;
i := FuzzyI+1;
Fuzzy := nil;
//WriteLn('Resolved Fuzzy Type: ', Tmp.CType);
continue;
end;
end;
if (Fuzzy = nil) and (Tmp.ObjectType = otFuzzyType) and (TgirFuzzyType(Tmp).ResolvedType = nil) then
begin
if i >= FTypes.Count then
break;
FuzzyI:=i;
Fuzzy := TgirFuzzyType(Tmp);
//WriteLn('Looking For: ',Fuzzy.CType);
end;
inc(i);
end;
// if the types are still fuzzy then we will search used namespaces for what we want
for FuzzyP in StillFuzzy do //FuzzyP is Fuzzy absolute
begin
if Fuzzy.ResolvedType <> nil then
continue;
for i := 0 to RequiredNameSpaces.Count-1 do
begin
ReqNS := TgirNamespace(RequiredNameSpaces.Items[i]);
Current := ReqNS.LookupTypeByName(Fuzzy.Name, '', True);
if Current <> nil then
begin
if (Current.ObjectType = otFuzzyType) and (TgirFuzzyType(Current).ResolvedType <> nil) then
Current := TgirFuzzyType(Current).ResolvedType;
Fuzzy.ResolvedType := Current;
Break;
end;
end;
end;
StillFuzzy.Free;
end;
procedure TgirNamespace.ParseSubNode(ANode: TDomNode);
begin
case GirTokenNameToToken(ANode.NodeName) of
gtAlias: HandleAlias(ANode);
gtConstant: HandleConstant(ANode);
gtRecord: HandleRecord(ANode);
gtBitField: HandleBitField(ANode);
gtEnumeration: HandleEnumeration(ANode);
gtCallback: HandleCallback(ANode);
gtUnion: HandleUnion(ANode);
gtFunction: HandleFunction(ANode);
gtClass: HandleClass(ANode);
gtInterface: HandleInterface(ANode);
gtMethod: HandleFunction(ANode);
else
girError(geError, 'Unknown NodeType: '+ANode.NodeName);
end;
end;
function TgirNamespace.LookupTypeByName(AName: String; const ACType: String; SearchOnly: Boolean = False): TGirBaseType;
function StripPointers(ACPointeredType: String; PtrLevel: PInteger = nil): String;
var
i: Integer;
begin
for i := Length(ACPointeredType) downto 1 do
if ACPointeredType[i] = '*' then
begin
Delete(ACPointeredType, i, 1);
end;
if PtrLevel <> nil then
Inc(PtrLevel^);
Result := ACPointeredType;
end;
var
NS: TgirNamespace;
NSString: String;
FPos: Integer;
PointerLevel: Integer = 0;
PlainCType: String;
begin
Result := nil;
NS := Self;
// some basic fixes
PlainCType:=StringReplace(StripPointers(ACType, @PointerLevel), ' ', '_', [rfReplaceAll]);
if (PlainCType = 'gchar') or {(PlainCType = 'guchar') or} (PlainCType = 'char') or (PlainCType = 'const_char') then
AName := 'GLib.utf8';
if (PlainCType = 'GType') {or (AName = 'Type')} or (AName = 'GType')then
AName := 'GLib.Type';
FPos := Pos('.', AName);
if FPos > 0 then // type includes namespace "NameSpace.Type"
begin
NSString:=Copy(AName,1,FPos-1);
//NS := (Owner As TgirNamespaces).FindNameSpace(NSString);
NS := TgirNamespaces(Owner).FindNameSpace(NSString);
if NS = nil then
girError(geError, 'Referenced Namespace "'+NSString+'" not found while looking for '+AName);
AName := Copy(AName, FPos+1, Length(AName));
end;
if NS <> Self then SearchOnly:=True;
//if NS <> Self then WriteLn('Self NS = ', NameSpace, ' Lookup NS = ', NS.NameSpace);
Result := TGirBaseType(NS.Types.Find(AName));
if (Result <> nil) and (Result.ObjectType = otFuzzyType) and (TgirFuzzyType(Result).ResolvedType <> nil) then
Result := TgirFuzzyType(Result).ResolvedType;
// if we find a result in another namespace then we need to depend on that namespace/unit
if (NS <> nil) and (NS <> Self) and (Result <> nil) then
if FRequiredNameSpaces.IndexOf(NS) = -1 then
FRequiredNameSpaces.Add(NS);
if (Result = nil) and Not SearchOnly then
Result := NS.AddFuzzyType(AName, ACType);
if Result <> nil then
Result.ImpliedPointerLevel:=PointerLevel;
end;
function TgirNamespace.ResolveFuzzyType(AFuzzyType: TgirFuzzyType): TGirBaseType;
var
i: Integer;
begin
for i := 0 to FTypes.Count-1 do
begin
if (TGirBaseType(FTypes[i]) <> AFuzzyType) and (TGirBaseType(FTypes[i]).Name = AFuzzyType.Name) then
Exit(TGirBaseType(FTypes[i]));
end;
end;
function TgirNamespace.UsesGLib: Boolean;
var
Tmp: Pointer;
NS: TgirNamespace absolute Tmp;
begin
Result := False;
if Pos('glib', LowerCase(NameSpace)) = 1 then
Exit(True);
for Tmp in RequiredNameSpaces do
if Pos('glib',LowerCase(NS.NameSpace)) = 1 then
Exit(True);
end;
constructor TgirNamespace.Create(AOwner:TObject; AImpliedNamespace: String);
begin
Fowner:=AOwner;
FOnlyImplied:=True;
FNameSpace:=AImpliedNamespace;
girError(geDebug, 'Creating Stub for namespace: '+ AImpliedNamespace);
end;
constructor TgirNamespace.CreateFromRepositoryNode(AOwner:TObject; ANode: TDOMNode; AIncludes: TList);
procedure SetCInclude;
var
Child: TDomElement;
begin
Child := TDOMElement(ANode.FindNode('c:include name'));
if (Child <> nil) and Child.InheritsFrom(TDOMElement) then
FCIncludeName:= Child.GetAttribute('name');
end;
procedure SetPackage;
var
Child: TDOMElement;
begin
Child := TDOMElement(ANode.FindNode('package'));
if (Child <> nil) and Child.InheritsFrom(TDOMElement) then
FCPackageName:=Child.GetAttribute('name');
end;
var
Node: TDOMElement;
begin
FOwner := AOwner;
if ANode = nil then
girError(geError, 'expected namespace got nil');
if ANode.NodeName <> 'repository' then
girError(geError, 'expected "repository" got '+ANode.NodeName);
Node := TDOMElement( ANode.FindNode('namespace') );
FNameSpace:=Node.GetAttribute('name');
FRequiredNameSpaces := AIncludes;
FSharedLibrary:=Node.GetAttribute('shared-library');
FVersion:=Node.GetAttribute('version');
SetCInclude;
SetPackage;
girError(geDebug, Format('Creating namespace=%s Version=%s LibName=%s',[FNameSpace, FVersion, FSharedLibrary]));
FConstants := TList.Create;
FFunctions := TList.Create;
FTypes := TFPHashObjectList.Create(True);
FUnresolvedTypes := TList.Create;
if FNameSpace = 'GLib' then
AddGLibBaseTypes;
end;
destructor TgirNamespace.Destroy;
begin
FConstants.Free;
FFunctions.Free;
FTypes.Free;
FUnresolvedTypes.Free;
if Assigned(FRequiredNameSpaces) then
FRequiredNameSpaces.Free;
inherited Destroy;
end;
end.