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
;
139 sjme_jshort major
, minor
;
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
;
146 if (inPool
== NULL
|| inStream
== NULL
|| inStringPool
== NULL
||
148 return SJME_ERROR_NONE
;
150 /* Make sure we can actually allocate the resultant class. */
152 if (sjme_error_is(error
= sjme_alloc_weakNew(inPool
,
153 sizeof(*result
), NULL
, NULL
,
154 (sjme_pointer
*)&result
, NULL
)) || result
== NULL
)
155 goto fail_allocResult
;
158 if (sjme_error_is(error
= sjme_nvm_initCommon(
159 SJME_AS_NVM_COMMON(result
),
160 SJME_NVM_STRUCT_CLASS_INFO
)))
161 goto fail_initResult
;
163 /* Read in magic number. */
165 if (sjme_error_is(error
= sjme_stream_inputReadValueJI(
169 /* It must be valid! */
170 if (magic
!= SJME_CLASS_MAGIC
)
172 error
= SJME_ERROR_INVALID_CLASS_MAGIC
;
176 /* Read in version info. */
178 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
183 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
187 /* Compose and find matching version. */
188 fullVersion
= (major
<< 16) | (minor
& 0xFFFF);
189 if (fullVersion
>= SJME_CLASS_CLDC_1_0
&&
190 fullVersion
<= SJME_CLASS_CLDC_1_0_MAX
)
191 actualVersion
= SJME_CLASS_CLDC_1_0
;
192 else if (fullVersion
>= SJME_CLASS_CLDC_1_1
&&
193 fullVersion
<= SJME_CLASS_CLDC_1_1_MAX
)
194 actualVersion
= SJME_CLASS_CLDC_1_1
;
195 else if (fullVersion
>= SJME_CLASS_CLDC_1_8
&&
196 fullVersion
<= SJME_CLASS_CLDC_1_8_MAX
)
197 actualVersion
= SJME_CLASS_CLDC_1_8
;
202 error
= SJME_ERROR_INVALID_CLASS_VERSION
;
203 goto fail_badVersion
;
206 /* Parse the constant pool. */
208 if (sjme_error_is(error
= sjme_class_parseConstantPool(
209 inPool
, inStream
, inStringPool
, &pool
)) || pool
== NULL
)
212 /* We are using this, so count it up. */
213 if (sjme_error_is(error
= sjme_alloc_weakRef(pool
, NULL
)))
218 if (sjme_error_is(error
= sjme_class_readClassFlags(
219 inStream
, &result
->flags
)))
222 /* Read in this name. */
224 if (sjme_error_is(error
= sjme_class_readPoolRefIndex(
225 inStream
, result
->pool
,
226 SJME_CLASS_POOL_TYPE_CLASS
,
227 SJME_JNI_FALSE
, &thisName
)) || thisName
== NULL
)
228 goto fail_readThisName
;
231 result
->name
= thisName
->classRef
.descriptor
;
232 if (sjme_error_is(error
= sjme_alloc_weakRef(
233 result
->name
, NULL
)))
234 goto fail_refThisName
;
236 /* Read in super name. */
238 if (sjme_error_is(error
= sjme_class_readPoolRefIndex(
239 inStream
, result
->pool
,
240 SJME_CLASS_POOL_TYPE_CLASS
,
241 SJME_JNI_TRUE
, &superName
)))
242 goto fail_readSuperName
;
244 /* Reference it, if valid. */
245 if (superName
!= NULL
)
247 result
->name
= thisName
->classRef
.descriptor
;
248 if (sjme_error_is(error
= sjme_alloc_weakRef(
249 result
->name
, NULL
)))
250 goto fail_refSuperName
;
254 return SJME_ERROR_NOT_IMPLEMENTED
;
265 sjme_closeable_close(SJME_AS_CLOSEABLE(pool
));
277 sjme_closeable_close(SJME_AS_CLOSEABLE(result
));
278 return sjme_error_default(error
);
281 sjme_errorCode
sjme_class_parseConstantPool(
282 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
283 sjme_attrInNotNull sjme_stream_input inStream
,
284 sjme_attrInNotNull sjme_stringPool inStringPool
,
285 sjme_attrOutNotNull sjme_class_poolInfo
* outPool
)
287 sjme_errorCode error
;
291 sjme_list_sjme_class_poolEntry
* entries
;
292 sjme_class_poolEntry
* entry
;
293 sjme_class_poolEntry
* target
;
294 sjme_stringPool_string utf
;
295 sjme_class_poolInfo result
;
297 if (inPool
== NULL
|| inStream
== NULL
|| outPool
== NULL
||
298 inStringPool
== NULL
)
299 return SJME_ERROR_NULL_ARGUMENTS
;
301 /* Make sure we can actually allocate this. */
303 if (sjme_error_is(error
= sjme_alloc_weakNew(inPool
,
304 sizeof(*result
), NULL
, NULL
,
305 (sjme_pointer
*)&result
, NULL
)) || result
== NULL
)
306 goto fail_allocResult
;
309 if (sjme_error_is(error
= sjme_nvm_initCommon(
310 SJME_AS_NVM_COMMON(result
), SJME_NVM_STRUCT_POOL
)))
311 goto fail_initCommon
;
313 /* Read in pool count. */
315 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
316 inStream
, &count
)) || count
< 0)
319 /* Invalid pool size? */
320 if (count
< 0 || count
>= INT16_MAX
)
322 error
= SJME_ERROR_INVALID_CLASS_POOL_COUNT
;
326 /* Count up by one, since zero is included! */
329 /* Allocate resultant entries, where they will all go. */
331 if (sjme_error_is(error
= sjme_list_alloc(inPool
,
332 count
, &entries
, sjme_class_poolEntry
, 0)) || entries
== NULL
)
335 /* Read in all entries. */
336 /* This is a first pass since index items can refer to later entries. */
337 for (index
= 1; index
< count
- 1; index
++)
339 /* Which entry is being written? */
340 entry
= &entries
->elements
[index
];
344 if (sjme_error_is(error
= sjme_stream_inputReadValueJB(
345 inStream
, &tag
)) || tag
< 0)
349 sjme_message("TAG: %d", tag
);
354 /* Which tag is this? */
357 case SJME_CLASS_POOL_TYPE_DOUBLE
:
358 if (sjme_error_is(error
= sjme_stream_inputReadValueJI(
360 (sjme_jint
*)&entry
->constDouble
.value
.hi
)))
362 if (sjme_error_is(error
= sjme_stream_inputReadValueJI(
364 (sjme_jint
*)&entry
->constDouble
.value
.lo
)))
368 case SJME_CLASS_POOL_TYPE_CLASS
:
369 if (sjme_error_is(error
= sjme_stream_inputReadValueJS(
371 &entry
->classRef
.descriptorIndex
)))
376 case SJME_CLASS_POOL_TYPE_UTF
:
378 if (sjme_error_is(error
= sjme_stringPool_locateStream(
379 inStringPool
, inStream
, &utf
)) || utf
== NULL
)
383 sjme_message("Read UTF: %s",
386 /* Store and count up entry as we are using it now. */
387 entry
->utf
.utf
= utf
;
388 if (sjme_error_is(error
= sjme_alloc_weakRef(
394 sjme_todo("Impl? %d", tag
);
395 return SJME_ERROR_NOT_IMPLEMENTED
;
399 /* Second stage item linking. */
400 for (index
= 1; index
< count
- 1; index
++)
402 /* Which entry is being initialized? */
403 entry
= &entries
->elements
[index
];
405 /* Initialize accordingly. */
408 /* These are base elements that need no initialization. */
409 case SJME_CLASS_POOL_TYPE_UTF
:
410 case SJME_CLASS_POOL_TYPE_INTEGER
:
411 case SJME_CLASS_POOL_TYPE_FLOAT
:
412 case SJME_CLASS_POOL_TYPE_LONG
:
413 case SJME_CLASS_POOL_TYPE_DOUBLE
:
417 case SJME_CLASS_POOL_TYPE_CLASS
:
418 if (entry
->classRef
.descriptorIndex
<= 0 ||
419 entry
->classRef
.descriptorIndex
>= entries
->length
)
421 error
= SJME_ERROR_INVALID_CLASS_POOL_INDEX
;
425 /* Need to be a UTF string. */
426 target
= &entries
->elements
[entry
->classRef
.descriptorIndex
];
427 if (target
->type
!= SJME_CLASS_POOL_TYPE_UTF
)
429 error
= SJME_ERROR_WRONG_CLASS_POOL_INDEX_TYPE
;
433 /* Refer to it and count up, since we are using it. */
434 entry
->classRef
.descriptor
= target
->utf
.utf
;
435 if (sjme_error_is(error
= sjme_alloc_weakRef(
436 entry
->classRef
.descriptor
, NULL
)))
441 sjme_todo("Impl? %d", tag
);
442 return SJME_ERROR_NOT_IMPLEMENTED
;
447 result
->pool
= entries
;
451 return SJME_ERROR_NONE
;
458 sjme_alloc_free(entries
);
464 sjme_closeable_close(SJME_AS_CLOSEABLE(result
));
465 return sjme_error_default(error
);