Base for method reading.
[SquirrelJME.git] / nanocoat / src / classy.c
blobb25a6af73b6fd9d6055edcb5259eef07e2d639dd
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
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)
26 /** Public class. */
27 #define SJME_CLASS_ACC_PUBLIC INT16_C(0x0001)
29 /** Final class. */
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)
54 sjme_errorCode error;
55 sjme_jshort rawFlags;
57 if (inStream == NULL || outFlags == NULL)
58 return SJME_ERROR_NULL_ARGUMENTS;
60 /* Read in flags. */
61 rawFlags = -1;
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;
93 /* Success! */
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;
105 sjme_jshort index;
106 sjme_class_poolEntry* result;
108 if (inStream == NULL || inClassPool == NULL || outEntry == NULL)
109 return SJME_ERROR_NULL_ARGUMENTS;
111 /* Read in index. */
112 index = -1;
113 if (sjme_error_is(error = sjme_stream_inputReadValueJS(
114 inStream, &index)))
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;
126 /* Success! */
127 *outEntry = result;
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 ||
151 outClass == NULL)
152 return SJME_ERROR_NONE;
154 /* Make sure we can actually allocate the resultant class. */
155 result = NULL;
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;
161 /* Initialize. */
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. */
168 magic = INT32_MAX;
169 if (sjme_error_is(error = sjme_stream_inputReadValueJI(
170 inStream, &magic)))
171 goto fail_readMagic;
173 /* It must be valid! */
174 if (magic != SJME_CLASS_MAGIC)
176 error = SJME_ERROR_INVALID_CLASS_MAGIC;
177 goto fail_badMagic;
180 /* Read in version info. */
181 minor = INT16_MAX;
182 if (sjme_error_is(error = sjme_stream_inputReadValueJS(
183 inStream, &minor)))
184 goto fail_readMinor;
186 major = INT16_MAX;
187 if (sjme_error_is(error = sjme_stream_inputReadValueJS(
188 inStream, &major)))
189 goto fail_readMajor;
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;
203 /* Not valid. */
204 else
206 error = SJME_ERROR_INVALID_CLASS_VERSION;
207 goto fail_badVersion;
210 /* Set version. */
211 result->version = actualVersion;
213 /* Parse the constant pool. */
214 pool = NULL;
215 if (sjme_error_is(error = sjme_class_parseConstantPool(
216 inPool, inStream, inStringPool, &pool)) || pool == NULL)
217 goto fail_parsePool;
219 /* We are using this, so count it up. */
220 if (sjme_error_is(error = sjme_alloc_weakRef(pool, NULL)))
221 goto fail_countPool;
222 result->pool = pool;
224 /* Read in flags. */
225 if (sjme_error_is(error = sjme_class_readClassFlags(
226 inStream, &result->flags)))
227 goto fail_readFlags;
229 /* Read in this name. */
230 thisName = NULL;
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;
237 /* Reference it. */
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. */
244 superName = NULL;
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? */
261 interfaceCount = -1;
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++)
277 /* Read in name. */
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;
286 /* Reference it. */
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. */
294 fieldCount = -1;
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. */
300 fields = NULL;
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. */
318 methodCount = -1;
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. */
324 methods = NULL;
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. */
343 sjme_todo("Impl?");
344 return SJME_ERROR_NOT_IMPLEMENTED;
346 fail_parseMethod:
347 fail_allocMethods:
348 if (methods != NULL)
350 sjme_alloc_free(methods);
351 methods = NULL;
352 result->methods = NULL;
354 fail_readMethodCount:
355 fail_parseField:
356 fail_allocFields:
357 if (fields != NULL)
359 sjme_alloc_free(fields);
360 fields = NULL;
361 result->fields = NULL;
363 fail_readFieldCount:
364 fail_allocInterfaceNames:
365 if (interfaceNames != NULL)
367 sjme_alloc_free(interfaceNames);
368 interfaceNames = NULL;
369 result->interfaceNames = NULL;
371 fail_readInterfaceCount:
372 fail_refSuperName:
373 fail_readSuperName:
374 fail_refThisName:
375 fail_readThisName:
376 fail_readFlags:
377 fail_countPool:
378 fail_parsePool:
379 if (pool != NULL)
381 sjme_closeable_close(SJME_AS_CLOSEABLE(pool));
382 pool = NULL;
383 result->pool = NULL;
385 fail_badVersion:
386 fail_readMinor:
387 fail_readMajor:
388 fail_badMagic:
389 fail_readMagic:
390 fail_initResult:
391 fail_allocResult:
392 if (result != NULL)
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;
404 sjme_jshort count;
405 sjme_jint index;
406 sjme_jbyte tag;
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. */
418 result = NULL;
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;
424 /* Initialize it. */
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. */
430 count = -1;
431 if (sjme_error_is(error = sjme_stream_inputReadValueJS(
432 inStream, &count)) || count < 0)
433 goto fail_readCount;
435 /* Invalid pool size? */
436 if (count < 0 || count >= INT16_MAX)
438 error = SJME_ERROR_INVALID_CLASS_POOL_COUNT;
439 goto fail_poolCount;
442 /* Count up by one, since zero is included! */
443 count += 1;
445 /* Allocate resultant entries, where they will all go. */
446 entries = NULL;
447 if (sjme_error_is(error = sjme_list_alloc(inPool,
448 count, &entries, sjme_class_poolEntry, 0)) || entries == NULL)
449 goto fail_entryList;
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];
458 /* Read in tag. */
459 tag = -1;
460 if (sjme_error_is(error = sjme_stream_inputReadValueJB(
461 inStream, &tag)) || tag < 0)
462 goto fail_readTag;
464 /* Debug. */
465 sjme_message("TAG: %d", tag);
467 /* Set tag. */
468 entry->type = tag;
470 /* Which tag is this? */
471 switch (tag)
473 case SJME_CLASS_POOL_TYPE_DOUBLE:
474 if (sjme_error_is(error = sjme_stream_inputReadValueJI(
475 inStream,
476 (sjme_jint*)&entry->constDouble.value.hi)))
477 goto fail_readItem;
478 if (sjme_error_is(error = sjme_stream_inputReadValueJI(
479 inStream,
480 (sjme_jint*)&entry->constDouble.value.lo)))
481 goto fail_readItem;
482 break;
484 case SJME_CLASS_POOL_TYPE_CLASS:
485 if (sjme_error_is(error = sjme_stream_inputReadValueJS(
486 inStream,
487 &entry->classRef.descriptorIndex)))
488 goto fail_readItem;
489 break;
491 /* UTF String. */
492 case SJME_CLASS_POOL_TYPE_UTF:
493 utf = NULL;
494 if (sjme_error_is(error = sjme_stringPool_locateStream(
495 inStringPool, inStream, &utf)) || utf == NULL)
496 goto fail_readItem;
498 /* Debug. */
499 sjme_message("Read UTF: %s",
500 utf->chars);
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(
505 utf, NULL)))
506 goto fail_readItem;
507 break;
509 default:
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. */
522 switch (entry->type)
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:
530 break;
532 /* Class type. */
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;
538 goto fail_initItem;
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;
546 goto fail_initItem;
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)))
553 goto fail_initItem;
554 break;
556 default:
557 sjme_todo("Impl? %d", tag);
558 return SJME_ERROR_NOT_IMPLEMENTED;
562 /* Setup details. */
563 result->pool = entries;
565 /* Success! */
566 *outPool = result;
567 return SJME_ERROR_NONE;
569 fail_initItem:
570 fail_readItem:
571 fail_readTag:
572 fail_entryList:
573 if (entries != NULL)
574 sjme_alloc_free(entries);
575 fail_poolCount:
576 fail_readCount:
577 fail_initCommon:
578 fail_allocResult:
579 if (result != NULL)
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;
592 sjme_todo("Impl?");
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;
604 sjme_todo("Impl?");
605 return SJME_ERROR_NOT_IMPLEMENTED;