1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include "sjme/nvm/classy.h"
11 #include "sjme/debug.h"
12 #include "sjme/cleanup.h"
14 /** The magic number for classes. */
15 #define SJME_CLASS_MAGIC INT32_C(0xCAFEBABE)
17 /** CLDC 1.1 max version (JSR 30). */
18 #define SJME_CLASS_CLDC_1_0_MAX INT32_C(3080191)
20 /** CLDC 1.1 max version. (JSR 139). */
21 #define SJME_CLASS_CLDC_1_1_MAX INT32_C(3342335)
23 /** CLDC 8 max version. */
24 #define SJME_CLASS_CLDC_1_8_MAX INT32_C(3407872)
27 #define SJME_CLASS_ACC_PUBLIC INT16_C(0x0001)
30 #define SJME_CLASS_ACC_FINAL INT16_C(0x0010)
32 /** Alternative @c invokesuper logic. */
33 #define SJME_CLASS_ACC_SUPER INT16_C(0x0020)
35 /** Class is an interface. */
36 #define SJME_CLASS_ACC_INTERFACE INT16_C(0x0200)
38 /** Class is abstract. */
39 #define SJME_CLASS_ACC_ABSTRACT INT16_C(0x0400)
41 /** Class is synthetic. */
42 #define SJME_CLASS_ACC_SYNTHETIC INT16_C(0x1000)
44 /** Class is an annotation. */
45 #define SJME_CLASS_ACC_ANNOTATION INT16_C(0x2000)
47 /** Class is an enum. */
48 #define SJME_CLASS_ACC_ENUM INT16_C(0x4000)
50 static sjme_errorCode
sjme_class_readClassFlags(
51 sjme_attrInNotNull sjme_stream_input inStream
,
52 sjme_attrOutNotNull sjme_class_classFlags
* outFlags
)
57 if (inStream
== NULL
|| outFlags
== NULL
)
58 return SJME_ERROR_NULL_ARGUMENTS
;
62 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
63 inStream
, &rawFlags
)) || rawFlags
< 0)
64 return sjme_error_default(error
);
66 /* Translate to bitfield. */
67 if ((rawFlags
& SJME_CLASS_ACC_PUBLIC
) != 0)
68 outFlags
->access
.public = SJME_JNI_TRUE
;
69 if ((rawFlags
& SJME_CLASS_ACC_FINAL
) != 0)
70 outFlags
->final
= SJME_JNI_TRUE
;
71 if ((rawFlags
& SJME_CLASS_ACC_SUPER
) != 0)
72 outFlags
->super
= SJME_JNI_TRUE
;
73 if ((rawFlags
& SJME_CLASS_ACC_INTERFACE
) != 0)
74 outFlags
->interface
= SJME_JNI_TRUE
;
75 if ((rawFlags
& SJME_CLASS_ACC_ABSTRACT
) != 0)
76 outFlags
->abstract
= SJME_JNI_TRUE
;
77 if ((rawFlags
& SJME_CLASS_ACC_SYNTHETIC
) != 0)
78 outFlags
->synthetic
= SJME_JNI_TRUE
;
79 if ((rawFlags
& SJME_CLASS_ACC_ANNOTATION
) != 0)
80 outFlags
->annotation
= SJME_JNI_TRUE
;
81 if ((rawFlags
& SJME_CLASS_ACC_ENUM
) != 0)
82 outFlags
->enumeration
= SJME_JNI_TRUE
;
84 /* Cannot be abstract and final. */
85 /* Annotation must be an interface. */
86 /* Interface must be abstract and not final, super, or enum */
87 if ((outFlags
->abstract
&& outFlags
->final
) ||
88 (outFlags
->annotation
&& !outFlags
->interface
) ||
89 (outFlags
->interface
&& (!outFlags
->abstract
||
90 outFlags
->final
|| outFlags
->super
|| outFlags
->enumeration
)))
91 return SJME_ERROR_INVALID_CLASS_FLAGS
;
94 return SJME_ERROR_NONE
;
97 static sjme_errorCode
sjme_class_readPoolRefIndex(
98 sjme_attrInNotNull sjme_stream_input inStream
,
99 sjme_attrInNotNull sjme_class_poolInfo inClassPool
,
100 sjme_attrInPositiveNonZero sjme_class_poolType desireType
,
101 sjme_attrInValue sjme_jboolean canNull
,
102 sjme_attrOutNotNull sjme_class_poolEntry
** outEntry
)
104 sjme_errorCode error
;
106 sjme_class_poolEntry
* result
;
108 if (inStream
== NULL
|| inClassPool
== NULL
|| outEntry
== NULL
)
109 return SJME_ERROR_NULL_ARGUMENTS
;
113 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
115 return sjme_error_default(error
);
117 /* Not a valid index? */
118 if (index
<= 0 || index
>= inClassPool
->pool
->length
)
119 return SJME_ERROR_INVALID_CLASS_POOL_INDEX
;
121 /* Must be the desired type. */
122 result
= &inClassPool
->pool
->elements
[index
];
123 if (result
->type
!= desireType
)
124 return SJME_ERROR_WRONG_CLASS_POOL_INDEX_TYPE
;
128 return SJME_ERROR_NONE
;
131 sjme_errorCode
sjme_class_parse(
132 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
133 sjme_attrInNotNull sjme_stream_input inStream
,
134 sjme_attrInNotNull sjme_stringPool inStringPool
,
135 sjme_attrOutNotNull sjme_class_info
* outClass
)
137 sjme_errorCode error
;
138 sjme_jint magic
, fullVersion
, i
;
139 sjme_jshort major
, minor
, interfaceCount
, fieldCount
, methodCount
;
140 sjme_class_version actualVersion
;
141 sjme_class_poolInfo pool
;
142 sjme_class_info result
;
143 sjme_class_poolEntry
* thisName
;
144 sjme_class_poolEntry
* superName
;
145 sjme_class_poolEntry
* interfaceName
;
146 sjme_list_sjme_stringPool_string
* interfaceNames
;
147 sjme_list_sjme_class_fieldInfo
* fields
;
148 sjme_list_sjme_class_methodInfo
* methods
;
150 if (inPool
== NULL
|| inStream
== NULL
|| inStringPool
== NULL
||
152 return SJME_ERROR_NONE
;
154 /* Make sure we can actually allocate the resultant class. */
156 if (sjme_error_is(error
= sjme_alloc_weakNew(inPool
,
157 sizeof(*result
), NULL
, NULL
,
158 (sjme_pointer
*)&result
, NULL
)) || result
== NULL
)
159 goto fail_allocResult
;
162 if (sjme_error_is(error
= sjme_nvm_initCommon(
163 SJME_AS_NVM_COMMON(result
),
164 SJME_NVM_STRUCT_CLASS_INFO
)))
165 goto fail_initResult
;
167 /* Read in magic number. */
169 if (sjme_error_is(error
= sjme_stream_inputReadValueJI(
173 /* It must be valid! */
174 if (magic
!= SJME_CLASS_MAGIC
)
176 error
= SJME_ERROR_INVALID_CLASS_MAGIC
;
180 /* Read in version info. */
182 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
187 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
191 /* Compose and find matching version. */
192 fullVersion
= (major
<< 16) | (minor
& 0xFFFF);
193 if (fullVersion
>= SJME_CLASS_CLDC_1_0
&&
194 fullVersion
<= SJME_CLASS_CLDC_1_0_MAX
)
195 actualVersion
= SJME_CLASS_CLDC_1_0
;
196 else if (fullVersion
>= SJME_CLASS_CLDC_1_1
&&
197 fullVersion
<= SJME_CLASS_CLDC_1_1_MAX
)
198 actualVersion
= SJME_CLASS_CLDC_1_1
;
199 else if (fullVersion
>= SJME_CLASS_CLDC_1_8
&&
200 fullVersion
<= SJME_CLASS_CLDC_1_8_MAX
)
201 actualVersion
= SJME_CLASS_CLDC_1_8
;
206 error
= SJME_ERROR_INVALID_CLASS_VERSION
;
207 goto fail_badVersion
;
211 result
->version
= actualVersion
;
213 /* Parse the constant pool. */
215 if (sjme_error_is(error
= sjme_class_parseConstantPool(
216 inPool
, inStream
, inStringPool
, &pool
)) || pool
== NULL
)
219 /* We are using this, so count it up. */
220 if (sjme_error_is(error
= sjme_alloc_weakRef(pool
, NULL
)))
225 if (sjme_error_is(error
= sjme_class_readClassFlags(
226 inStream
, &result
->flags
)))
229 /* Read in this name. */
231 if (sjme_error_is(error
= sjme_class_readPoolRefIndex(
232 inStream
, result
->pool
,
233 SJME_CLASS_POOL_TYPE_CLASS
,
234 SJME_JNI_FALSE
, &thisName
)) || thisName
== NULL
)
235 goto fail_readThisName
;
238 result
->name
= thisName
->classRef
.descriptor
;
239 if (sjme_error_is(error
= sjme_alloc_weakRef(
240 result
->name
, NULL
)))
241 goto fail_refThisName
;
243 /* Read in super name. */
245 if (sjme_error_is(error
= sjme_class_readPoolRefIndex(
246 inStream
, result
->pool
,
247 SJME_CLASS_POOL_TYPE_CLASS
,
248 SJME_JNI_TRUE
, &superName
)))
249 goto fail_readSuperName
;
251 /* Reference it, if valid. */
252 if (superName
!= NULL
)
254 result
->superName
= superName
->classRef
.descriptor
;
255 if (sjme_error_is(error
= sjme_alloc_weakRef(
256 result
->superName
, NULL
)))
257 goto fail_refSuperName
;
260 /* How many interfaces are there? */
262 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
263 inStream
, &interfaceCount
)) || interfaceCount
< 0)
264 goto fail_readInterfaceCount
;
266 /* Allocate interfaces count. */
267 interfaceNames
= NULL
;
268 if (sjme_error_is(error
= sjme_list_alloc(inPool
,
269 interfaceCount
, &interfaceNames
, sjme_stringPool_string
, 0)) ||
270 interfaceNames
== NULL
)
271 goto fail_allocInterfaceNames
;
272 result
->interfaceNames
= interfaceNames
;
274 /* Read in all interfaces. */
275 for (i
= 0; i
< interfaceCount
; i
++)
278 interfaceName
= NULL
;
279 if (sjme_error_is(error
= sjme_class_readPoolRefIndex(
280 inStream
, result
->pool
,
281 SJME_CLASS_POOL_TYPE_CLASS
,
282 SJME_JNI_FALSE
, &interfaceName
)) ||
283 interfaceName
== NULL
)
284 goto fail_readThisName
;
287 interfaceNames
->elements
[i
] = interfaceName
->classRef
.descriptor
;
288 if (sjme_error_is(error
= sjme_alloc_weakRef(
289 interfaceNames
->elements
[i
], NULL
)))
290 goto fail_refThisName
;
293 /* Read in field count. */
295 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
296 inStream
, &fieldCount
)) || fieldCount
< 0)
297 goto fail_readFieldCount
;
299 /* Setup list to store fields in. */
301 if (sjme_error_is(error
= sjme_list_alloc(inPool
,
302 fieldCount
, &fields
, sjme_class_fieldInfo
, 0)) || fields
== NULL
)
303 goto fail_allocFields
;
304 result
->fields
= fields
;
306 /* Load in and process each field. */
307 for (i
= 0; i
< fieldCount
; i
++)
309 /* Parse each field. */
310 if (sjme_error_is(error
= sjme_class_parseField(
311 inStream
, result
->pool
,
312 &fields
->elements
[i
])) ||
313 fields
->elements
[i
] == NULL
)
314 goto fail_parseField
;
317 /* Read in method count. */
319 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
320 inStream
, &methodCount
)) || methodCount
< 0)
321 goto fail_readMethodCount
;
323 /* Setup list to store methods in. */
325 if (sjme_error_is(error
= sjme_list_alloc(inPool
,
326 methodCount
, &methods
, sjme_class_methodInfo
, 0)) || methods
== NULL
)
327 goto fail_allocMethods
;
328 result
->methods
= methods
;
330 /* Load in and process each method. */
331 for (i
= 0; i
< methodCount
; i
++)
333 /* Parse each method. */
334 if (sjme_error_is(error
= sjme_class_parseMethod(
335 inStream
, result
->pool
,
336 &methods
->elements
[i
])) ||
337 methods
->elements
[i
] == NULL
)
338 goto fail_parseMethod
;
341 /* Parse attributes. */
344 return SJME_ERROR_NOT_IMPLEMENTED
;
350 sjme_alloc_free(methods
);
352 result
->methods
= NULL
;
354 fail_readMethodCount
:
359 sjme_alloc_free(fields
);
361 result
->fields
= NULL
;
364 fail_allocInterfaceNames
:
365 if (interfaceNames
!= NULL
)
367 sjme_alloc_free(interfaceNames
);
368 interfaceNames
= NULL
;
369 result
->interfaceNames
= NULL
;
371 fail_readInterfaceCount
:
381 sjme_closeable_close(SJME_AS_CLOSEABLE(pool
));
393 sjme_closeable_close(SJME_AS_CLOSEABLE(result
));
394 return sjme_error_default(error
);
397 sjme_errorCode
sjme_class_parseConstantPool(
398 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
399 sjme_attrInNotNull sjme_stream_input inStream
,
400 sjme_attrInNotNull sjme_stringPool inStringPool
,
401 sjme_attrOutNotNull sjme_class_poolInfo
* outPool
)
403 sjme_errorCode error
;
407 sjme_list_sjme_class_poolEntry
* entries
;
408 sjme_class_poolEntry
* entry
;
409 sjme_class_poolEntry
* target
;
410 sjme_stringPool_string utf
;
411 sjme_class_poolInfo result
;
413 if (inPool
== NULL
|| inStream
== NULL
|| outPool
== NULL
||
414 inStringPool
== NULL
)
415 return SJME_ERROR_NULL_ARGUMENTS
;
417 /* Make sure we can actually allocate this. */
419 if (sjme_error_is(error
= sjme_alloc_weakNew(inPool
,
420 sizeof(*result
), NULL
, NULL
,
421 (sjme_pointer
*)&result
, NULL
)) || result
== NULL
)
422 goto fail_allocResult
;
425 if (sjme_error_is(error
= sjme_nvm_initCommon(
426 SJME_AS_NVM_COMMON(result
), SJME_NVM_STRUCT_POOL
)))
427 goto fail_initCommon
;
429 /* Read in pool count. */
431 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
432 inStream
, &count
)) || count
< 0)
435 /* Invalid pool size? */
436 if (count
< 0 || count
>= INT16_MAX
)
438 error
= SJME_ERROR_INVALID_CLASS_POOL_COUNT
;
442 /* Count up by one, since zero is included! */
445 /* Allocate resultant entries, where they will all go. */
447 if (sjme_error_is(error
= sjme_list_alloc(inPool
,
448 count
, &entries
, sjme_class_poolEntry
, 0)) || entries
== NULL
)
451 /* Read in all entries. */
452 /* This is a first pass since index items can refer to later entries. */
453 for (index
= 1; index
< count
- 1; index
++)
455 /* Which entry is being written? */
456 entry
= &entries
->elements
[index
];
460 if (sjme_error_is(error
= sjme_stream_inputReadValueJB(
461 inStream
, &tag
)) || tag
< 0)
465 sjme_message("TAG: %d", tag
);
470 /* Which tag is this? */
473 case SJME_CLASS_POOL_TYPE_DOUBLE
:
474 if (sjme_error_is(error
= sjme_stream_inputReadValueJI(
476 (sjme_jint
*)&entry
->constDouble
.value
.hi
)))
478 if (sjme_error_is(error
= sjme_stream_inputReadValueJI(
480 (sjme_jint
*)&entry
->constDouble
.value
.lo
)))
484 case SJME_CLASS_POOL_TYPE_CLASS
:
485 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
487 &entry
->classRef
.descriptorIndex
)))
492 case SJME_CLASS_POOL_TYPE_UTF
:
494 if (sjme_error_is(error
= sjme_stringPool_locateStream(
495 inStringPool
, inStream
, &utf
)) || utf
== NULL
)
499 sjme_message("Read UTF: %s",
502 /* Store and count up entry as we are using it now. */
503 entry
->utf
.utf
= utf
;
504 if (sjme_error_is(error
= sjme_alloc_weakRef(
510 sjme_todo("Impl? %d", tag
);
511 return SJME_ERROR_NOT_IMPLEMENTED
;
515 /* Second stage item linking. */
516 for (index
= 1; index
< count
- 1; index
++)
518 /* Which entry is being initialized? */
519 entry
= &entries
->elements
[index
];
521 /* Initialize accordingly. */
524 /* These are base elements that need no initialization. */
525 case SJME_CLASS_POOL_TYPE_UTF
:
526 case SJME_CLASS_POOL_TYPE_INTEGER
:
527 case SJME_CLASS_POOL_TYPE_FLOAT
:
528 case SJME_CLASS_POOL_TYPE_LONG
:
529 case SJME_CLASS_POOL_TYPE_DOUBLE
:
533 case SJME_CLASS_POOL_TYPE_CLASS
:
534 if (entry
->classRef
.descriptorIndex
<= 0 ||
535 entry
->classRef
.descriptorIndex
>= entries
->length
)
537 error
= SJME_ERROR_INVALID_CLASS_POOL_INDEX
;
541 /* Need to be a UTF string. */
542 target
= &entries
->elements
[entry
->classRef
.descriptorIndex
];
543 if (target
->type
!= SJME_CLASS_POOL_TYPE_UTF
)
545 error
= SJME_ERROR_WRONG_CLASS_POOL_INDEX_TYPE
;
549 /* Refer to it and count up, since we are using it. */
550 entry
->classRef
.descriptor
= target
->utf
.utf
;
551 if (sjme_error_is(error
= sjme_alloc_weakRef(
552 entry
->classRef
.descriptor
, NULL
)))
557 sjme_todo("Impl? %d", tag
);
558 return SJME_ERROR_NOT_IMPLEMENTED
;
563 result
->pool
= entries
;
567 return SJME_ERROR_NONE
;
574 sjme_alloc_free(entries
);
580 sjme_closeable_close(SJME_AS_CLOSEABLE(result
));
581 return sjme_error_default(error
);
584 sjme_errorCode
sjme_class_parseField(
585 sjme_attrInNotNull sjme_stream_input inStream
,
586 sjme_attrInNotNull sjme_class_poolInfo inConstPool
,
587 sjme_attrOutNotNull sjme_class_fieldInfo
* outField
)
589 if (inStream
== NULL
|| inConstPool
== NULL
|| outField
== NULL
)
590 return SJME_ERROR_NULL_ARGUMENTS
;
593 return SJME_ERROR_NOT_IMPLEMENTED
;
596 sjme_errorCode
sjme_class_parseMethod(
597 sjme_attrInNotNull sjme_stream_input inStream
,
598 sjme_attrInNotNull sjme_class_poolInfo inConstPool
,
599 sjme_attrInOutNotNull sjme_class_methodInfo
* outMethod
)
601 if (inStream
== NULL
|| inConstPool
== NULL
|| outMethod
== NULL
)
602 return SJME_ERROR_NULL_ARGUMENTS
;
605 return SJME_ERROR_NOT_IMPLEMENTED
;