1 |
/* |
2 |
* layout.c - Type and object layout algorithms. |
3 |
* |
4 |
* Copyright (C) 2001 Southern Storm Software, Pty Ltd. |
5 |
* |
6 |
* This program is free software; you can redistribute it and/or modify |
7 |
* it under the terms of the GNU General Public License as published by |
8 |
* the Free Software Foundation; either version 2 of the License, or |
9 |
* (at your option) any later version. |
10 |
* |
11 |
* This program is distributed in the hope that it will be useful, |
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU General Public License |
17 |
* along with this program; if not, write to the Free Software |
18 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
*/ |
20 |
|
21 |
#include "engine_private.h" |
22 |
#include "il_opcodes.h" |
23 |
#if defined(HAVE_LIBFFI) |
24 |
#include "ffi.h" |
25 |
#else |
26 |
#include "il_align.h" |
27 |
#endif |
28 |
|
29 |
#ifdef __cplusplus |
30 |
extern "C" { |
31 |
#endif |
32 |
|
33 |
/* |
34 |
* Information that results from laying out a type. |
35 |
*/ |
36 |
typedef struct |
37 |
{ |
38 |
ILUInt32 size; |
39 |
ILUInt32 alignment; |
40 |
ILUInt32 nativeSize; |
41 |
ILUInt32 nativeAlignment; |
42 |
ILUInt32 vtableSize; |
43 |
ILMethod **vtable; |
44 |
ILUInt32 staticSize; |
45 |
int hasFinalizer; |
46 |
int managedInstance; |
47 |
int managedStatic; |
48 |
|
49 |
} LayoutInfo; |
50 |
|
51 |
/* |
52 |
* Forward declaration. |
53 |
*/ |
54 |
static int LayoutClass(ILExecProcess *process, ILClass *info, LayoutInfo *layout); |
55 |
|
56 |
/* |
57 |
* Get the layout information for a type. Returns zero |
58 |
* if there is something wrong with the type. |
59 |
*/ |
60 |
static int LayoutType(ILExecProcess *process, ILType *type, LayoutInfo *layout) |
61 |
{ |
62 |
type = ILTypeStripPrefixes(type); |
63 |
if(ILType_IsPrimitive(type)) |
64 |
{ |
65 |
/* Lay out a primitive type */ |
66 |
layout->managedInstance = 0; |
67 |
switch(ILType_ToElement(type)) |
68 |
{ |
69 |
case IL_META_ELEMTYPE_BOOLEAN: |
70 |
case IL_META_ELEMTYPE_I1: |
71 |
case IL_META_ELEMTYPE_U1: |
72 |
{ |
73 |
layout->size = 1; |
74 |
layout->alignment = 1; |
75 |
} |
76 |
break; |
77 |
|
78 |
case IL_META_ELEMTYPE_CHAR: |
79 |
case IL_META_ELEMTYPE_I2: |
80 |
case IL_META_ELEMTYPE_U2: |
81 |
{ |
82 |
layout->size = 2; |
83 |
layout->alignment = 2; |
84 |
} |
85 |
break; |
86 |
|
87 |
case IL_META_ELEMTYPE_I4: |
88 |
case IL_META_ELEMTYPE_U4: |
89 |
#ifdef IL_NATIVE_INT32 |
90 |
case IL_META_ELEMTYPE_I: |
91 |
case IL_META_ELEMTYPE_U: |
92 |
#endif |
93 |
{ |
94 |
layout->size = 4; |
95 |
layout->alignment = 4; |
96 |
} |
97 |
break; |
98 |
|
99 |
case IL_META_ELEMTYPE_I8: |
100 |
case IL_META_ELEMTYPE_U8: |
101 |
#ifdef IL_NATIVE_INT64 |
102 |
case IL_META_ELEMTYPE_I: |
103 |
case IL_META_ELEMTYPE_U: |
104 |
#endif |
105 |
{ |
106 |
#if defined(HAVE_LIBFFI) |
107 |
layout->size = ffi_type_uint64.size; |
108 |
layout->alignment = ffi_type_uint64.alignment; |
109 |
#else |
110 |
layout->size = sizeof(ILInt64); |
111 |
layout->alignment = _IL_ALIGN_FOR_TYPE(long); |
112 |
#endif |
113 |
} |
114 |
break; |
115 |
|
116 |
case IL_META_ELEMTYPE_R4: |
117 |
{ |
118 |
#if defined(HAVE_LIBFFI) |
119 |
layout->size = ffi_type_float.size; |
120 |
layout->alignment = ffi_type_float.alignment; |
121 |
#else |
122 |
layout->size = sizeof(ILFloat); |
123 |
layout->alignment = _IL_ALIGN_FOR_TYPE(float); |
124 |
#endif |
125 |
} |
126 |
break; |
127 |
|
128 |
case IL_META_ELEMTYPE_R8: |
129 |
{ |
130 |
#if defined(HAVE_LIBFFI) |
131 |
layout->size = ffi_type_double.size; |
132 |
layout->alignment = ffi_type_double.alignment; |
133 |
#else |
134 |
layout->size = sizeof(ILDouble); |
135 |
layout->alignment = _IL_ALIGN_FOR_TYPE(double); |
136 |
#endif |
137 |
} |
138 |
break; |
139 |
|
140 |
case IL_META_ELEMTYPE_R: |
141 |
{ |
142 |
#if defined(HAVE_LIBFFI) |
143 |
#ifdef IL_NATIVE_FLOAT |
144 |
layout->size = ffi_type_longdouble.size; |
145 |
layout->alignment = ffi_type_longdouble.alignment; |
146 |
#else |
147 |
layout->size = ffi_type_double.size; |
148 |
layout->alignment = ffi_type_double.alignment; |
149 |
#endif |
150 |
#else |
151 |
layout->size = sizeof(ILNativeFloat); |
152 |
layout->alignment = _IL_ALIGN_FOR_TYPE(long_double); |
153 |
#endif |
154 |
} |
155 |
break; |
156 |
|
157 |
case IL_META_ELEMTYPE_TYPEDBYREF: |
158 |
{ |
159 |
#if defined(HAVE_LIBFFI) |
160 |
layout->size = sizeof(ILTypedRef); |
161 |
layout->alignment = ffi_type_pointer.alignment; |
162 |
#else |
163 |
layout->size = sizeof(ILTypedRef); |
164 |
layout->alignment = _IL_ALIGN_FOR_TYPE(void_p); |
165 |
#endif |
166 |
layout->managedInstance = 1; |
167 |
} |
168 |
break; |
169 |
|
170 |
default: return 0; |
171 |
} |
172 |
layout->nativeSize = layout->size; |
173 |
layout->nativeAlignment = layout->alignment; |
174 |
layout->vtableSize = 0; |
175 |
layout->vtable = 0; |
176 |
layout->staticSize = 0; |
177 |
layout->hasFinalizer = 0; |
178 |
layout->managedStatic = 0; |
179 |
return 1; |
180 |
} |
181 |
else if(ILType_IsValueType(type)) |
182 |
{ |
183 |
/* Lay out a value type by getting the full size and alignment |
184 |
of the class that underlies the value type */ |
185 |
ILClass *classInfo = ILClassResolve(ILType_ToValueType(type)); |
186 |
ILType *synType = ILClassGetSynType(classInfo); |
187 |
if(synType == 0) |
188 |
{ |
189 |
return LayoutClass(process, classInfo, layout); |
190 |
} |
191 |
else |
192 |
{ |
193 |
return LayoutType(process, ILTypeStripPrefixes(synType), layout); |
194 |
} |
195 |
} |
196 |
else |
197 |
{ |
198 |
/* Everything else is laid out as a pointer */ |
199 |
#if defined(HAVE_LIBFFI) |
200 |
layout->size = ffi_type_pointer.size; |
201 |
layout->alignment = ffi_type_pointer.alignment; |
202 |
#else |
203 |
layout->size = sizeof(void *); |
204 |
layout->alignment = _IL_ALIGN_FOR_TYPE(void_p); |
205 |
#endif |
206 |
layout->nativeSize = layout->size; |
207 |
layout->nativeAlignment = layout->alignment; |
208 |
layout->vtableSize = 0; |
209 |
layout->vtable = 0; |
210 |
layout->staticSize = 0; |
211 |
layout->hasFinalizer = 0; |
212 |
layout->managedInstance = ILTypeIsReference(ILTypeStripPrefixes(type)); |
213 |
layout->managedStatic = 0; |
214 |
return 1; |
215 |
} |
216 |
} |
217 |
|
218 |
/* |
219 |
* Find the virtual ancestor for a method. |
220 |
* Returns NULL if no ancestor found. |
221 |
*/ |
222 |
static ILMethod *FindVirtualAncestor(ILClass *scope, ILClass *info, |
223 |
ILMethod *testMethod) |
224 |
{ |
225 |
ILMethod *method; |
226 |
while(info != 0) |
227 |
{ |
228 |
method = 0; |
229 |
while((method = (ILMethod *)ILClassNextMemberByKind |
230 |
(info, (ILMember *)method, IL_META_MEMBERKIND_METHOD)) != 0) |
231 |
{ |
232 |
if((method->member.attributes & IL_META_METHODDEF_VIRTUAL) != 0 && |
233 |
!strcmp(method->member.name, testMethod->member.name) && |
234 |
ILTypeIdentical(method->member.signature, |
235 |
testMethod->member.signature)) |
236 |
{ |
237 |
/* If the ancestor is not accessible from the original class, |
238 |
then allocate a new vtable slot for the method */ |
239 |
if(ILMemberAccessible(&(method->member), scope)) |
240 |
{ |
241 |
return method; |
242 |
} |
243 |
else |
244 |
{ |
245 |
return 0; |
246 |
} |
247 |
} |
248 |
} |
249 |
info = ILClassGetParent(info); |
250 |
} |
251 |
return 0; |
252 |
} |
253 |
|
254 |
/* |
255 |
* Compute the method table for a class's interface. |
256 |
*/ |
257 |
static int ComputeInterfaceTable(ILClass *info, ILClass *interface) |
258 |
{ |
259 |
ILClass *parent; |
260 |
ILImplPrivate *impl; |
261 |
ILImplPrivate *impl2; |
262 |
ILUInt32 size; |
263 |
ILUInt16 *table; |
264 |
ILUInt32 slot; |
265 |
ILMethod *method; |
266 |
ILMethod *method2; |
267 |
ILOverride *over; |
268 |
ILImplements *implBlock; |
269 |
|
270 |
/* Determine if we already have an implementation of this interface. |
271 |
This may happen if we reach the same interface via two different |
272 |
paths in the interface inheritance tree */ |
273 |
impl = ((ILClassPrivate *)(info->userData))->implements; |
274 |
while(impl != 0) |
275 |
{ |
276 |
if(impl->interface == interface) |
277 |
{ |
278 |
return 1; |
279 |
} |
280 |
impl = impl->next; |
281 |
} |
282 |
|
283 |
/* Create a new interface method table for the class */ |
284 |
size = ((ILClassPrivate *)(interface->userData))->vtableSize; |
285 |
impl = (ILImplPrivate *)ILMemStackAllocItem |
286 |
(&(info->programItem.image->memStack), |
287 |
sizeof(ILImplPrivate) + size * sizeof(ILUInt16)); |
288 |
if(!impl) |
289 |
{ |
290 |
return 0; |
291 |
} |
292 |
table = ILImplPrivate_Table(impl); |
293 |
impl->interface = interface; |
294 |
impl->next = ((ILClassPrivate *)(info->userData))->implements; |
295 |
((ILClassPrivate *)(info->userData))->implements = impl; |
296 |
|
297 |
/* Determine if the parent class implements the interface */ |
298 |
parent = info; |
299 |
impl2 = 0; |
300 |
while(impl2 == 0 && (parent = ILClassGetParent(parent)) != 0) |
301 |
{ |
302 |
impl2 = ((ILClassPrivate *)(parent->userData))->implements; |
303 |
while(impl2 != 0) |
304 |
{ |
305 |
if(impl2->interface == interface) |
306 |
{ |
307 |
break; |
308 |
} |
309 |
impl2 = impl2->next; |
310 |
} |
311 |
} |
312 |
|
313 |
/* Copy the parent's interface method table, or mark all slots abstract */ |
314 |
if(impl2) |
315 |
{ |
316 |
ILMemCpy(table, ILImplPrivate_Table(impl2), size * sizeof(ILUInt16)); |
317 |
} |
318 |
else |
319 |
{ |
320 |
ILMemSet(table, 0xFF, size * sizeof(ILUInt16)); |
321 |
} |
322 |
|
323 |
/* Fill the interface table slots */ |
324 |
for(slot = 0; slot < size; ++slot) |
325 |
{ |
326 |
/* Get the method for this slot */ |
327 |
method = ((ILClassPrivate *)(interface->userData))->vtable[slot]; |
328 |
if(!method) |
329 |
{ |
330 |
continue; |
331 |
} |
332 |
|
333 |
/* Search for a method in the current class with a matching name */ |
334 |
method2 = 0; |
335 |
while((method2 = (ILMethod *)ILClassNextMemberByKind |
336 |
(info, (ILMember *)method2, |
337 |
IL_META_MEMBERKIND_METHOD)) != 0) |
338 |
{ |
339 |
if((method2->member.attributes & |
340 |
(IL_META_METHODDEF_MEMBER_ACCESS_MASK | |
341 |
IL_META_METHODDEF_VIRTUAL | |
342 |
IL_META_METHODDEF_NEW_SLOT)) == |
343 |
(IL_META_METHODDEF_PUBLIC | |
344 |
IL_META_METHODDEF_VIRTUAL | |
345 |
IL_META_METHODDEF_NEW_SLOT)) |
346 |
{ |
347 |
if(strcmp(method2->member.name, method->member.name) != 0 || |
348 |
!ILTypeIdentical(method2->member.signature, |
349 |
method->member.signature)) |
350 |
{ |
351 |
continue; |
352 |
} |
353 |
table[slot] = (ILUInt16)(method2->index); |
354 |
break; |
355 |
} |
356 |
} |
357 |
|
358 |
/* If the slot is still empty, then search the class |
359 |
hierarchy for a virtual method match */ |
360 |
if(table[slot] == (ILUInt16)0xFFFF) |
361 |
{ |
362 |
parent = info; |
363 |
while(parent != 0) |
364 |
{ |
365 |
method2 = 0; |
366 |
while((method2 = (ILMethod *)ILClassNextMemberByKind |
367 |
(parent, (ILMember *)method2, |
368 |
IL_META_MEMBERKIND_METHOD)) != 0) |
369 |
{ |
370 |
if((method2->member.attributes & |
371 |
(IL_META_METHODDEF_MEMBER_ACCESS_MASK | |
372 |
IL_META_METHODDEF_VIRTUAL)) == |
373 |
(IL_META_METHODDEF_PUBLIC | |
374 |
IL_META_METHODDEF_VIRTUAL)) |
375 |
{ |
376 |
if(strcmp(method2->member.name, |
377 |
method->member.name) != 0 || |
378 |
!ILTypeIdentical(method2->member.signature, |
379 |
method->member.signature)) |
380 |
{ |
381 |
continue; |
382 |
} |
383 |
table[slot] = (ILUInt16)(method2->index); |
384 |
break; |
385 |
} |
386 |
} |
387 |
if(table[slot] != (ILUInt16)0xFFFF) |
388 |
{ |
389 |
break; |
390 |
} |
391 |
parent = ILClassGetParent(parent); |
392 |
} |
393 |
} |
394 |
|
395 |
/* Look for an override for the interface method */ |
396 |
over = 0; |
397 |
while((over = (ILOverride *)ILClassNextMemberByKind |
398 |
(info, (ILMember *)over, IL_META_MEMBERKIND_OVERRIDE)) != 0) |
399 |
{ |
400 |
if(ILMemberResolve((ILMember *)(ILOverrideGetDecl(over))) |
401 |
== (ILMember *)method) |
402 |
{ |
403 |
table[slot] = (ILOverrideGetBody(over))->index; |
404 |
break; |
405 |
} |
406 |
} |
407 |
if(table[slot] == (ILUInt16)0xFFFF) |
408 |
{ |
409 |
/* There is no implementation of this method, which is an error */ |
410 |
return 0; |
411 |
} |
412 |
} |
413 |
|
414 |
/* Recursively compute all interfaces that the interface inherits from */ |
415 |
implBlock = interface->implements; |
416 |
while(implBlock != 0) |
417 |
{ |
418 |
if(!ComputeInterfaceTable(info, ILClassResolve(implBlock->interface))) |
419 |
{ |
420 |
return 0; |
421 |
} |
422 |
implBlock = implBlock->nextInterface; |
423 |
} |
424 |
|
425 |
/* Done */ |
426 |
return 1; |
427 |
} |
428 |
|
429 |
#ifdef IL_USE_IMTS |
430 |
|
431 |
/* |
432 |
* Build an "Interface Method Table" (IMT) for a particular class, from |
433 |
* the interface information in that class. The mechanism is described |
434 |
* in the following paper by IBM: |
435 |
* |
436 |
* "Efficient Implementation of Java Interfaces: Invokeinterface Considered |
437 |
* Harmless", Bowen Alpern, Anthony Cocchi, Stephen Fink, David Grove, |
438 |
* and Derek Lieber. ACM Conference on Object-Oriented Programming, Systems, |
439 |
* Languages, and Applications (OOPSLA), Tampa, FL, USA, Oct 14-18, 2001. |
440 |
* |
441 |
* http://www.research.ibm.com/people/d/dgrove/papers/oopsla01.pdf |
442 |
* |
443 |
* Each interface method in the system is assigned a unique identifier. |
444 |
* The identifier is used to hash into a fixed-sized table of method entry |
445 |
* points, similar to a vtable. In most cases, the hashed position will |
446 |
* be the right position. |
447 |
* |
448 |
* We modify the IMT conflict resolution mechanism slightly. IBM's RVM |
449 |
* system generates special conflict resolution stubs for interface methods |
450 |
* that hash to the same IMT position. We store NULL at any position where |
451 |
* there is a conflict and bail out to the traditional lookup algorithm. |
452 |
* This is necessary because we convert methods one at a time, instead of |
453 |
* class at a time like RVM does. |
454 |
*/ |
455 |
static void BuildIMT(ILExecProcess *process, ILClass *info, |
456 |
ILClassPrivate *classPrivate) |
457 |
{ |
458 |
ILImplPrivate *impl; |
459 |
ILClassPrivate *implPrivate; |
460 |
ILClassPrivate *parentPrivate; |
461 |
ILUInt32 posn, size; |
462 |
ILUInt32 vtableIndex; |
463 |
ILUInt32 imtIndex; |
464 |
|
465 |
/* Is this class itself an interface? */ |
466 |
if(ILClass_IsInterface(info)) |
467 |
{ |
468 |
/* Allocate the base identifier for the interface. We must do |
469 |
this here in case an interface is referenced by a "callvirt" |
470 |
instruction before any of the classes that implement it are |
471 |
laid out */ |
472 |
if(!(classPrivate->imtBase)) |
473 |
{ |
474 |
size = (ILUInt32)(classPrivate->vtableSize); |
475 |
classPrivate->imtBase = process->imtBase; |
476 |
process->imtBase += size; |
477 |
} |
478 |
return; |
479 |
} |
480 |
|
481 |
parentPrivate = classPrivate; |
482 |
while(parentPrivate != NULL) |
483 |
{ |
484 |
/* Process the implementation records that are attached to this class */ |
485 |
impl = parentPrivate->implements; |
486 |
while(impl != 0) |
487 |
{ |
488 |
implPrivate = (ILClassPrivate *)(impl->interface->userData); |
489 |
if(implPrivate) |
490 |
{ |
491 |
/* Allocate a base identifer for the interface if necessary. |
492 |
This will probably never be used because interfaces are |
493 |
typically laid out before their implementing classes, and |
494 |
the allocation above will catch us. But let's be paranoid |
495 |
and allocate the base identifier here anyway, just in case */ |
496 |
size = (ILUInt32)(implPrivate->vtableSize); |
497 |
if(!(implPrivate->imtBase)) |
498 |
{ |
499 |
implPrivate->imtBase = process->imtBase; |
500 |
process->imtBase += size; |
501 |
} |
502 |
|
503 |
/* Process the members of this interface */ |
504 |
for(posn = 0; posn < size; ++posn) |
505 |
{ |
506 |
imtIndex = (implPrivate->imtBase + posn) % IL_IMT_SIZE; |
507 |
vtableIndex = (ILImplPrivate_Table(impl))[posn]; |
508 |
if(vtableIndex != (ILUInt32)(ILUInt16)0xFFFF) |
509 |
{ |
510 |
if(!(classPrivate->imt[imtIndex])) |
511 |
{ |
512 |
/* No conflict at this table position */ |
513 |
classPrivate->imt[imtIndex] = |
514 |
classPrivate->vtable[vtableIndex]; |
515 |
} |
516 |
else |
517 |
{ |
518 |
/* We have encountered a conflict in the table */ |
519 |
classPrivate->imt[imtIndex] = |
520 |
(ILMethod *)(ILNativeInt)(-1); |
521 |
} |
522 |
} |
523 |
} |
524 |
} |
525 |
impl = impl->next; |
526 |
} |
527 |
if(!parentPrivate->classInfo->parent) |
528 |
{ |
529 |
break; |
530 |
} |
531 |
parentPrivate = (ILClassPrivate*) (parentPrivate->classInfo->parent->userData); |
532 |
} |
533 |
|
534 |
/* Clear positions in the table that indicate conflicts */ |
535 |
for(posn = 0; posn < IL_IMT_SIZE; ++posn) |
536 |
{ |
537 |
if(classPrivate->imt[posn] == (ILMethod *)(ILNativeInt)(-1)) |
538 |
{ |
539 |
classPrivate->imt[posn] = 0; |
540 |
} |
541 |
} |
542 |
} |
543 |
|
544 |
#endif /* IL_USE_IMTS */ |
545 |
|
546 |
/* |
547 |
* Lay out a particular class. Returns zero if there |
548 |
* is something wrong with the class definition. |
549 |
*/ |
550 |
static int LayoutClass(ILExecProcess *process, ILClass *info, LayoutInfo *layout) |
551 |
{ |
552 |
ILClassLayout *classLayout; |
553 |
ILFieldLayout *fieldLayout; |
554 |
ILFieldRVA *fieldRVA; |
555 |
LayoutInfo typeLayout; |
556 |
ILUInt32 maxAlignment; |
557 |
ILUInt32 maxNativeAlignment; |
558 |
ILUInt32 packingSize; |
559 |
ILUInt32 explicitSize; |
560 |
int allowFieldLayout; |
561 |
int allowRVALayout; |
562 |
ILClassPrivate *volatile classPrivate; |
563 |
ILField *field; |
564 |
ILMethod *method; |
565 |
ILMethod *ancestor; |
566 |
ILMethod **vtable; |
567 |
ILClass *parent; |
568 |
ILImplements *implements; |
569 |
ILMethodCode code; |
570 |
|
571 |
/* Determine if we have already tried to lay out this class */ |
572 |
if(info->userData) |
573 |
{ |
574 |
/* We have layout data, so return it */ |
575 |
classPrivate = (ILClassPrivate *)(info->userData); |
576 |
if(classPrivate->inLayout) |
577 |
{ |
578 |
/* We have detected a layout loop. This can occur if a |
579 |
class attempts to include itself in a value type field */ |
580 |
return 0; |
581 |
} |
582 |
else |
583 |
{ |
584 |
layout->size = classPrivate->size; |
585 |
layout->alignment = classPrivate->alignment; |
586 |
layout->nativeSize = classPrivate->nativeSize; |
587 |
layout->nativeAlignment = classPrivate->nativeAlignment; |
588 |
layout->vtableSize = classPrivate->vtableSize; |
589 |
layout->vtable = classPrivate->vtable; |
590 |
layout->staticSize = classPrivate->staticSize; |
591 |
layout->hasFinalizer = classPrivate->hasFinalizer; |
592 |
layout->managedInstance = classPrivate->managedInstance; |
593 |
layout->managedStatic = classPrivate->managedStatic; |
594 |
return 1; |
595 |
} |
596 |
} |
597 |
else |
598 |
{ |
599 |
/* Create a new layout record and attach it to the class. |
600 |
We must allocate this in memory accessible by the |
601 |
garbage collector so that the object pointers that |
602 |
it contains will be visible to the collector */ |
603 |
classPrivate = (ILClassPrivate *) |
604 |
(ILGCAlloc(sizeof(ILClassPrivate))); |
605 |
if(!classPrivate) |
606 |
{ |
607 |
return 0; |
608 |
} |
609 |
classPrivate->classInfo = info; |
610 |
info->userData = (void *)classPrivate; |
611 |
classPrivate->inLayout = 1; |
612 |
classPrivate->gcTypeDescriptor = IL_MAX_NATIVE_UINT; |
613 |
classPrivate->process = process; |
614 |
} |
615 |
|
616 |
/* Lay out the parent class first */ |
617 |
if(info->parent) |
618 |
{ |
619 |
/* Use "ILClassGetParent" to resolve cross-image links */ |
620 |
parent = ILClassGetParent(info); |
621 |
if(!LayoutClass(process, parent, layout)) |
622 |
{ |
623 |
info->userData = 0; |
624 |
return 0; |
625 |
} |
626 |
} |
627 |
else |
628 |
{ |
629 |
/* This is a top-level class (normally "System.Object") */ |
630 |
parent = 0; |
631 |
layout->size = 0; |
632 |
layout->alignment = 1; |
633 |
layout->nativeSize = 0; |
634 |
layout->nativeAlignment = 1; |
635 |
layout->vtableSize = 0; |
636 |
layout->hasFinalizer = 0; |
637 |
layout->managedInstance = 0; |
638 |
layout->managedStatic = 0; |
639 |
} |
640 |
|
641 |
/* Zero the static size, which must be recomputed for each class */ |
642 |
layout->staticSize = 0; |
643 |
|
644 |
/* Lay out the interfaces that this class implements */ |
645 |
implements = info->implements; |
646 |
while(implements != 0) |
647 |
{ |
648 |
if(!LayoutClass(process, ILClassResolve(implements->interface), &typeLayout)) |
649 |
{ |
650 |
info->userData = 0; |
651 |
return 0; |
652 |
} |
653 |
implements = implements->nextInterface; |
654 |
} |
655 |
|
656 |
/* Should we use the explicit layout algorithm? */ |
657 |
packingSize = 0; |
658 |
explicitSize = 0; |
659 |
allowFieldLayout = 0; |
660 |
allowRVALayout = ILImageIsSecure(ILProgramItem_Image(info)); |
661 |
if((info->attributes & IL_META_TYPEDEF_LAYOUT_MASK) == |
662 |
IL_META_TYPEDEF_EXPLICIT_LAYOUT) |
663 |
{ |
664 |
/* Check that security permissions allow explicit layout */ |
665 |
if(allowRVALayout) |
666 |
{ |
667 |
/* Look for class layout information to specify the size */ |
668 |
classLayout = ILClassLayoutGetFromOwner(info); |
669 |
if(classLayout) |
670 |
{ |
671 |
/* Validate the class packing size */ |
672 |
if(classLayout->packingSize != 0 && |
673 |
classLayout->packingSize != 1 && |
674 |
classLayout->packingSize != 2 && |
675 |
classLayout->packingSize != 4 && |
676 |
classLayout->packingSize != 8) |
677 |
{ |
678 |
info->userData = 0; |
679 |
return 0; |
680 |
} |
681 |
packingSize = classLayout->packingSize; |
682 |
|
683 |
/* Record the explicit size to be used later */ |
684 |
explicitSize = classLayout->classSize; |
685 |
} |
686 |
|
687 |
/* Field layout is permitted */ |
688 |
allowFieldLayout = 1; |
689 |
} |
690 |
} |
691 |
|
692 |
/* Use straight-forward field allocation, which will usually |
693 |
match the algorithm used by the platform C compiler */ |
694 |
field = 0; |
695 |
maxAlignment = 1; |
696 |
maxNativeAlignment = 1; |
697 |
while((field = (ILField *)ILClassNextMemberByKind |
698 |
(info, (ILMember *)field, IL_META_MEMBERKIND_FIELD)) != 0) |
699 |
{ |
700 |
if((field->member.attributes & IL_META_FIELDDEF_STATIC) == 0) |
701 |
{ |
702 |
/* Get the layout information for this field's type */ |
703 |
if(!LayoutType(process, field->member.signature, &typeLayout)) |
704 |
{ |
705 |
info->userData = 0; |
706 |
return 0; |
707 |
} |
708 |
|
709 |
/* Decrease the alignment if we have an explicit packing size */ |
710 |
if(packingSize != 0 && packingSize < typeLayout.alignment) |
711 |
{ |
712 |
typeLayout.alignment = packingSize; |
713 |
} |
714 |
if(packingSize != 0 && packingSize < typeLayout.nativeAlignment) |
715 |
{ |
716 |
typeLayout.nativeAlignment = packingSize; |
717 |
} |
718 |
|
719 |
/* Use an explicit field offset if necessary */ |
720 |
if(allowFieldLayout && |
721 |
(fieldLayout = ILFieldLayoutGetFromOwner(field)) != 0) |
722 |
{ |
723 |
/* Record the explicit field offset */ |
724 |
field->offset = fieldLayout->offset; |
725 |
field->nativeOffset = fieldLayout->offset; |
726 |
|
727 |
/* Extend the default class size to include the field */ |
728 |
if((field->offset + typeLayout.size) > layout->size) |
729 |
{ |
730 |
layout->size = field->offset + typeLayout.size; |
731 |
} |
732 |
if((field->offset + typeLayout.nativeSize) > layout->nativeSize) |
733 |
{ |
734 |
layout->nativeSize = field->offset + typeLayout.nativeSize; |
735 |
} |
736 |
} |
737 |
else |
738 |
{ |
739 |
/* Align the field on an appropriate boundary */ |
740 |
if((layout->size % typeLayout.alignment) != 0) |
741 |
{ |
742 |
layout->size += typeLayout.alignment - |
743 |
(layout->size % typeLayout.alignment); |
744 |
} |
745 |
if((layout->nativeSize % typeLayout.nativeAlignment) != 0) |
746 |
{ |
747 |
layout->nativeSize += typeLayout.nativeAlignment - |
748 |
(layout->nativeSize % typeLayout.nativeAlignment); |
749 |
} |
750 |
|
751 |
/* Record the field's offset and advance past it */ |
752 |
field->offset = layout->size; |
753 |
field->nativeOffset = layout->nativeSize; |
754 |
layout->size += typeLayout.size; |
755 |
layout->nativeSize += typeLayout.nativeSize; |
756 |
} |
757 |
|
758 |
/* Update the maximum alignment */ |
759 |
if(typeLayout.alignment > maxAlignment) |
760 |
{ |
761 |
maxAlignment = typeLayout.alignment; |
762 |
} |
763 |
if(typeLayout.nativeAlignment > maxNativeAlignment) |
764 |
{ |
765 |
maxNativeAlignment = typeLayout.nativeAlignment; |
766 |
} |
767 |
|
768 |
/* Set the "managedInstance" flag if the type is managed */ |
769 |
if(typeLayout.managedInstance) |
770 |
{ |
771 |
layout->managedInstance = 1; |
772 |
} |
773 |
} |
774 |
} |
775 |
|
776 |
/* Compute the final class size based on explicit sizes and alignment */ |
777 |
if(maxAlignment > layout->alignment) |
778 |
{ |
779 |
layout->alignment = maxAlignment; |
780 |
} |
781 |
if(explicitSize > layout->size) |
782 |
{ |
783 |
layout->size = explicitSize; |
784 |
} |
785 |
else if((layout->size % layout->alignment) != 0) |
786 |
{ |
787 |
layout->size += layout->alignment - |
788 |
(layout->size % layout->alignment); |
789 |
} |
790 |
if(maxNativeAlignment > layout->nativeAlignment) |
791 |
{ |
792 |
layout->nativeAlignment = maxNativeAlignment; |
793 |
} |
794 |
if(explicitSize > layout->nativeSize) |
795 |
{ |
796 |
layout->nativeSize = explicitSize; |
797 |
} |
798 |
else if((layout->nativeSize % layout->nativeAlignment) != 0) |
799 |
{ |
800 |
layout->nativeSize += layout->nativeAlignment - |
801 |
(layout->nativeSize % layout->nativeAlignment); |
802 |
} |
803 |
|
804 |
/* Record the object size information for this class */ |
805 |
classPrivate->size = layout->size; |
806 |
classPrivate->alignment = layout->alignment; |
807 |
classPrivate->nativeSize = layout->nativeSize; |
808 |
classPrivate->nativeAlignment = layout->nativeAlignment; |
809 |
classPrivate->inLayout = 0; |
810 |
|
811 |
/* Allocate the static fields. We must do this after the |
812 |
regular fields because some of the statics may be instances |
813 |
of the class that we are trying to lay out, especially |
814 |
in value type definitions */ |
815 |
field = 0; |
816 |
while((field = (ILField *)ILClassNextMemberByKind |
817 |
(info, (ILMember *)field, IL_META_MEMBERKIND_FIELD)) != 0) |
818 |
{ |
819 |
if((field->member.attributes & IL_META_FIELDDEF_STATIC) != 0 && |
820 |
(field->member.attributes & IL_META_FIELDDEF_LITERAL) == 0) |
821 |
{ |
822 |
/* Lay out a static field */ |
823 |
fieldRVA = ILFieldRVAGetFromOwner(field); |
824 |
if(fieldRVA && !allowRVALayout) |
825 |
{ |
826 |
/* RVA fields are not permitted, so remove the attribute */ |
827 |
field->member.attributes &= ~IL_META_FIELDDEF_HAS_FIELD_RVA; |
828 |
} |
829 |
if(!fieldRVA || !allowRVALayout) |
830 |
{ |
831 |
/* Get the layout information for this field's type */ |
832 |
if(!LayoutType(process, field->member.signature, &typeLayout)) |
833 |
{ |
834 |
info->userData = 0; |
835 |
return 0; |
836 |
} |
837 |
|
838 |
/* Thread-static variables are allocated slots from the |
839 |
ILExecProcess record. We assume that some higher level |
840 |
function has acquired the metadata lock on the process */ |
841 |
if(ILFieldIsThreadStatic(field)) |
842 |
{ |
843 |
/* Store the slot number in the "offset" field and |
844 |
the field size in the "nativeOffset" field */ |
845 |
field->offset = (process->numThreadStaticSlots)++; |
846 |
field->nativeOffset = typeLayout.size; |
847 |
continue; |
848 |
} |
849 |
|
850 |
/* Align the field on an appropriate boundary */ |
851 |
if((layout->staticSize % typeLayout.alignment) != 0) |
852 |
{ |
853 |
layout->staticSize += typeLayout.alignment - |
854 |
(layout->staticSize % typeLayout.alignment); |
855 |
} |
856 |
|
857 |
/* Record the field's offset and advance past it */ |
858 |
field->offset = layout->staticSize; |
859 |
field->nativeOffset = layout->staticSize; |
860 |
layout->staticSize += typeLayout.size; |
861 |
|
862 |
/* Set the "managedStatic" flag if the type is managed */ |
863 |
if(typeLayout.managedInstance) |
864 |
{ |
865 |
layout->managedStatic = 1; |
866 |
} |
867 |
} |
868 |
} |
869 |
} |
870 |
|
871 |
/* Allocate vtable slots to the virtual methods in this class */ |
872 |
method = 0; |
873 |
explicitSize = layout->vtableSize; |
874 |
while((method = (ILMethod *)ILClassNextMemberByKind |
875 |
(info, (ILMember *)method, IL_META_MEMBERKIND_METHOD)) != 0) |
876 |
{ |
877 |
/* Skip this method if it isn't virtual */ |
878 |
if((method->member.attributes & IL_META_METHODDEF_VIRTUAL) == 0) |
879 |
{ |
880 |
continue; |
881 |
} |
882 |
|
883 |
/* Is this the finalize method? */ |
884 |
if(method->member.name[0] == 'F' && |
885 |
!strcmp(method->member.name + 1, "inalize") && |
886 |
method->member.signature->un.method__.retType__ == ILType_Void && |
887 |
method->member.signature->num__ == 0) |
888 |
{ |
889 |
/* Determine if the finalizer is non-trivial */ |
890 |
if(!ILMethodGetCode(method, &code) || |
891 |
code.codeLen != 1 || |
892 |
((unsigned char *)(code.code))[0] != IL_OP_RET) |
893 |
{ |
894 |
layout->hasFinalizer = 1; |
895 |
} |
896 |
} |
897 |
|
898 |
/* Do we need a new slot for this method? */ |
899 |
if((method->member.attributes & IL_META_METHODDEF_NEW_SLOT) != 0) |
900 |
{ |
901 |
/* Allocate a vtable slot */ |
902 |
method->index = layout->vtableSize; |
903 |
++(layout->vtableSize); |
904 |
} |
905 |
else |
906 |
{ |
907 |
/* Find the method in an ancestor class that this one overrides */ |
908 |
ancestor = FindVirtualAncestor(info, parent, method); |
909 |
if(ancestor) |
910 |
{ |
911 |
/* Use the same index as the ancestor */ |
912 |
method->index = ancestor->index; |
913 |
} |
914 |
else |
915 |
{ |
916 |
/* No ancestor, so allocate a new slot. This case is |
917 |
quite rare and will typically only happen with code |
918 |
that has been loaded from a Java .class file, or |
919 |
where the ancestor method is not accessible due to |
920 |
permission issues */ |
921 |
method->member.attributes |= IL_META_METHODDEF_NEW_SLOT; |
922 |
method->index = layout->vtableSize; |
923 |
++(layout->vtableSize); |
924 |
} |
925 |
} |
926 |
} |
927 |
|
928 |
/* If the vtable has grown too big, then bail out */ |
929 |
if(layout->vtableSize > (ILUInt32)65535) |
930 |
{ |
931 |
info->userData = 0; |
932 |
return 0; |
933 |
} |
934 |
|
935 |
/* Allocate the vtable and copy the parent's vtable into it */ |
936 |
if((vtable = (ILMethod **) |
937 |
ILMemStackAllocItem(&(info->programItem.image->memStack), |
938 |
layout->vtableSize * sizeof(ILMethod *))) == 0) |
939 |
{ |
940 |
info->userData = 0; |
941 |
return 0; |
942 |
} |
943 |
if(explicitSize > 0) |
944 |
{ |
945 |
ILMemCpy(vtable, layout->vtable, explicitSize * sizeof(ILMethod *)); |
946 |
} |
947 |
|
948 |
/* Override the vtable slots with this class's method implementations */ |
949 |
method = 0; |
950 |
while((method = (ILMethod *)ILClassNextMemberByKind |
951 |
(info, (ILMember *)method, IL_META_MEMBERKIND_METHOD)) != 0) |
952 |
{ |
953 |
if((method->member.attributes & IL_META_METHODDEF_VIRTUAL) != 0) |
954 |
{ |
955 |
vtable[method->index] = method; |
956 |
} |
957 |
} |
958 |
|
959 |
/* Compute the interface tables for this class */ |
960 |
if((info->attributes & IL_META_TYPEDEF_CLASS_SEMANTICS_MASK) != |
961 |
IL_META_TYPEDEF_INTERFACE) |
962 |
{ |
963 |
implements = info->implements; |
964 |
while(implements != 0) |
965 |
{ |
966 |
parent = ILClassResolve(implements->interface); |
967 |
if(parent->userData) |
968 |
{ |
969 |
if(!ComputeInterfaceTable(info, parent)) |
970 |
{ |
971 |
info->userData = 0; |
972 |
return 0; |
973 |
} |
974 |
} |
975 |
implements = implements->nextInterface; |
976 |
} |
977 |
} |
978 |
|
979 |
/* Record the rest of the layout information for this class */ |
980 |
classPrivate->staticSize = layout->staticSize; |
981 |
classPrivate->vtableSize = layout->vtableSize; |
982 |
classPrivate->vtable = vtable; |
983 |
classPrivate->hasFinalizer = layout->hasFinalizer; |
984 |
classPrivate->managedInstance = layout->managedInstance; |
985 |
classPrivate->managedStatic = layout->managedStatic; |
986 |
layout->vtable = vtable; |
987 |
|
988 |
#ifdef IL_USE_IMTS |
989 |
/* Build the interface method table for this class */ |
990 |
BuildIMT(process, info, classPrivate); |
991 |
#endif |
992 |
|
993 |
/* Done */ |
994 |
/* add the classprivate data to the liked list */ |
995 |
classPrivate->nextClassPrivate = process->firstClassPrivate; |
996 |
process->firstClassPrivate = classPrivate; |
997 |
|
998 |
return 1; |
999 |
} |
1000 |
|
1001 |
int _ILLayoutClass(ILExecProcess *process, ILClass *info) |
1002 |
{ |
1003 |
LayoutInfo layout; |
1004 |
return LayoutClass(process, info, &layout); |
1005 |
} |
1006 |
|
1007 |
ILUInt32 _ILLayoutClassReturn(ILExecProcess *process, ILClass *info, ILUInt32 *alignment) |
1008 |
{ |
1009 |
LayoutInfo layout; |
1010 |
if(LayoutClass(process, info, &layout)) |
1011 |
{ |
1012 |
*alignment = layout.alignment; |
1013 |
return layout.size; |
1014 |
} |
1015 |
else |
1016 |
{ |
1017 |
*alignment = 0; |
1018 |
return 0; |
1019 |
} |
1020 |
} |
1021 |
|
1022 |
int _ILLayoutAlreadyDone(ILClass *info) |
1023 |
{ |
1024 |
if(info->userData) |
1025 |
{ |
1026 |
ILClassPrivate *classPrivate = (ILClassPrivate *)(info->userData); |
1027 |
return !(classPrivate->inLayout); |
1028 |
} |
1029 |
else |
1030 |
{ |
1031 |
return 0; |
1032 |
} |
1033 |
} |
1034 |
|
1035 |
ILUInt32 _ILSizeOfTypeLocked(ILExecProcess *process, ILType *type) |
1036 |
{ |
1037 |
LayoutInfo layout; |
1038 |
if(!LayoutType(process, type, &layout)) |
1039 |
{ |
1040 |
return 0; |
1041 |
} |
1042 |
else |
1043 |
{ |
1044 |
return layout.size; |
1045 |
} |
1046 |
} |
1047 |
|
1048 |
ILUInt32 ILSizeOfType(ILExecThread *thread, ILType *type) |
1049 |
{ |
1050 |
if(!ILType_IsValueType(type)) |
1051 |
{ |
1052 |
/* We can take a shortcut because the type is not a value type */ |
1053 |
return _ILSizeOfTypeLocked(_ILExecThreadProcess(thread), type); |
1054 |
} |
1055 |
else |
1056 |
{ |
1057 |
/* We have to lock down the metadata first */ |
1058 |
ILUInt32 size; |
1059 |
IL_METADATA_WRLOCK(_ILExecThreadProcess(thread)); |
1060 |
size = _ILSizeOfTypeLocked(_ILExecThreadProcess(thread), type); |
1061 |
IL_METADATA_UNLOCK(_ILExecThreadProcess(thread)); |
1062 |
return size; |
1063 |
} |
1064 |
} |
1065 |
|
1066 |
#ifdef __cplusplus |
1067 |
}; |
1068 |
#endif |