ObjFW  Check-in [57fb5578cc]

Overview
Comment:Remove forwarding methods. See long commit message for details.

There are two resons for removing it:

First, OFPlugin does not need forwarding anymore. Second is that
forwarding is broken in both, the GNU and the Apple runtime.

In GNU libobjc, objc_msg_sendv is implemented using __builtin_apply,
which is broken on many platforms, including x86_64. If forwarding is
used, the application will just crash. To work around that, I'd need to
parse the type encoding and use libffi to call the method instead of
using objc_msg_sendv.

Now the Apple runtime has a similar problem: There is no objc_msgSendv
for PPC64 and x86_64 as it's only in ObjC1 and on ARM (iPhone), it's
broken (most likely because the iPhone uses only ObjC2 - I was confused
that objc_msgSendv was even in the libobjc there). So I'd need to write
an ASM implementation for these 3.

Writing those 3 ASM implementations (or 5, so we don't depend on ObjC1
stuff on PPC32 and x86 as well) wouldn't be a problem, but there is a
problem the GNU libobjc and the Apple runtime got in common, which
originates from the early ObjC implementations:

forward:: and performv:: were only designed to return scalar types. But
today, it's possible to return floats, structs and unions as well. What
Apple and GNU use here is a very hacky workaround and it's just luck
that it works. forward:: and performv:: both return an id (Apple) or
void* (GNU). forward:: is called by the runtime if you called a method
that is not implemented. The compiler does not know at compile time
that it is not implemented, therefore expects a float as a return. On
x86, floats are returned in sp0. The runtime now notices that the
called method is not implemented and calls forward::. Forward then
calls performv:: to call the right method. The method returns a float
and stores it in sp0. Remember that both, forward:: and performv::
return an id / void*. performv:: returns now and after that, forward::
returns. The return of those was always put into eax, as that's how
scalar values are returned on x86. The original caller of the method
does not expect any return value in eax, but in sp0. This works, as
no code touched sp0. However, you can not rely on sp0 not being
touched. It's just luck that the compiler generates code that does not
touch sp0.

While this works for forwarding due to the ABI on x86 (and the ABIs on
many other platforms allow this hack as well), this fails if you call
performv:: directly on a method returning a float. In this case, the
compiler does not expect a return value in sp0, but in eax, as
performv:: is expected to return id / void*. Therefore the bogus value
in eax will be casted to float and the result will be useless.

This is why I decided to remove forwarding and performv:: from libobjfw
for now. If I encounter a situation where I need forwarding, I'm going
to implement it in a sane way and NOT the objc way. The forwarding
methods this commit removes did it the objc way, which is IMO just
wrong. (That way was ok back then when you only had scalar return
types, but today you're not limited to scalar return types anymore.)

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 57fb5578ccbf50db7566a37efd04b4026f9a9055afdb2080be0cd3c2ace6434b
User & Date: js on 2009-04-19 17:37:52
Other Links: manifest | tags
Context
2009-04-19
18:13
Minor cleanups. check-in: 54850dda81 user: js tags: trunk
17:37
Remove forwarding methods. See long commit message for details. check-in: 57fb5578cc user: js tags: trunk
17:06
Don't use forwarding for OFPlugin. check-in: 179174571e user: js tags: trunk
Changes

Modified src/OFObject.h from [e051be24f6] to [9fc0378e5d].

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
/**
 * \param selector The selector for which the method should be returned
 *
 * \return The implementation for the specified selector
 */
- (IMP)methodFor: (SEL)selector;

/**
 * This method is called when a method was called which isn't implemented.
 * It's possible to override it so the method call cann be forwarded to another
 * object.
 *
 * \param selector The selector which was called
 * \param args The arguments with which the selector was called
 * \return The return value of the call
 */
#ifdef __objc_INCLUDE_GNU
- (retval_t)forward: (SEL)selector
		   : (arglist_t)args;
#else
- (id)forward: (SEL)selector
	     : (marg_list)args;
#endif

/**
 * Perform the given selector with the given arguments.
 *
 * \param selector The selector to perform
 * \param args The arguments with which the selector is performed
 * \return The return value of the performed selector
 */
#ifdef __objc_INCLUDE_GNU
- (retval_t)performv: (SEL)selector
		    : (arglist_t)args;
#else
- performv: (SEL)selector
	  : (marg_list)args;
#endif

/**
 * Compare two objects.
 * Classes containing data (like strings, arrays, lists etc.) should reimplement
 * this!
 *
 * \param obj The object which is tested for equality
 * \return A boolean whether the object is equal to the other object







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







98
99
100
101
102
103
104
































105
106
107
108
109
110
111
/**
 * \param selector The selector for which the method should be returned
 *
 * \return The implementation for the specified selector
 */
- (IMP)methodFor: (SEL)selector;

































/**
 * Compare two objects.
 * Classes containing data (like strings, arrays, lists etc.) should reimplement
 * this!
 *
 * \param obj The object which is tested for equality
 * \return A boolean whether the object is equal to the other object

Modified src/OFObject.m from [7381da9158] to [a0cbb15bb4].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of libobjfw. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */

/*
 * Get rid of the stupid warnings until we have our own implementation for
 * objc_msgSendv.
 */
#define OBJC2_UNAVAILABLE

#import "config.h"

#include <stdlib.h>
#include <string.h>
#include <limits.h>

#import "OFObject.h"











<
<
<
<
<
<







1
2
3
4
5
6
7
8
9
10
11






12
13
14
15
16
17
18
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of libobjfw. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */







#import "config.h"

#include <stdlib.h>
#include <string.h>
#include <limits.h>

#import "OFObject.h"
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
	else
		return method_get_imp(class_get_class_method(isa, selector));
#else
	return class_getMethodImplementation(isa, selector);
#endif
}

#ifdef __objc_INCLUDE_GNU
- (retval_t)forward: (SEL)selector
		   : (arglist_t)args
#else
- (id)forward: (SEL)selector
	     : (marg_list)args
#endif
{
	@throw [OFNotImplementedException newWithClass: [self class]
					   andSelector: _cmd];
	return self;
}

#ifdef __objc_INCLUDE_GNU
- (retval_t)performv: (SEL)selector
		    : (arglist_t)args
{
	return objc_msg_sendv(self, selector, args);
}
#else
- performv: (SEL)selector
	  : (marg_list)args
{
	Method m;
	char *encoding, rettype;
	size_t depth, argsize;

	if ((m = class_getInstanceMethod(isa, selector)) == NULL ||
	    (encoding = (char*)method_getTypeEncoding(m)) == NULL)
		@throw [OFInvalidArgumentException newWithClass: [self class]
						    andSelector: _cmd];

	rettype = *encoding;

	/* Skip the return type */
	switch (*encoding) {
	case '{':
		for (depth = 0; *encoding; encoding++) {
			if (OF_UNLIKELY(*encoding == '{'))
				depth++;
			else if (OF_UNLIKELY(*encoding == '}') &&
			    OF_LIKELY(!--depth))
				break;
		}
		break;
	case '(':
		for (depth = 0; *encoding; encoding++) {
			if (OF_UNLIKELY(*encoding == '('))
				depth++;
			else if (OF_UNLIKELY(*encoding == ')') &&
			    OF_LIKELY(!--depth))
				break;
		}
		break;
	}
	encoding++;

	for (argsize = 0; *encoding >= '0' && *encoding <= '9'; encoding++)
		argsize = argsize * 10 + (*encoding - '0');

	/* We don't support returning structs or unions yet */
	if (rettype == '{' || rettype == '(')
		@throw [OFNotImplementedException newWithClass: [self class]
						   andSelector: _cmd];

#if __OBJC2__
	@throw [OFNotImplementedException newWithClass: [self class]
					   andSelector: _cmd];
	return self;
#else
	return objc_msgSendv(self, selector, argsize, args);
#endif
}
#endif

- (BOOL)isEqual: (id)obj
{
	/* Classes containing data should reimplement this! */
	return (self == obj ? YES : NO);
}

- (uint32_t)hash







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







177
178
179
180
181
182
183











































































184
185
186
187
188
189
190
	else
		return method_get_imp(class_get_class_method(isa, selector));
#else
	return class_getMethodImplementation(isa, selector);
#endif
}












































































- (BOOL)isEqual: (id)obj
{
	/* Classes containing data should reimplement this! */
	return (self == obj ? YES : NO);
}

- (uint32_t)hash