ObjFW  Check-in [a74bff96c4]

Overview
Comment:OFPlugin: Completely redesign API
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a74bff96c4dae6909ee0760190bde62ac4b48edda47fec263dbdd608ae4f1051
User & Date: js on 2022-06-17 17:12:52
Other Links: manifest | tags
Context
2022-06-17
20:04
Make GCC happy again check-in: b29bfe6485 user: js tags: trunk
17:12
OFPlugin: Completely redesign API check-in: a74bff96c4 user: js tags: trunk
14:36
Better workaround for Clang bug on Windows check-in: 658caa441b user: js tags: trunk
Changes

Modified src/OFPlugin.h from [9b033eeb96] to [4badab4fb7].

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57



58
59
60
61
62


63
64


65


66

67




68

69
70
71
72







73
74
75
#import "OFObject.h"

@class OFString;

#ifndef OF_WINDOWS
# include <dlfcn.h>
typedef void *OFPluginHandle;

typedef enum {
	OFDLOpenFlagLazy = RTLD_LAZY,
	OFDLOpenFlagNow  = RTLD_NOW
} OFDLOpenFlags;
#else
# include <windows.h>
typedef HMODULE OFPluginHandle;

typedef enum {
	OFDLOpenFlagLazy = 0,
	OFDLOpenFlagNow  = 0
} OFDLOpenFlags;
#endif

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFPlugin OFPlugin.h ObjFW/OFPlugin.h
 *
 * @brief Provides a system for loading plugins at runtime.
 *
 * A plugin must subclass @ref OFPlugin and have a global function called
 * `OFPluginInit`, which returns an instance of the @ref OFPlugin subclass and
 * takes no parameters.
 */

@interface OFPlugin: OFObject
{
	OFPluginHandle _pluginHandle;
	OF_RESERVE_IVARS(OFPlugin, 4)
}

/**
 * @brief Loads a plugin from a file.
 *



 * @param path Path to the plugin file. The suffix is appended automatically.
 * @return The loaded plugin
 */
+ (OF_KINDOF(OFPlugin *))pluginWithPath: (OFString *)path;
@end



#ifdef __cplusplus


extern "C" {


#endif

extern OFPluginHandle OFDLOpen(OFString *path, OFDLOpenFlags flags);




extern void *OFDLSym(OFPluginHandle handle, const char *symbol);

extern OFString *_Nullable OFDLError(void);
extern void OFDLClose(OFPluginHandle handle);
#ifdef __cplusplus
}







#endif

OF_ASSUME_NONNULL_END







<
<
<
<
<



<
<
<
<
<







|

<
<
<

>


|
<



|

>
>
>
|
|

|
|
>
>
|
<
>
>
|
>
>
|
>
|
>
>
>
>
|
>
|
<
<
|
>
>
>
>
>
>
>
|


16
17
18
19
20
21
22





23
24
25





26
27
28
29
30
31
32
33
34



35
36
37
38
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70


71
72
73
74
75
76
77
78
79
80
81
#import "OFObject.h"

@class OFString;

#ifndef OF_WINDOWS
# include <dlfcn.h>
typedef void *OFPluginHandle;





#else
# include <windows.h>
typedef HMODULE OFPluginHandle;





#endif

OF_ASSUME_NONNULL_BEGIN

/**
 * @class OFPlugin OFPlugin.h ObjFW/OFPlugin.h
 *
 * @brief A class representing a loaded plugin (shared library).
 *



 */
OF_SUBCLASSING_RESTRICTED
@interface OFPlugin: OFObject
{
	OFPluginHandle _handle;

}

/**
 * @brief Returns the plugin path for a plugin with the specified name.
 *
 * E.g. on ELF systems, it appends .so, while on macOS and iOS, it creates the
 * appropriate plugin path. This can also be prefixed by a directory.
 *
 * @param name The name to return the plugin path for
 * @return The plugin path
 */
+ (OFString *)pathForName: (OFString *)name;

/**
 * @brief Creates a new OFPlugin by loading the plugin with the specified path.
 *

 * @param path The path to the plugin file. The suffix is appended
 *	       automatically.
 * @return An new, autoreleased OFPlugin
 */
+ (instancetype)pluginWithPath: (OFString *)path;

/**
 * @brief Initializes an already allocated OFPlugin by loading the plugin with
 *	  the specified path.
 *
 * @param path The path to the plugin file. The suffix is appended
 *	       automatically.
 * @return An initialized OFPlugin
 */
- (instancetype)initWithPath: (OFString *)path;



/**
 * @brief Returns the address for the specified symbol, or `nil` if not found.
 *
 * @param symbol The symbol to return the address for
 * @return The address for the speccified symbol, or `nil` if not found
 */
- (nullable void *)addressForSymbol: (OFString *)symbol;
@end

OF_ASSUME_NONNULL_END

Modified src/OFPlugin.m from [e14701ced1] to [8ad69b7ef8].

26
27
28
29
30
31
32
33


34
35
36
37
38
39

40
41
42
43
44



45

46
47
48
49
50
51





52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78


79


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131



132





133
134
135
136

137



138
139
140
141
142
143
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFLoadPluginFailedException.h"

typedef OFPlugin *(*PluginInit)(void);



OFPluginHandle
OFDLOpen(OFString *path, OFDLOpenFlags flags)
{
#ifndef OF_WINDOWS
	return dlopen([path cStringWithEncoding: [OFLocale encoding]], flags);

#else
	if (path == nil)
		return GetModuleHandle(NULL);

	if ([OFSystemInfo isWindowsNT])



		return LoadLibraryW(path.UTF16String);

	else
		return LoadLibraryA(
		    [path cStringWithEncoding: [OFLocale encoding]]);
#endif
}






void *
OFDLSym(OFPluginHandle handle, const char *symbol)
{
#ifndef OF_WINDOWS
	return dlsym(handle, symbol);
#else
	return (void *)(uintptr_t)GetProcAddress(handle, symbol);
#endif
}

void
OFDLClose(OFPluginHandle handle)
{
#ifndef OF_WINDOWS
	dlclose(handle);
#else
	FreeLibrary(handle);
#endif
}

OFString *
OFDLError(void)
{
#ifndef OF_WINDOWS
	return [OFString stringWithCString: dlerror()
				  encoding: [OFLocale encoding]];
#else


	return nil;


#endif
}

@implementation OFPlugin
+ (id)pluginWithPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFPluginHandle handle;
	PluginInit initPlugin;
	OFPlugin *plugin;

#if defined(OF_MACOS)
	path = [path stringByAppendingFormat: @".bundle/Contents/MacOS/%@",
					      path.lastPathComponent];
#elif defined(OF_IOS)
	path = [path stringByAppendingFormat: @".bundle/%@",
					      path.lastPathComponent];
#else
	path = [path stringByAppendingString: @PLUGIN_SUFFIX];
#endif

	if ((handle = OFDLOpen(path, OFDLOpenFlagLazy)) == NULL)
		@throw [OFLoadPluginFailedException
		    exceptionWithPath: path
				error: OFDLError()];

	objc_autoreleasePoolPop(pool);

	initPlugin = (PluginInit)(uintptr_t)OFDLSym(handle, "OFPluginInit");
	if (initPlugin == (PluginInit)0 || (plugin = initPlugin()) == nil) {
		OFDLClose(handle);
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
	}

	plugin->_pluginHandle = handle;
	return plugin;
}

- (instancetype)init
{
	if ([self isMemberOfClass: [OFPlugin class]]) {
		@try {
			[self doesNotRecognizeSelector: _cmd];
		} @catch (id e) {
			[self release];
			@throw e;
		}

		abort();
	}




	return [super init];





}

- (void)dealloc
{

	OFPluginHandle h = _pluginHandle;




	[super dealloc];

	OFDLClose(h);
}
@end







|
>
>

|
|

<
|
>
|
|
<
|
|
>
>
>
|
>
|
|
<



>
>
>
>
>
|
<
|

<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
|

>
>
|
>
>

|
|
|
|
<
<
|
<
<
|
<
<
<
<
<
<

|

<
<
|
|
|
|
<

<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
|
|
|
|

|
|

>
>
>
|
>
>
>
>
>




>
|
>
>
>


|
<
<

26
27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43

44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61

62
63










64










65
66
67
68
69
70
71
72
73
74
75
76


77


78






79
80
81


82
83
84
85

86






87









88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116


117
#import "OFLocale.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFLoadPluginFailedException.h"

#ifndef RTLD_LAZY
# define RTLD_LAZY 0
#endif

@implementation OFPlugin
+ (instancetype)pluginWithPath: (OFString *)path
{

	return [[[self alloc] initWithPath: path] autorelease];
}

+ (OFString *)pathForName: (OFString *)name

{
#if defined(OF_MACOS)
	return [name stringByAppendingFormat: @".bundle/Contents/MacOS/%@",
					      name.lastPathComponent];
#elif defined(OF_IOS)
	return [name stringByAppendingFormat: @".bundle/%@",
					      name.lastPathComponent];
#else
	return [name stringByAppendingString: @PLUGIN_SUFFIX];

#endif
}

- (instancetype)initWithPath: (OFString *)path
{
	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();


#ifndef OF_WINDOWS










		_handle = dlopen(










		    [path cStringWithEncoding: [OFLocale encoding]], RTLD_LAZY);
#else
		if ([OFSystemInfo isWindowsNT])
			_handle = LoadLibraryW(path.UTF16String);
		else
			_handle LoadLibraryA(
			    [path cStringWithEncoding: [OFLocale encoding]]);
#endif

		if (_handle == NULL) {
#ifndef OF_WINDOWS
			OFString *error = [OFString


			    stringWithCString: dlerror()


				     encoding: [OFLocale encoding]];






#else
			OFString *error = nil;
#endif


			@throw [OFLoadPluginFailedException
			    exceptionWithPath: path
					error: error];
		}








		objc_autoreleasePoolPop(pool);









	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void *)addressForSymbol: (OFString *)symbol
{
#ifndef OF_WINDOWS
	return dlsym(_handle,
	    [symbol cStringWithEncoding: [OFLocale encoding]]);
#else
	return (void *)(uintptr_t)GetProcAddress(_handle,
	    [symbol cStringWithEncoding: [OFLocale encoding]]);
#endif
}

- (void)dealloc
{
#ifndef OF_WINDOWS
	dlclose(_handle);
#else
	FreeLibrary(_handle);
#endif

	[super dealloc];
}


@end

Modified tests/OFPluginTests.m from [cebf7a6561] to [d69a23db27].

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33



34
35


36
37


38


39



40
41
42
43
#include "config.h"

#import "TestsAppDelegate.h"

#import "plugin/TestPlugin.h"

#ifndef OF_IOS
static OFString *const pluginPath = @"plugin/TestPlugin";
#else
static OFString *const pluginPath = @"PlugIns/TestPlugin";
#endif

static OFString *const module = @"OFPlugin";

@implementation TestsAppDelegate (OFPluginTests)
- (void)pluginTests
{
	void *pool = objc_autoreleasePoolPush();



	TestPlugin *plugin;



	TEST(@"+[pluginWithPath:]",
	    (plugin = [OFPlugin pluginWithPath: pluginPath]))





	TEST(@"TestPlugin's -[test:]", [plugin test: 1234] == 2468)




	objc_autoreleasePoolPop(pool);
}
@end







|

|








>
>
>
|

>
>
|
|
>
>

>
>
|
>
>
>




16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "config.h"

#import "TestsAppDelegate.h"

#import "plugin/TestPlugin.h"

#ifndef OF_IOS
static OFString *const pluginName = @"plugin/TestPlugin";
#else
static OFString *const pluginName = @"PlugIns/TestPlugin";
#endif

static OFString *const module = @"OFPlugin";

@implementation TestsAppDelegate (OFPluginTests)
- (void)pluginTests
{
	void *pool = objc_autoreleasePoolPush();
	OFString *path;
	OFPlugin *plugin;
	Class (*class)(void);
	TestPlugin *test;

	TEST(@"+[pathForName:]", (path = [OFPlugin pathForName: pluginName]))

	TEST(@"+[pluginWithPath:]", (plugin = [OFPlugin pluginWithPath: path]))

	TEST(@"-[addressForSymbol:]",
	    (class = (Class (*)(void))[plugin addressForSymbol: @"class"]))

	test = [[class() alloc] init];
	@try {
		TEST(@"TestPlugin's -[test:]", [test test: 1234] == 2468)
	} @finally {
		[test release];
	}

	objc_autoreleasePoolPop(pool);
}
@end

Modified tests/plugin/TestPlugin.h from [52670dca56] to [2f9aa9b796].

9
10
11
12
13
14
15
16
17
18
19
20
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#import "OFPlugin.h"

@interface TestPlugin: OFPlugin
- (int)test: (int)num;
@end







|

|


9
10
11
12
13
14
15
16
17
18
19
20
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#import "OFObject.h"

@interface TestPlugin: OFObject
- (int)test: (int)num;
@end

Modified tests/plugin/TestPlugin.m from [b5310ee637] to [5616dbe160].

40
41
42
43
44
45
46
47

48
49
50
51
@implementation TestPlugin
- (int)test: (int)num
{
	return num * 2;
}
@end

id

OFPluginInit(void)
{
	return [[[TestPlugin alloc] init] autorelease];
}







<
>
|

|

40
41
42
43
44
45
46

47
48
49
50
51
@implementation TestPlugin
- (int)test: (int)num
{
	return num * 2;
}
@end


Class
class(void)
{
	return [TestPlugin class];
}