Make type swapping generic and get rid of config files (fixes #3) #13

Merged
Letterus merged 29 commits from generic-type-swapping into main 6 months ago
  1. 1
      src/BaseClasses/OGTKObject.h
  2. 40
      src/Config/extra_imports.json
  3. 549
      src/Config/swap_types.json
  4. 2
      src/GIR/GIRReturnValue.m
  5. 22
      src/Generator/OGTKClass.h
  6. 78
      src/Generator/OGTKClass.m
  7. 1
      src/Generator/OGTKClassWriter.h
  8. 112
      src/Generator/OGTKClassWriter.m
  9. 226
      src/Generator/OGTKMapper.h
  10. 445
      src/Generator/OGTKMapper.m
  11. 11
      src/Generator/OGTKMethod.h
  12. 57
      src/Generator/OGTKMethod.m
  13. 2
      src/Generator/OGTKParameter.h
  14. 3
      src/Generator/OGTKParameter.m
  15. 40
      src/Generator/OGTKUtil.h
  16. 181
      src/Generator/OGTKUtil.m
  17. 1
      src/Gir2Objc.h
  18. 300
      src/Gir2Objc.m
  19. 280
      src/Output/makefile
  20. 1
      src/makefile

1
src/BaseClasses/OGTKObject.h

@ -34,6 +34,7 @@
* C imports
*/
#import <gtk/gtk.h>
#import <gtk/gtk-a11y.h>
/**
* The base class for all CoreGTK wrapper classes

40
src/Config/extra_imports.json

@ -1,40 +0,0 @@
{
"OGTKMenuButton": [
"\"OGTKMenu.h\""
],
"OGTKTextMark": [
],
"GtkBuilder": [
"\"OGTKWidget.h\""
],
"OGTKStyleContext": [
],
"OGTKClipboard": [
"\"OGTKTextBuffer.h\""
],
"OGTKContainer": [
"\"OGTKAdjustment.h\""
],
"OGTKTextBuffer": [
"\"OGTKTextChildAnchor.h\"",
"\"OGTKTextTag.h\"",
"\"OGTKTextTagTable.h\"",
"\"OGTKTextMark.h\""
],
"OGTKWindow": [
"\"OGTKWindowGroup.h\""
],
"OGTKApplication": [
"\"OGTKWindow.h\""
],
"OGTKWidget": [
"\"OGTKAccelGroup.h\"",
"\"OGTKClipboard.h\"",
"\"OGTKRcStyle.h\"",
"\"OGTKSettings.h\"",
"\"OGTKStyle.h\"",
"\"OGTKStyleContext.h\""
],
"OGTKSettings": [
]
}

549
src/Config/swap_types.json

File diff suppressed because it is too large Load Diff

2
src/GIR/GIRReturnValue.m

@ -67,7 +67,7 @@
for (OFString* key in dict) {
id value = [dict objectForKey:key];
// TODO: Do we nee nullable?
// TODO: Do we need nullable?
if ([key isEqual:@"text"] || [key isEqual:@"nullable"]) {
// Do nothing
} else if ([key isEqual:@"transfer-ownership"]) {

22
src/Generator/OGTKClass.h

@ -26,7 +26,6 @@
*/
#import <ObjFW/ObjFW.h>
#import "OGTKMethod.h"
/**
@ -35,26 +34,45 @@
@interface OGTKClass : OFObject {
OFString* _cName;
OFString* _cType;
OFString* _parentName;
OFString* _cParentType;
OFString* _cSymbolPrefix;
OFString* _cNSSymbolPrefix;
OFString* _cNSIdentifierPrefix;
OFMutableArray* _constructors;
OFMutableArray* _functions;
OFMutableArray* _methods;
OFMutableSet* _dependsOnClasses;
OFMutableSet* _forwardDeclarationForClasses;
bool _visited;
@private
OFString* _typeWithoutPrefix;
}
@property (copy, nonatomic) OFString* cName;
@property (copy, nonatomic) OFString* cType;
@property (readonly, nonatomic) OFString* type;
@property (copy, nonatomic) OFString* parentName;
@property (copy, nonatomic) OFString* cParentType;
@property (readonly, nonatomic) OFString* name;
@property (copy, nonatomic) OFString* cSymbolPrefix;
@property (copy, nonatomic) OFString* cNSSymbolPrefix;
@property (copy, nonatomic) OFString* cNSIdentifierPrefix;
@property (readonly, nonatomic) OFArray* constructors;
@property (readonly, nonatomic) bool hasConstructors;
@property (readonly, nonatomic) OFArray* functions;
@property (readonly, nonatomic) bool hasFunctions;
@property (readonly, nonatomic) OFArray* methods;
@property (readonly, nonatomic) bool hasMethods;
@property (readonly, nonatomic) OFMutableSet* dependsOnClasses;
@property (readonly, nonatomic) OFMutableSet* forwardDeclarationForClasses;
@property (nonatomic) bool visited;
- (void)addConstructor:(OGTKMethod*)ctor;
- (void)addFunction:(OGTKMethod*)fun;
- (void)addMethod:(OGTKMethod*)meth;
- (void)addDependency:(OFString*)cType;
- (void)removeForwardDeclarationsFromDependencies;
- (void)addForwardDeclarationForClass:(OFString*)cType;
@end

78
src/Generator/OGTKClass.m

@ -29,9 +29,16 @@
* Objective-C imports
*/
#import "OGTKClass.h"
#import "../Exceptions/OGTKReceivedNilExpectedStringException.h"
@implementation OGTKClass
@synthesize cName = _cName, cType = _cType, cParentType = _cParentType;
@synthesize cName = _cName, cType = _cType, parentName = _parentName,
cParentType = _cParentType, cSymbolPrefix = _cSymbolPrefix,
cNSSymbolPrefix = _cNSSymbolPrefix,
cNSIdentifierPrefix = _cNSIdentifierPrefix,
dependsOnClasses = _dependsOnClasses,
forwardDeclarationForClasses = _forwardDeclarationForClasses,
visited = _visited;
- (instancetype)init
{
@ -41,6 +48,9 @@
_constructors = [[OFMutableArray alloc] init];
_functions = [[OFMutableArray alloc] init];
_methods = [[OFMutableArray alloc] init];
_dependsOnClasses = [[OFMutableSet alloc] init];
_forwardDeclarationForClasses = [[OFMutableSet alloc] init];
_visited = false;
} @catch (id e) {
[self release];
@throw e;
@ -49,14 +59,45 @@
return self;
}
- (OFString*)type
- (void)dealloc
{
return [OGTKUtil swapTypes:_cType];
[_cName release];
[_cType release];
[_parentName release];
[_cParentType release];
[_cSymbolPrefix release];
[_cNSIdentifierPrefix release];
[_cNSSymbolPrefix release];
[_constructors release];
[_functions release];
[_methods release];
[_dependsOnClasses release];
[_forwardDeclarationForClasses release];
[_typeWithoutPrefix release];
[super dealloc];
}
- (OFString*)name
- (OFString*)type
{
return [OFString stringWithFormat:@"OGTK%@", _cName];
if (self.cType == nil)
@throw [OGTKReceivedNilExpectedStringException exception];
if ([self.cNSIdentifierPrefix isEqual:@"Gtk"] &&
[self.cType hasPrefix:@"Gtk"]) {
if (_typeWithoutPrefix == nil) {
size_t prefixLength = self.cNSIdentifierPrefix.length;
_typeWithoutPrefix = [self.cType substringFromIndex:prefixLength];
[_typeWithoutPrefix retain];
}
return [OFString stringWithFormat:@"OGTK%@", _typeWithoutPrefix];
}
return [OFString stringWithFormat:@"OG%@", self.cType];
}
- (void)addConstructor:(OGTKMethod*)constructor
@ -83,6 +124,21 @@
}
}
- (void)addDependency:(OFString*)cType
{
[_dependsOnClasses addObject:cType];
}
- (void)removeForwardDeclarationsFromDependencies
{
[_dependsOnClasses minusSet:_forwardDeclarationForClasses];
}
- (void)addForwardDeclarationForClass:(OFString*)cType
{
[_forwardDeclarationForClasses addObject:cType];
}
- (OFArray*)functions
{
return [[_functions copy] autorelease];
@ -110,16 +166,4 @@
return (_methods.count > 0);
}
- (void)dealloc
{
[_cName release];
[_cType release];
[_cParentType release];
[_constructors release];
[_functions release];
[_methods release];
[super dealloc];
}
@end

1
src/Generator/OGTKClassWriter.h

@ -32,6 +32,7 @@
#import "OGTKClass.h"
#import "OGTKUtil.h"
#import "OGTKMapper.h"
/**
* Functions to write in memory Class representation to file as ObjGTK source

112
src/Generator/OGTKClassWriter.m

@ -42,19 +42,20 @@
@try {
// Header
OFString* hFilename =
[[outputDir stringByAppendingPathComponent:[cgtkClass name]]
[[outputDir stringByAppendingPathComponent:[cgtkClass type]]
stringByAppendingString:@".h"];
[[OGTKClassWriter headerStringFor:cgtkClass] writeToFile:hFilename];
// Source
OFString* sFilename =
[[outputDir stringByAppendingPathComponent:[cgtkClass name]]
[[outputDir stringByAppendingPathComponent:[cgtkClass type]]
stringByAppendingString:@".m"];
[[OGTKClassWriter sourceStringFor:cgtkClass] writeToFile:sFilename];
} @catch (id e) {
OFLog(@"Warning: Cannot generate file for definition for class %@. "
@"Definition may be incorrect. Skipping…",
cgtkClass.name);
OFLog(@"Warning: Cannot generate file for type %@. "
@"Exception %@, description: %@ "
@"Class definition may be incorrect. Skipping…",
cgtkClass.cName, [e class], [e description]);
}
}
@ -62,30 +63,41 @@
{
OFMutableString* output = [[OFMutableString alloc] init];
// OFLog(@"Writing header file for class %@.", [cgtkClass name]);
// OFLog(@"Writing header file for class %@.", [cgtkClass type]);
[output appendString:[OGTKClassWriter
generateLicense:[OFString stringWithFormat:@"%@.h",
[cgtkClass name]]]];
[cgtkClass type]]]];
// Imports
[output appendFormat:@"\n#import \"%@.h\"\n",
[OGTKUtil swapTypes:[cgtkClass cParentType]]];
OFArray* extraImports = [OGTKUtil extraImports:[cgtkClass type]];
[output appendString:@"\n"];
if (extraImports != nil) {
for (OFString* imp in extraImports) {
if (imp != nil) {
[output appendFormat:@"#import %@\n", imp];
}
}
// Imports/Dependencies
for (OFString* dependency in cgtkClass.dependsOnClasses) {
if ([[OGTKMapper swapTypes:dependency] isEqual:@"OGTKObject"])
[output appendString:@"#import \"OGTKObject.h\"\n"];
else if ([OGTKMapper isGobjType:dependency] &&
[OGTKMapper isTypeSwappable:dependency])
[output appendFormat:@"#import \"%@.h\"\n",
[OGTKMapper swapTypes:dependency]];
}
[output appendString:@"\n"];
// Forward class declarations (for circular dependencies)
if (cgtkClass.forwardDeclarationForClasses.count > 0) {
for (OFString* gobjClassName in cgtkClass
.forwardDeclarationForClasses) {
if ([OGTKMapper isGobjType:gobjClassName] &&
[OGTKMapper isTypeSwappable:gobjClassName])
[output appendFormat:@"@class %@;\n",
[OGTKMapper swapTypes:gobjClassName]];
}
[output appendString:@"\n"];
}
// Interface declaration
[output appendFormat:@"@interface %@ : %@\n{\n\n}\n\n", [cgtkClass name],
[OGTKUtil swapTypes:[cgtkClass cParentType]]];
[output appendFormat:@"@interface %@ : %@\n{\n\n}\n\n", [cgtkClass type],
[OGTKMapper swapTypes:[cgtkClass cParentType]]];
// Function declarations
if ([cgtkClass hasFunctions]) {
@ -128,16 +140,29 @@
{
OFMutableString* output = [[OFMutableString alloc] init];
// OFLog(@"Writing implementation file for class %@.", [cgtkClass name]);
// OFLog(@"Writing implementation file for class %@.", [cgtkClass type]);
[output appendString:[OGTKClassWriter
generateLicense:[OFString stringWithFormat:@"%@.m",
[cgtkClass name]]]];
[cgtkClass type]]]];
// Imports
[output appendFormat:@"\n#import \"%@.h\"\n\n", [cgtkClass name]];
[output appendFormat:@"\n#import \"%@.h\"\n\n", [cgtkClass type]];
// Imports for forward class declarations (for circular dependencies)
if (cgtkClass.forwardDeclarationForClasses.count > 0) {
for (OFString* gobjClassName in cgtkClass
.forwardDeclarationForClasses) {
if ([OGTKMapper isGobjType:gobjClassName] &&
[OGTKMapper isTypeSwappable:gobjClassName])
[output appendFormat:@"#import \"%@.h\"\n",
[OGTKMapper swapTypes:gobjClassName]];
}
[output appendString:@"\n"];
}
// Implementation declaration
[output appendFormat:@"@implementation %@\n\n", [cgtkClass name]];
[output appendFormat:@"@implementation %@\n\n", [cgtkClass type]];
// Function implementations
for (OGTKMethod* func in [cgtkClass functions]) {
@ -146,28 +171,28 @@
[output appendString:@"\n{\n"];
if ([func returnsVoid]) {
[output appendFormat:@"\t%@(%@);\n", [func cName],
[output appendFormat:@"\t%@(%@);\n", [func cIdentifier],
[OGTKClassWriter
generateCParameterListString:[func parameters]]];
} else {
// Need to add "return ..."
[output appendString:@"\treturn "];
if ([OGTKUtil isTypeSwappable:[func cReturnType]]) {
if ([OGTKMapper isTypeSwappable:[func cReturnType]]) {
// Need to swap type on return
[output
appendString:
[OGTKUtil
[OGTKMapper
convertType:[func cReturnType]
withName:[OFString
stringWithFormat:@"%@(%@)",
[func cName],
[func cIdentifier],
[OGTKClassWriter
generateCParameterListString:
[func parameters]]]
toType:[func returnType]]];
} else {
[output appendFormat:@"%@(%@)", [func cName],
[output appendFormat:@"%@(%@)", [func cIdentifier],
[OGTKClassWriter
generateCParameterListString:[func parameters]]];
}
@ -192,7 +217,7 @@
withConstructor:
[OFString
stringWithFormat:@"%@(%@)",
[ctor cName],
[ctor cIdentifier],
[OGTKClassWriter
generateCParameterListString:
[ctor parameters]]]]];
@ -205,7 +230,7 @@
// Self type method implementation
[output appendFormat:@"- (%@*)%@\n{\n\treturn %@;\n}\n\n",
[cgtkClass cType], [[cgtkClass cName] uppercaseString],
[OGTKUtil selfTypeMethodCall:[cgtkClass cType]]];
[OGTKMapper selfTypeMethodCall:[cgtkClass cType]]];
for (OGTKMethod* meth in [cgtkClass methods]) {
[output appendFormat:@"- (%@)%@", [meth returnType], [meth sig]];
@ -214,7 +239,7 @@
if ([meth returnsVoid]) {
[output
appendFormat:@"\t%@(%@);\n", [meth cName],
appendFormat:@"\t%@(%@);\n", [meth cIdentifier],
[OGTKClassWriter
generateCParameterListWithInstanceString:[cgtkClass type]
andParams:[meth
@ -223,15 +248,16 @@
// Need to add "return ..."
[output appendString:@"\treturn "];
if ([OGTKUtil isTypeSwappable:[meth cReturnType]]) {
if ([OGTKMapper isTypeSwappable:[meth cReturnType]]) {
// Need to swap type on return
[output
appendString:
[OGTKUtil
[OGTKMapper
convertType:[meth cReturnType]
withName:
[OFString
stringWithFormat:@"%@(%@)", [meth cName],
stringWithFormat:@"%@(%@)",
[meth cIdentifier],
[OGTKClassWriter
generateCParameterListWithInstanceString:
[cgtkClass type]
@ -241,7 +267,7 @@
toType:[meth returnType]]];
} else {
[output
appendFormat:@"%@(%@)", [meth cName],
appendFormat:@"%@(%@)", [meth cIdentifier],
[OGTKClassWriter
generateCParameterListWithInstanceString:[cgtkClass
type]
@ -267,9 +293,9 @@
size_t i = 0, count = params.count;
for (OGTKParameter* param in params) {
[paramsOutput appendString:[OGTKUtil convertType:param.type
withName:param.name
toType:param.cType]];
[paramsOutput appendString:[OGTKMapper convertType:param.type
withName:param.name
toType:param.cType]];
if (i++ < count - 1)
[paramsOutput appendString:@", "];
@ -284,7 +310,7 @@
int i;
OFMutableString* paramsOutput = [OFMutableString string];
[paramsOutput appendString:[OGTKUtil selfTypeMethodCall:instanceType]];
[paramsOutput appendString:[OGTKMapper selfTypeMethodCall:instanceType]];
if (params != nil && [params count] > 0) {
[paramsOutput appendString:@", "];
@ -294,9 +320,9 @@
// Start at index 1
for (i = 0; i < [params count]; i++) {
p = [params objectAtIndex:i];
[paramsOutput appendString:[OGTKUtil convertType:[p type]
withName:[p name]
toType:[p cType]]];
[paramsOutput appendString:[OGTKMapper convertType:[p type]
withName:[p name]
toType:[p cType]]];
if (i < [params count] - 1) {
[paramsOutput appendString:@", "];

226
src/Generator/OGTKMapper.h

@ -0,0 +1,226 @@
/*
* OGTKMapper.h
* This file is part of ObjGTKGen
*
* Copyright (C) 2021 - Johannes Brakensiek
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Modified by the ObjGTK Team, 2021. See the AUTHORS file for a
* list of people on the ObjGTK Team.
* See the ChangeLog files for a list of changes.
*/
#import <ObjFW/ObjFW.h>
@class OGTKClass;
@interface OGTKMapper : OFObject {
OFMutableDictionary OF_GENERIC(OFString*, OGTKClass*)
* _gobjTypeToClassMapping;
OFMutableDictionary OF_GENERIC(OFString*, OGTKClass*)
* _girNameToClassMapping;
OFMutableDictionary OF_GENERIC(OFString*, OGTKClass*)
* _objcTypeToClassMapping;
}
/**
* @brief Dictionary that maps Gobj type names to class information (OGTKClass)
*/
@property (readonly, nonatomic) OFMutableDictionary OF_GENERIC(
OFString*, OGTKClass*)
* gobjTypeToClassMapping;
/**
* @brief Dictionary that maps general type names that are specified in the .gir
* file to class information (OGTKClass)
*/
@property (readonly, nonatomic) OFMutableDictionary OF_GENERIC(
OFString*, OGTKClass*)
* girNameToClassMapping;
/**
* @brief Dictionary that maps ObjC type names to class information (OGTKClass)
*/
@property (readonly, nonatomic) OFMutableDictionary OF_GENERIC(
OFString*, OGTKClass*)
* objcTypeToClassMapping;
/**
* @brief Singleton
* @return instancetype One unique instance of OGTKMapper
*/
+ (instancetype)sharedMapper;
/**
* @brief Adds a class to the mapping dictionaries
* @param classInfo The object describing the class
*/
- (void)addClass:(OGTKClass*)classInfo;
/**
* @brief Iterates through all the class information objects retained in the
* dict and looks for class dependencies
* @details Dependencies are stored as Gobj types as well and should be
* mapped/swapped using this class when actually written out to ObjC files.
*
* For this method to work all the class information objects need to be filled
* with correct data.
*/
- (void)determineDependencies;
/**
* @brief Iterates through all the dependencies of all the class information
* objects retained in the dict
* @details This will only work if ```determineDependencies``` is called before
* @see -determineDependencies
*/
- (void)detectAndMarkCircularDependencies;
/**
* @brief Returns if a given type string is listed as Gobj class type
* @return True if the given string is a listed Gobj class type
*/
- (bool)isGobjType:(OFString*)type;
/**
* @brief Returns if a given type string is listed as ObjC class type
* @return True if the given string is a listed ObjC class type
*/
- (bool)isObjcType:(OFString*)type;
/**
* @brief Tries to swap Gobj data types with ObjC data types and vice versa.
* Returns the input if it can't.
* @details In addition to the class information provided via
* ```addClass:```before this method will also swap basic data types like gchar*
* and gboolean.
*
* Class types of basic (runtime) libraries which Gtk depends on, f.e. Glib, are
* always mapped to OGTKObject because we do not want to wrap and use those but
* use ObjFW classes instead.
*
* Pointers to pointer (**) currently are not swapped because conversion of
* these types is not yet implemented by ```convertType:withName:toType```.
* @see -convertType:withName:toType
*
*/
- (OFString*)swapTypes:(OFString*)type;
/**
* @brief Tests if the given type is swappable by ```swapTypes:```
* @param type The Gobj or ObjC type name
* @return True if the given type is swappable by ```swapTypes:```
*/
- (bool)isTypeSwappable:(OFString*)type;
/**
* @brief Provides the string holding the source code snipped to convert
* ```fromType``` to ```toType``` holding ```name``` as variable name
* @details This method is the corresponding part to ```swapTypes:```. While
* that swaps type names this is meant to provide the code snipped string needed
* to transfor one type to the other.
* @param fromType The Gobj or ObjC type name to provide conversion code for.
* @param name The name of the variable in the source code, defined by fromType.
* @param toType The ObjC or Gobj type name which the returned code should
* convert to.
* @return The source code needed to convert ```fromType``` to
* ```toType``` holding ```name``` as variable name
*
*/
- (OFString*)convertType:(OFString*)fromType
withName:(OFString*)name
toType:(OFString*)toType;
/**
* @brief Returns the appropriate self referencing method call for the type
* (i.e. -(type)[self TYPE] or GTK_TYPE([self GOBJECT]) to unwrap the Gobj
* object instance
* @param type The Gobj or ObjC class name for which the method call should be
* generated.
* @return The code snipped holding the method call snipped to unwrap the Gobj
* object instance.
*
*/
- (OFString*)selfTypeMethodCall:(OFString*)type;
/**
* @brief Returns the cType (Gobj type) for a name provided by a gir file
* @details In some cases the gir files do not provide cTypes (Gobj/Glib type
* names). Then this method may be used to retrieve the correct cType for gir
* class (name) definition.
*
* This only works if the necessary class information has been provided using
* ```addClass:``` before.
* @see -addClass:
*
*/
- (OFString*)getCTypeFromName:(OFString*)name;
/**
* @brief Return is ```type``` is known by the Gobj type dict
*
*/
+ (bool)isGobjType:(OFString*)type;
/**
* @brief Return is ```type``` is known by the ObjC type dict
*
*/
+ (bool)isObjcType:(OFString*)type;
/**
* @brief Tries to swap Gobj data types with ObjC data types and vice versa.
* Returns the input if it can't. Singleton access shortcut.
* @see -swapTypes:
*/
+ (OFString*)swapTypes:(OFString*)type;
/**
* @brief Tests if the given type is swappable by ```swapTypes:``` Singleton
* access shortcut.
* @see -isTypeSwappable:
*/
+ (bool)isTypeSwappable:(OFString*)type;
/**
* @brief Provides the string holding the source code snipped to convert
* ```fromType``` to ```toType``` holding ```name``` as variable name. Singleton
* access shortcut.
* @see -convertType:withName:toType:
*/
+ (OFString*)convertType:(OFString*)fromType
withName:(OFString*)name
toType:(OFString*)toType;
/**
* @brief Returns the appropriate self referencing method call for the type
* (i.e. -(type)[self TYPE] or GTK_TYPE([self GOBJECT]) to unwrap the Gobj
* object instance. Singleton access shortcut.
* @see -selfTypeMethodCall:
*/
+ (OFString*)selfTypeMethodCall:(OFString*)type;
/**
* @brief Returns the appropriate self referencing method call for the type
* (i.e. -(type)[self TYPE] or GTK_TYPE([self GOBJECT]) to unwrap the Gobj
* object instance. Singleton access shortcut.
* @see -getCTypeFromName:
*/
+ (OFString*)getCTypeFromName:(OFString*)name;
@end

445
src/Generator/OGTKMapper.m

@ -0,0 +1,445 @@
/*
* OGTKMapper.m
* This file is part of ObjGTKGen
*
* Copyright (C) 2021 - Johannes Brakensiek
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Modified by the ObjGTK Team, 2021. See the AUTHORS file for a
* list of people on the ObjGTK Team.
* See the ChangeLog files for a list of changes.
*/
#import "OGTKMapper.h"
#import "OGTKClass.h"
#import "OGTKParameter.h"
static OGTKMapper* sharedMyMapper = nil;
@implementation OGTKMapper
@synthesize gobjTypeToClassMapping = _gobjTypeToClassMapping,
girNameToClassMapping = _girNameToClassMapping,
objcTypeToClassMapping = _objcTypeToClassMapping;
#pragma mark - Object lifecycle
- (instancetype)init
{
self = [super init];
@try {
_gobjTypeToClassMapping = [[OFMutableDictionary alloc] init];
_girNameToClassMapping = [[OFMutableDictionary alloc] init];
_objcTypeToClassMapping = [[OFMutableDictionary alloc] init];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (void)dealloc
{
[_gobjTypeToClassMapping release];
[_girNameToClassMapping release];
[_objcTypeToClassMapping release];
[super dealloc];
}
+ (instancetype)sharedMapper
{
@synchronized(self) {
if (sharedMyMapper == nil)
sharedMyMapper = [[self alloc] init];
}
return sharedMyMapper;
}
#pragma mark - Public methods - domain logic
- (void)addClass:(OGTKClass*)classInfo
{
[_gobjTypeToClassMapping setObject:classInfo forKey:classInfo.cType];
[_girNameToClassMapping setObject:classInfo forKey:classInfo.cName];
[_objcTypeToClassMapping setObject:classInfo forKey:classInfo.type];
}
- (bool)isGobjType:(OFString*)type
{
return ([_gobjTypeToClassMapping objectForKey:[self stripAsterisks:type]]
!= nil);
}
- (bool)isObjcType:(OFString*)type
{
return ([_objcTypeToClassMapping objectForKey:[self stripAsterisks:type]]
!= nil);
}
- (void)determineDependencies
{
for (OFString* className in _objcTypeToClassMapping) {
OGTKClass* classInfo = [_objcTypeToClassMapping objectForKey:className];
if (classInfo.cParentType != nil)
[classInfo addDependency:classInfo.cParentType];
for (OGTKMethod* constructor in classInfo.constructors)
[self addDependenciesFromMethod:constructor to:classInfo];
for (OGTKMethod* function in classInfo.functions)
[self addDependenciesFromMethod:function to:classInfo];
for (OGTKMethod* method in classInfo.methods)
[self addDependenciesFromMethod:method to:classInfo];
}
}
- (void)detectAndMarkCircularDependencies
{
for (OFString* className in _objcTypeToClassMapping) {
OGTKClass* classInfo = [_objcTypeToClassMapping objectForKey:className];
OFMutableDictionary* stack = [[OFMutableDictionary alloc] init];
[stack setObject:@"1" forKey:classInfo.cType];
[self walkDependencyTreeFrom:classInfo usingStack:stack];
[stack release];
}
}
- (OFString*)swapTypes:(OFString*)type
{
// Convert basic types by hardcoding
if ([type isEqual:@"AtkObject"] || [type isEqual:@"GApplication"] ||
[type isEqual:@"GInitiallyUnowned"] || [type isEqual:@"GObject"] ||
[type isEqual:@"GMountOperation"])
return @"OGTKObject";
else if ([type isEqual:@"const gchar*"] || [type isEqual:@"gchar*"])
return @"OFString*";
else if ([type isEqual:@"Gtk"])
return @"OGTK";
else if ([type isEqual:@"OFString*"])
return @"const gchar*";
// Different naming, same type
else if ([type isEqual:@"gboolean"])
return @"bool";
else if ([type isEqual:@"bool"])
return @"gboolean";
// Get the number of '*' - currently we only swap simple pointers (*)
size_t numberOfAsterisks = [self numberOfAsterisksIn:type];
if (numberOfAsterisks > 1)
return type;
OFString* strippedType = [self stripAsterisks:type];
// Gobj -> ObjC type swapping
OGTKClass* objcClassInfo =
[_gobjTypeToClassMapping objectForKey:strippedType];
if (objcClassInfo != nil) {
if ([strippedType isEqual:type])
return objcClassInfo.type;
else
return [OFString stringWithFormat:@"%@*", objcClassInfo.type];
}
// ObjC -> Gobj type swapping
OGTKClass* gobjClassInfo =
[_objcTypeToClassMapping objectForKey:strippedType];
if (gobjClassInfo != nil) {
if ([strippedType isEqual:type])
return gobjClassInfo.cType;
else
return [OFString stringWithFormat:@"%@*", gobjClassInfo.cType];
}
return type;
}
- (bool)isTypeSwappable:(OFString*)type
{
return [type isEqual:@"gchar*"] || [type isEqual:@"const gchar*"] ||
[type isEqual:@"OFString*"] || [type isEqual:@"OFArray*"] ||
[self isGobjType:type] || [self isObjcType:type];
}
- (OFString*)convertType:(OFString*)fromType
withName:(OFString*)name
toType:(OFString*)toType
{
// Try to return conversion for string types first
if (([fromType isEqual:@"gchar*"] || [fromType isEqual:@"const gchar*"]) &&
[toType isEqual:@"OFString*"]) {
return [OFString
stringWithFormat:@"[OFString stringWithUTF8String:%@]", name];
} else if ([fromType isEqual:@"OFString*"]
&& ([toType isEqual:@"gchar*"] || [toType isEqual:@"const gchar*"])) {
return [OFString stringWithFormat:@"[%@ UTF8String]", name];
}
// Then try to return generic Gobj type conversion
if ([self isGobjType:fromType] && [self isObjcType:toType]) {
// Converting from Gobjc -> Objc
return [OFString
stringWithFormat:@"[[%@ alloc] initWithGObject:(GObject*)%@]",
[self stripAsterisks:toType], name];
} else if ([self isObjcType:fromType] && [self isGobjType:toType]) {
// Converting from Objc -> Gobj
OGTKClass* toClass = [_objcTypeToClassMapping
objectForKey:[self stripAsterisks:fromType]];
return [OFString
stringWithFormat:@"[%@ %@]", name, [toClass.cName uppercaseString]];
}
// Otherwise don't do any conversion (including bool types, as ObjFW uses
// the stdc bool type)
return name;
}
- (OFString*)selfTypeMethodCall:(OFString*)type;
{
// Convert OGTKFooBar into [self FOOBAR]
if ([self isObjcType:type]) {
OGTKClass* toClass =
[_objcTypeToClassMapping objectForKey:[self stripAsterisks:type]];
return [OFString
stringWithFormat:@"[self %@]", [toClass.cName uppercaseString]];
}
// Convert GtkFooBar into GTK_FOO_BAR([self GOBJECT])
if ([self isGobjType:type]) {
OGTKClass* classInfo =
[_gobjTypeToClassMapping objectForKey:[self stripAsterisks:type]];
OFString* functionMacroName =
[[OFString stringWithFormat:@"%@_%@", classInfo.cNSSymbolPrefix,
classInfo.cSymbolPrefix] uppercaseString];
return [OFString
stringWithFormat:@"%@%@", functionMacroName, @"([self GOBJECT])"];
}
return type;
}
- (OFString*)getCTypeFromName:(OFString*)name
{
// Some shortcut definitions from libraries we do not want to add as
// dependencies
if ([name isEqual:@"Atk.Object"])
return @"AtkObject";
else if ([name isEqual:@"Gio.Application"])
return @"GApplication";
else if ([name isEqual:@"GObject.InitiallyUnowned"])
return @"GInitiallyUnowned";
else if ([name isEqual:@"GObject.Object"])
return @"GObject";
// Case: Name has a namespace prefix
if ([name containsString:@"."]) {
OFArray* nameParts = [name componentsSeparatedByString:@"."];
OGTKClass* classInfo =
[_girNameToClassMapping objectForKey:[nameParts objectAtIndex:1]];
if (classInfo != nil &&
[classInfo.cNSIdentifierPrefix isEqual:[nameParts objectAtIndex:0]])
return classInfo.cType;
}
// Case: Simple name without prefix
OGTKClass* classInfo = [_girNameToClassMapping objectForKey:name];
if (classInfo != nil)
return classInfo.cType;
// Case: We did not find any c type
@throw [OFInvalidArgumentException exception];
}
#pragma mark - Private methods - domain logic
- (OFString*)stripAsterisks:(OFString*)identifier
{
OFCharacterSet* charSet =
[OFCharacterSet characterSetWithCharactersInString:@"*"];
size_t index = [identifier indexOfCharacterFromSet:charSet];
if (index == OFNotFound)
return identifier;
return [identifier substringToIndex:index];
}
- (size_t)numberOfAsterisksIn:(OFString*)identifier
{
OFCharacterSet* charSet =
[OFCharacterSet characterSetWithCharactersInString:@"*"];
size_t index = [identifier indexOfCharacterFromSet:charSet];
if (index == OFNotFound)
return 0;
return identifier.length - index;
}
- (void)addDependenciesFromMethod:(OGTKMethod*)method to:(OGTKClass*)classInfo
{
OFString* strippedReturnType = [self stripAsterisks:method.cReturnType];
if ([self isTypeSwappable:strippedReturnType]
&& ![strippedReturnType isEqual:classInfo.cType])
[classInfo addDependency:strippedReturnType];
for (OGTKParameter* parameter in method.parameters) {
OFString* strippedParameterCType =
[self stripAsterisks:parameter.cType];
if ([self isTypeSwappable:strippedParameterCType]
&& ![strippedParameterCType isEqual:classInfo.cType])
[classInfo addDependency:strippedParameterCType];
}
}
- (void)walkDependencyTreeFrom:(OGTKClass*)classInfo
usingStack:(OFMutableDictionary*)stack
{
if (classInfo.visited) {
// OFLog(@"Class %@ aleady visited. Skipping…", classInfo.cType);
return;
}
// OFLog(@"Visiting class: %@.", classInfo.cType);
classInfo.visited = true;
// First follow parent classes - traverse to the topmost tree element
OGTKClass* parentClassInfo = nil;
if (classInfo.cParentType != nil)
parentClassInfo =
[_gobjTypeToClassMapping objectForKey:classInfo.cParentType];
if (parentClassInfo != nil &&
[stack objectForKey:classInfo.cParentType] == nil) {
[stack setObject:@"1" forKey:classInfo.cParentType];
[self walkDependencyTreeFrom:parentClassInfo usingStack:stack];
}
// OFLog(@"Checking dependencies of %@.", classInfo.cType);
// Then start to follow dependencies - leave out parent classes this time
for (OFString* dependencyGobjName in classInfo.dependsOnClasses) {
// Add a forward declaration if the dependency is within the stack -
// we're inside a circular dependency structure then
if ([stack objectForKey:dependencyGobjName] != nil
&& ![classInfo.cParentType isEqual:dependencyGobjName]) {
// OFLog(
// @"Detected circular dependency %@, adding forward declaration.",
// dependencyGobjName);
[classInfo addForwardDeclarationForClass:dependencyGobjName];
continue;
}
OGTKClass* dependencyClassInfo =
[_gobjTypeToClassMapping objectForKey:dependencyGobjName];
if (dependencyClassInfo == nil)
continue;
// We got a dependency to follow, so we are eady to visit that
// dependency and follow its dependencies
[stack setObject:@"1" forKey:dependencyGobjName];
[self walkDependencyTreeFrom:dependencyClassInfo usingStack:stack];
}
[classInfo removeForwardDeclarationsFromDependencies];
}
#pragma mark - Shortcuts for singleton access
+ (OFString*)swapTypes:(OFString*)type
{
OGTKMapper* sharedMapper = [OGTKMapper sharedMapper];
return [sharedMapper swapTypes:type];
}
+ (bool)isTypeSwappable:(OFString*)type
{
OGTKMapper* sharedMapper = [OGTKMapper sharedMapper];
return [sharedMapper isTypeSwappable:type];
}
+ (bool)isGobjType:(OFString*)type
{
OGTKMapper* sharedMapper = [OGTKMapper sharedMapper];
return [sharedMapper isGobjType:type];
}
+ (bool)isObjcType:(OFString*)type
{
OGTKMapper* sharedMapper = [OGTKMapper sharedMapper];
return [sharedMapper isObjcType:type];
}
+ (OFString*)convertType:(OFString*)fromType
withName:(OFString*)name
toType:(OFString*)toType
{
OGTKMapper* sharedMapper = [OGTKMapper sharedMapper];
return [sharedMapper convertType:fromType withName:name toType:toType];
}
+ (OFString*)selfTypeMethodCall:(OFString*)type
{
OGTKMapper* sharedMapper = [OGTKMapper sharedMapper];
return [sharedMapper selfTypeMethodCall:type];
}
+ (OFString*)getCTypeFromName:(OFString*)name
{
OGTKMapper* sharedMapper = [OGTKMapper sharedMapper];
return [sharedMapper getCTypeFromName:name];
}
@end

11
src/Generator/OGTKMethod.h

@ -26,25 +26,26 @@
*/
#import <ObjFW/ObjFW.h>
#import "OGTKParameter.h"
#import "OGTKUtil.h"
/**
* Abstracts Method operations
*/
@interface OGTKMethod : OFObject {
OFString* _cName;
OFString* _name;
OFString* _cIdentifier;
OFString* _cReturnType;
OFArray* _parameters;
bool _throws;
}
@property (copy, nonatomic) OFString* cName;
@property (readonly, nonatomic) OFString* name;
@property (copy, nonatomic) OFString* name;
@property (copy, nonatomic) OFString* cIdentifier;
@property (readonly, nonatomic) OFString* sig;
@property (copy, nonatomic) OFString* cReturnType;
@property (readonly, nonatomic) OFString* returnType;
@property (readonly, nonatomic) bool returnsVoid;
@property (copy, nonatomic) OFArray* parameters;
@property (nonatomic) bool throws;
@end

57
src/Generator/OGTKMethod.m

@ -26,13 +26,36 @@
*/
#import "OGTKMethod.h"
#import "OGTKMapper.h"
#import "OGTKUtil.h"
@implementation OGTKMethod
@synthesize cName = _cName, cReturnType = _cReturnType;
@synthesize name = _name, cIdentifier = _cIdentifier,
cReturnType = _cReturnType, parameters = _parameters,
throws = _throws;
- (instancetype)init
{
self = [super init];
_throws = false;
return self;
}
- (void)dealloc
{
[_name release];
[_cIdentifier release];
[_cReturnType release];
[_parameters release];
[super dealloc];
}
- (OFString*)name
{
return [OGTKUtil convertUSSToCamelCase:[OGTKUtil trimMethodName:_cName]];
return [OGTKUtil convertUSSToCamelCase:_name];
}
- (OFString*)sig
@ -58,10 +81,11 @@
if (first) {
first = false;
[output appendFormat:@"%@:(%@)%@",
[OGTKUtil convertUSSToCapCase:p.name], p.type, p.name];
[OGTKUtil convertUSSToCapCase:p.name], p.type, p.name];
} else {
[output appendFormat:@" %@:(%@)%@",
[OGTKUtil convertUSSToCamelCase:p.name], p.type, p.name];
[OGTKUtil convertUSSToCamelCase:p.name], p.type,
p.name];
}
}
@ -71,7 +95,7 @@
- (OFString*)returnType
{
return [OGTKUtil swapTypes:_cReturnType];
return [OGTKMapper swapTypes:_cReturnType];
}
- (bool)returnsVoid
@ -83,18 +107,8 @@
{
OFMutableArray* mutParams = [[params mutableCopy] autorelease];
// Hacky fix to get around issue with missing GError parameter from GIR file
if ([_cName isEqual:@"gtk_window_set_icon_from_file"] ||
[_cName isEqual:@"gtk_window_set_default_icon_from_file"] ||
[_cName isEqual:@"gtk_builder_add_from_file"] ||
[_cName isEqual:@"gtk_builder_add_from_resource"] ||
[_cName isEqual:@"gtk_builder_add_from_string"] ||
[_cName isEqual:@"gtk_builder_add_objects_from_file"] ||
[_cName isEqual:@"gtk_builder_add_objects_from_resource"] ||
[_cName isEqual:@"gtk_builder_add_objects_from_string"] ||
[_cName isEqual:@"gtk_builder_extend_with_template"] ||
[_cName isEqual:@"gtk_builder_value_from_string"] ||
[_cName isEqual:@"gtk_builder_value_from_string_type"]) {
// TODO: Replace this by an OFException implemention within the writer
if (_throws) {
OGTKParameter* param = [[[OGTKParameter alloc] init] autorelease];
param.cType = @"GError**";
param.cName = @"err";
@ -111,13 +125,4 @@
return [[_parameters copy] autorelease];
}
- (void)dealloc
{
[_cName release];
[_cReturnType release];
[_parameters release];
[super dealloc];
}
@end

2
src/Generator/OGTKParameter.h

@ -27,7 +27,7 @@
#import <ObjFW/ObjFW.h>
#import "OGTKUtil.h"
#import "OGTKMapper.h"
/**
* Abstracts Parameter operations

3
src/Generator/OGTKParameter.m

@ -26,6 +26,7 @@
*/
#import "OGTKParameter.h"
#import "OGTKUtil.h"
/**