mirror of
https://github.com/corda/corda.git
synced 2025-01-07 13:38:47 +00:00
implement ClassLoader.resolveClass and ensure class is linked in e.g. Class.getMethods; minor bugfixes
This commit is contained in:
parent
c27aa559e5
commit
c4edabdc02
@ -32,10 +32,10 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
private static final int PrimitiveFlag = 1 << 5;
|
private static final int PrimitiveFlag = 1 << 5;
|
||||||
|
|
||||||
private short flags;
|
private short flags;
|
||||||
private byte vmFlags;
|
private short vmFlags;
|
||||||
private byte arrayDimensions;
|
|
||||||
private short fixedSize;
|
private short fixedSize;
|
||||||
private short arrayElementSize;
|
private byte arrayElementSize;
|
||||||
|
private byte arrayDimensions;
|
||||||
private int[] objectMask;
|
private int[] objectMask;
|
||||||
private byte[] name;
|
private byte[] name;
|
||||||
private Class super_;
|
private Class super_;
|
||||||
@ -151,6 +151,7 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
loader = Class.class.loader;
|
loader = Class.class.loader;
|
||||||
}
|
}
|
||||||
Class c = loader.loadClass(name);
|
Class c = loader.loadClass(name);
|
||||||
|
c.link(loader);
|
||||||
if (initialize) {
|
if (initialize) {
|
||||||
c.initialize();
|
c.initialize();
|
||||||
}
|
}
|
||||||
@ -159,6 +160,8 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
|
|
||||||
private static native Class primitiveClass(char name);
|
private static native Class primitiveClass(char name);
|
||||||
|
|
||||||
|
private native void link(ClassLoader loader);
|
||||||
|
|
||||||
private native void initialize();
|
private native void initialize();
|
||||||
|
|
||||||
public static Class forCanonicalName(String name) {
|
public static Class forCanonicalName(String name) {
|
||||||
@ -215,6 +218,8 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
|
|
||||||
private Field findField(String name) {
|
private Field findField(String name) {
|
||||||
if (fieldTable != null) {
|
if (fieldTable != null) {
|
||||||
|
link(loader);
|
||||||
|
|
||||||
for (int i = 0; i < fieldTable.length; ++i) {
|
for (int i = 0; i < fieldTable.length; ++i) {
|
||||||
if (fieldTable[i].getName().equals(name)) {
|
if (fieldTable[i].getName().equals(name)) {
|
||||||
return fieldTable[i];
|
return fieldTable[i];
|
||||||
@ -258,8 +263,12 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
|
|
||||||
private Method findMethod(String name, Class[] parameterTypes) {
|
private Method findMethod(String name, Class[] parameterTypes) {
|
||||||
if (methodTable != null) {
|
if (methodTable != null) {
|
||||||
if (parameterTypes == null)
|
link(loader);
|
||||||
|
|
||||||
|
if (parameterTypes == null) {
|
||||||
parameterTypes = new Class[0];
|
parameterTypes = new Class[0];
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < methodTable.length; ++i) {
|
for (int i = 0; i < methodTable.length; ++i) {
|
||||||
if (methodTable[i].getName().equals(name)
|
if (methodTable[i].getName().equals(name)
|
||||||
&& match(parameterTypes, methodTable[i].getParameterTypes()))
|
&& match(parameterTypes, methodTable[i].getParameterTypes()))
|
||||||
@ -348,6 +357,8 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
public Constructor[] getDeclaredConstructors() {
|
public Constructor[] getDeclaredConstructors() {
|
||||||
Constructor[] array = new Constructor[countConstructors(false)];
|
Constructor[] array = new Constructor[countConstructors(false)];
|
||||||
if (methodTable != null) {
|
if (methodTable != null) {
|
||||||
|
link(loader);
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (int i = 0; i < methodTable.length; ++i) {
|
for (int i = 0; i < methodTable.length; ++i) {
|
||||||
if (methodTable[i].getName().equals("<init>")) {
|
if (methodTable[i].getName().equals("<init>")) {
|
||||||
@ -362,6 +373,8 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
public Constructor[] getConstructors() {
|
public Constructor[] getConstructors() {
|
||||||
Constructor[] array = new Constructor[countConstructors(true)];
|
Constructor[] array = new Constructor[countConstructors(true)];
|
||||||
if (methodTable != null) {
|
if (methodTable != null) {
|
||||||
|
link(loader);
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (int i = 0; i < methodTable.length; ++i) {
|
for (int i = 0; i < methodTable.length; ++i) {
|
||||||
if (((methodTable[i].getModifiers() & Modifier.PUBLIC) != 0)
|
if (((methodTable[i].getModifiers() & Modifier.PUBLIC) != 0)
|
||||||
@ -400,6 +413,8 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
public Field[] getFields() {
|
public Field[] getFields() {
|
||||||
Field[] array = new Field[countPublicFields()];
|
Field[] array = new Field[countPublicFields()];
|
||||||
if (fieldTable != null) {
|
if (fieldTable != null) {
|
||||||
|
link(loader);
|
||||||
|
|
||||||
int ai = 0;
|
int ai = 0;
|
||||||
for (int i = 0; i < fieldTable.length; ++i) {
|
for (int i = 0; i < fieldTable.length; ++i) {
|
||||||
if (((fieldTable[i].getModifiers() & Modifier.PUBLIC)) != 0) {
|
if (((fieldTable[i].getModifiers() & Modifier.PUBLIC)) != 0) {
|
||||||
@ -428,6 +443,8 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
public Method[] getDeclaredMethods() {
|
public Method[] getDeclaredMethods() {
|
||||||
Method[] array = new Method[countMethods(false)];
|
Method[] array = new Method[countMethods(false)];
|
||||||
if (methodTable != null) {
|
if (methodTable != null) {
|
||||||
|
link(loader);
|
||||||
|
|
||||||
int ai = 0;
|
int ai = 0;
|
||||||
for (int i = 0; i < methodTable.length; ++i) {
|
for (int i = 0; i < methodTable.length; ++i) {
|
||||||
if (! methodTable[i].getName().startsWith("<")) {
|
if (! methodTable[i].getName().startsWith("<")) {
|
||||||
@ -442,6 +459,8 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
public Method[] getMethods() {
|
public Method[] getMethods() {
|
||||||
Method[] array = new Method[countMethods(true)];
|
Method[] array = new Method[countMethods(true)];
|
||||||
if (methodTable != null) {
|
if (methodTable != null) {
|
||||||
|
link(loader);
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (int i = 0; i < methodTable.length; ++i) {
|
for (int i = 0; i < methodTable.length; ++i) {
|
||||||
if (((methodTable[i].getModifiers() & Modifier.PUBLIC) != 0)
|
if (((methodTable[i].getModifiers() & Modifier.PUBLIC) != 0)
|
||||||
@ -457,6 +476,8 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
|
|
||||||
public Class[] getInterfaces() {
|
public Class[] getInterfaces() {
|
||||||
if (interfaceTable != null) {
|
if (interfaceTable != null) {
|
||||||
|
link(loader);
|
||||||
|
|
||||||
Class[] array = new Class[interfaceTable.length / 2];
|
Class[] array = new Class[interfaceTable.length / 2];
|
||||||
for (int i = 0; i < array.length; ++i) {
|
for (int i = 0; i < array.length; ++i) {
|
||||||
array[i] = (Class) interfaceTable[i * 2];
|
array[i] = (Class) interfaceTable[i * 2];
|
||||||
@ -496,7 +517,7 @@ public final class Class <T> implements Type, GenericDeclaration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isArray() {
|
public boolean isArray() {
|
||||||
return this != Class.class && arrayElementSize != 0;
|
return arrayDimensions != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInstance(Object o) {
|
public boolean isInstance(Object o) {
|
||||||
|
@ -83,9 +83,7 @@ public abstract class ClassLoader {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void resolveClass(Class c) {
|
protected native void resolveClass(Class c);
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassLoader getParent() {
|
private ClassLoader getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
|
@ -199,6 +199,16 @@ Avian_java_lang_ClassLoader_defineClass
|
|||||||
return reinterpret_cast<int64_t>(c);
|
return reinterpret_cast<int64_t>(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
Avian_java_lang_ClassLoader_resolveClass
|
||||||
|
(Thread* t, object, uintptr_t* arguments)
|
||||||
|
{
|
||||||
|
object loader = reinterpret_cast<object>(arguments[0]);
|
||||||
|
object class_ = reinterpret_cast<object>(arguments[1]);
|
||||||
|
|
||||||
|
linkClass(t, loader, class_);
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT int64_t JNICALL
|
extern "C" JNIEXPORT int64_t JNICALL
|
||||||
Avian_avian_SystemClassLoader_findLoadedClass
|
Avian_avian_SystemClassLoader_findLoadedClass
|
||||||
(Thread* t, object, uintptr_t* arguments)
|
(Thread* t, object, uintptr_t* arguments)
|
||||||
@ -291,6 +301,16 @@ Avian_java_lang_Class_initialize
|
|||||||
initClass(t, this_);
|
initClass(t, this_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
Avian_java_lang_Class_link
|
||||||
|
(Thread* t, object, uintptr_t* arguments)
|
||||||
|
{
|
||||||
|
object this_ = reinterpret_cast<object>(arguments[0]);
|
||||||
|
object loader = reinterpret_cast<object>(arguments[1]);
|
||||||
|
|
||||||
|
linkClass(t, loader, this_);
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT int64_t JNICALL
|
extern "C" JNIEXPORT int64_t JNICALL
|
||||||
Avian_java_lang_Class_isAssignableFrom
|
Avian_java_lang_Class_isAssignableFrom
|
||||||
(Thread* t, object, uintptr_t* arguments)
|
(Thread* t, object, uintptr_t* arguments)
|
||||||
|
@ -2511,7 +2511,7 @@ bool
|
|||||||
needsReturnBarrier(MyThread* t, object method)
|
needsReturnBarrier(MyThread* t, object method)
|
||||||
{
|
{
|
||||||
return (methodFlags(t, method) & ConstructorFlag)
|
return (methodFlags(t, method) & ConstructorFlag)
|
||||||
and (classFlags(t, methodClass(t, method)) & HasFinalMemberFlag);
|
and (classVmFlags(t, methodClass(t, method)) & HasFinalMemberFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -3746,7 +3746,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
|||||||
if (singletonIsObject(t, pool, index - 1)) {
|
if (singletonIsObject(t, pool, index - 1)) {
|
||||||
object v = singletonObject(t, pool, index - 1);
|
object v = singletonObject(t, pool, index - 1);
|
||||||
if (objectClass(t, v)
|
if (objectClass(t, v)
|
||||||
== arrayBody(t, t->m->types, Machine::ByteArrayType))
|
== arrayBody(t, t->m->types, Machine::ReferenceType))
|
||||||
{
|
{
|
||||||
object class_ = resolveClassInPool(t, context->method, index - 1);
|
object class_ = resolveClassInPool(t, context->method, index - 1);
|
||||||
if (UNLIKELY(t->exception)) return;
|
if (UNLIKELY(t->exception)) return;
|
||||||
@ -6444,10 +6444,10 @@ class MyProcessor: public Processor {
|
|||||||
virtual object
|
virtual object
|
||||||
makeClass(vm::Thread* t,
|
makeClass(vm::Thread* t,
|
||||||
uint16_t flags,
|
uint16_t flags,
|
||||||
uint8_t vmFlags,
|
uint16_t vmFlags,
|
||||||
uint8_t arrayDimensions,
|
|
||||||
uint16_t fixedSize,
|
uint16_t fixedSize,
|
||||||
uint16_t arrayElementSize,
|
uint8_t arrayElementSize,
|
||||||
|
uint8_t arrayDimensions,
|
||||||
object objectMask,
|
object objectMask,
|
||||||
object name,
|
object name,
|
||||||
object super,
|
object super,
|
||||||
@ -6460,7 +6460,7 @@ class MyProcessor: public Processor {
|
|||||||
unsigned vtableLength)
|
unsigned vtableLength)
|
||||||
{
|
{
|
||||||
return vm::makeClass
|
return vm::makeClass
|
||||||
(t, flags, vmFlags, arrayDimensions, fixedSize, arrayElementSize,
|
(t, flags, vmFlags, fixedSize, arrayElementSize, arrayDimensions,
|
||||||
objectMask, name, super, interfaceTable, virtualTable, fieldTable,
|
objectMask, name, super, interfaceTable, virtualTable, fieldTable,
|
||||||
methodTable, staticTable, loader, vtableLength);
|
methodTable, staticTable, loader, vtableLength);
|
||||||
}
|
}
|
||||||
|
@ -2246,7 +2246,7 @@ interpret(Thread* t)
|
|||||||
if (singletonIsObject(t, pool, index - 1)) {
|
if (singletonIsObject(t, pool, index - 1)) {
|
||||||
object v = singletonObject(t, pool, index - 1);
|
object v = singletonObject(t, pool, index - 1);
|
||||||
if (objectClass(t, v)
|
if (objectClass(t, v)
|
||||||
== arrayBody(t, t->m->types, Machine::ByteArrayType))
|
== arrayBody(t, t->m->types, Machine::ReferenceType))
|
||||||
{
|
{
|
||||||
object class_ = resolveClassInPool(t, pool, index - 1);
|
object class_ = resolveClassInPool(t, pool, index - 1);
|
||||||
if (UNLIKELY(exception)) goto throw_;
|
if (UNLIKELY(exception)) goto throw_;
|
||||||
@ -2724,7 +2724,7 @@ interpret(Thread* t)
|
|||||||
case return_: {
|
case return_: {
|
||||||
object method = frameMethod(t, frame);
|
object method = frameMethod(t, frame);
|
||||||
if ((methodFlags(t, method) & ConstructorFlag)
|
if ((methodFlags(t, method) & ConstructorFlag)
|
||||||
and (classFlags(t, methodClass(t, method)) & HasFinalMemberFlag))
|
and (classVmFlags(t, methodClass(t, method)) & HasFinalMemberFlag))
|
||||||
{
|
{
|
||||||
storeStoreMemoryBarrier();
|
storeStoreMemoryBarrier();
|
||||||
}
|
}
|
||||||
@ -3100,10 +3100,10 @@ class MyProcessor: public Processor {
|
|||||||
virtual object
|
virtual object
|
||||||
makeClass(vm::Thread* t,
|
makeClass(vm::Thread* t,
|
||||||
uint16_t flags,
|
uint16_t flags,
|
||||||
uint8_t vmFlags,
|
uint16_t vmFlags,
|
||||||
uint8_t arrayDimensions,
|
|
||||||
uint16_t fixedSize,
|
uint16_t fixedSize,
|
||||||
uint16_t arrayElementSize,
|
uint8_t arrayElementSize,
|
||||||
|
uint8_t arrayDimensions,
|
||||||
object objectMask,
|
object objectMask,
|
||||||
object name,
|
object name,
|
||||||
object super,
|
object super,
|
||||||
@ -3116,7 +3116,7 @@ class MyProcessor: public Processor {
|
|||||||
unsigned vtableLength UNUSED)
|
unsigned vtableLength UNUSED)
|
||||||
{
|
{
|
||||||
return vm::makeClass
|
return vm::makeClass
|
||||||
(t, flags, vmFlags, arrayDimensions, fixedSize, arrayElementSize,
|
(t, flags, vmFlags, fixedSize, arrayElementSize, arrayDimensions,
|
||||||
objectMask, name, super, interfaceTable, virtualTable, fieldTable,
|
objectMask, name, super, interfaceTable, virtualTable, fieldTable,
|
||||||
methodTable, staticTable, loader, 0);
|
methodTable, staticTable, loader, 0);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ DetachCurrentThread(Machine* m)
|
|||||||
{
|
{
|
||||||
Thread* t = static_cast<Thread*>(m->localThread->get());
|
Thread* t = static_cast<Thread*>(m->localThread->get());
|
||||||
if (t) {
|
if (t) {
|
||||||
|
m->localThread->set(0);
|
||||||
t->exit();
|
t->exit();
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
174
src/machine.cpp
174
src/machine.cpp
@ -542,6 +542,50 @@ makeByteArray(Thread* t, const char* format, va_list a)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
resolveSpec(Thread* t, object loader, object spec, unsigned offset)
|
||||||
|
{
|
||||||
|
int8_t* s = &byteArrayBody(t, spec, offset);
|
||||||
|
unsigned result;
|
||||||
|
switch (*s) {
|
||||||
|
case 'L':
|
||||||
|
++ offset;
|
||||||
|
while (*s and *s != ';') ++ s;
|
||||||
|
result = s + 1 - &byteArrayBody(t, spec, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
while (*s == '[') ++ s;
|
||||||
|
switch (*s) {
|
||||||
|
case 'L':
|
||||||
|
while (*s and *s != ';') ++ s;
|
||||||
|
++ s;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
++ s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = s - &byteArrayBody(t, spec, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return offset + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROTECT(t, spec);
|
||||||
|
|
||||||
|
unsigned length = s - &byteArrayBody(t, spec, offset);
|
||||||
|
|
||||||
|
object name = makeByteArray(t, length + 1);
|
||||||
|
memcpy(&byteArrayBody(t, name, 0),
|
||||||
|
&byteArrayBody(t, spec, offset),
|
||||||
|
length);
|
||||||
|
resolveClass(t, loader, name);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
readByte(Stream& s, unsigned* value)
|
readByte(Stream& s, unsigned* value)
|
||||||
{
|
{
|
||||||
@ -706,7 +750,7 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i)
|
|||||||
unsigned si = s.read2() - 1;
|
unsigned si = s.read2() - 1;
|
||||||
parsePoolEntry(t, s, index, pool, si);
|
parsePoolEntry(t, s, index, pool, si);
|
||||||
|
|
||||||
object value = singletonObject(t, pool, si);
|
object value = makeReference(t, 0, singletonObject(t, pool, si), 0);
|
||||||
set(t, pool, SingletonBody + (i * BytesPerWord), value);
|
set(t, pool, SingletonBody + (i * BytesPerWord), value);
|
||||||
}
|
}
|
||||||
} return 1;
|
} return 1;
|
||||||
@ -749,7 +793,7 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i)
|
|||||||
parsePoolEntry(t, s, index, pool, ci);
|
parsePoolEntry(t, s, index, pool, ci);
|
||||||
parsePoolEntry(t, s, index, pool, nti);
|
parsePoolEntry(t, s, index, pool, nti);
|
||||||
|
|
||||||
object class_ = singletonObject(t, pool, ci);
|
object class_ = referenceName(t, singletonObject(t, pool, ci));
|
||||||
object nameAndType = singletonObject(t, pool, nti);
|
object nameAndType = singletonObject(t, pool, nti);
|
||||||
object value = makeReference
|
object value = makeReference
|
||||||
(t, class_, pairFirst(t, nameAndType), pairSecond(t, nameAndType));
|
(t, class_, pairFirst(t, nameAndType), pairSecond(t, nameAndType));
|
||||||
@ -861,7 +905,7 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool)
|
|||||||
|
|
||||||
unsigned count = s.read2();
|
unsigned count = s.read2();
|
||||||
for (unsigned i = 0; i < count; ++i) {
|
for (unsigned i = 0; i < count; ++i) {
|
||||||
object name = singletonObject(t, pool, s.read2() - 1);
|
object name = referenceName(t, singletonObject(t, pool, s.read2() - 1));
|
||||||
PROTECT(t, name);
|
PROTECT(t, name);
|
||||||
|
|
||||||
object interface = resolveClass(t, classLoader(t, class_), name);
|
object interface = resolveClass(t, classLoader(t, class_), name);
|
||||||
@ -983,7 +1027,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool)
|
|||||||
staticTypes[staticCount++] = code;
|
staticTypes[staticCount++] = code;
|
||||||
} else {
|
} else {
|
||||||
if (flags & ACC_FINAL) {
|
if (flags & ACC_FINAL) {
|
||||||
classFlags(t, class_) |= HasFinalMemberFlag;
|
classVmFlags(t, class_) |= HasFinalMemberFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned excess = (memberOffset % fieldSize(t, code)) % BytesPerWord;
|
unsigned excess = (memberOffset % fieldSize(t, code)) % BytesPerWord;
|
||||||
@ -1565,9 +1609,9 @@ makeArrayClass(Thread* t, object loader, unsigned dimensions, object spec,
|
|||||||
(t,
|
(t,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
dimensions,
|
|
||||||
2 * BytesPerWord,
|
2 * BytesPerWord,
|
||||||
BytesPerWord,
|
BytesPerWord,
|
||||||
|
dimensions,
|
||||||
classObjectMask(t, arrayBody(t, t->m->types, Machine::ArrayType)),
|
classObjectMask(t, arrayBody(t, t->m->types, Machine::ArrayType)),
|
||||||
spec,
|
spec,
|
||||||
arrayBody(t, t->m->types, Machine::JobjectType),
|
arrayBody(t, t->m->types, Machine::JobjectType),
|
||||||
@ -1704,7 +1748,7 @@ bootClass(Thread* t, Machine::Type type, int superType, uint32_t objectMask,
|
|||||||
super = (superType >= 0 ? arrayBody(t, t->m->types, superType) : 0);
|
super = (superType >= 0 ? arrayBody(t, t->m->types, superType) : 0);
|
||||||
|
|
||||||
object class_ = t->m->processor->makeClass
|
object class_ = t->m->processor->makeClass
|
||||||
(t, 0, BootstrapFlag, 0, fixedSize, arrayElementSize, mask, 0, super, 0, 0,
|
(t, 0, BootstrapFlag, fixedSize, arrayElementSize, 0, mask, 0, super, 0, 0,
|
||||||
0, 0, 0, t->m->loader, vtableLength);
|
0, 0, 0, t->m->loader, vtableLength);
|
||||||
|
|
||||||
set(t, t->m->types, ArrayBody + (type * BytesPerWord), class_);
|
set(t, t->m->types, ArrayBody + (type * BytesPerWord), class_);
|
||||||
@ -1773,10 +1817,10 @@ boot(Thread* t)
|
|||||||
|
|
||||||
m->unsafe = false;
|
m->unsafe = false;
|
||||||
|
|
||||||
classFlags(t, arrayBody(t, m->types, Machine::SingletonType))
|
classVmFlags(t, arrayBody(t, m->types, Machine::SingletonType))
|
||||||
|= SingletonFlag;
|
|= SingletonFlag;
|
||||||
|
|
||||||
classFlags(t, arrayBody(t, m->types, Machine::ContinuationType))
|
classVmFlags(t, arrayBody(t, m->types, Machine::ContinuationType))
|
||||||
|= ContinuationFlag;
|
|= ContinuationFlag;
|
||||||
|
|
||||||
classVmFlags(t, arrayBody(t, m->types, Machine::JreferenceType))
|
classVmFlags(t, arrayBody(t, m->types, Machine::JreferenceType))
|
||||||
@ -2074,8 +2118,6 @@ Thread::init()
|
|||||||
|
|
||||||
m->localThread->set(this);
|
m->localThread->set(this);
|
||||||
} else {
|
} else {
|
||||||
assert(this, javaThread);
|
|
||||||
|
|
||||||
peer = parent->child;
|
peer = parent->child;
|
||||||
parent->child = this;
|
parent->child = this;
|
||||||
}
|
}
|
||||||
@ -2083,11 +2125,16 @@ Thread::init()
|
|||||||
if (javaThread) {
|
if (javaThread) {
|
||||||
threadPeer(this, javaThread) = reinterpret_cast<jlong>(this);
|
threadPeer(this, javaThread) = reinterpret_cast<jlong>(this);
|
||||||
} else {
|
} else {
|
||||||
|
object group;
|
||||||
|
if (parent) {
|
||||||
|
group = threadGroup(this, parent->javaThread);
|
||||||
|
} else {
|
||||||
|
group = makeThreadGroup(this, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
const unsigned NewState = 0;
|
const unsigned NewState = 0;
|
||||||
const unsigned NormalPriority = 5;
|
const unsigned NormalPriority = 5;
|
||||||
|
|
||||||
object group = makeThreadGroup(this, 0, 0);
|
|
||||||
|
|
||||||
this->javaThread = makeThread
|
this->javaThread = makeThread
|
||||||
(this, reinterpret_cast<int64_t>(this), 0, 0, NewState, NormalPriority,
|
(this, reinterpret_cast<int64_t>(this), 0, 0, NewState, NormalPriority,
|
||||||
0, 0, 0, m->loader, 0, 0, group);
|
0, 0, 0, m->loader, 0, 0, group);
|
||||||
@ -2663,11 +2710,12 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size)
|
|||||||
object class_ = makeClass(t,
|
object class_ = makeClass(t,
|
||||||
flags,
|
flags,
|
||||||
0, // VM flags
|
0, // VM flags
|
||||||
0, // array dimensions
|
|
||||||
0, // fixed size
|
0, // fixed size
|
||||||
0, // array size
|
0, // array size
|
||||||
|
0, // array dimensions
|
||||||
0, // object mask
|
0, // object mask
|
||||||
singletonObject(t, pool, name - 1),
|
referenceName
|
||||||
|
(t, singletonObject(t, pool, name - 1)),
|
||||||
0, // super
|
0, // super
|
||||||
0, // interfaces
|
0, // interfaces
|
||||||
0, // vtable
|
0, // vtable
|
||||||
@ -2680,7 +2728,8 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size)
|
|||||||
|
|
||||||
unsigned super = s.read2();
|
unsigned super = s.read2();
|
||||||
if (super) {
|
if (super) {
|
||||||
object sc = resolveClass(t, loader, singletonObject(t, pool, super - 1));
|
object sc = resolveClass
|
||||||
|
(t, loader, referenceName(t, singletonObject(t, pool, super - 1)));
|
||||||
if (UNLIKELY(t->exception)) return 0;
|
if (UNLIKELY(t->exception)) return 0;
|
||||||
|
|
||||||
set(t, class_, ClassSuper, sc);
|
set(t, class_, ClassSuper, sc);
|
||||||
@ -2706,9 +2755,9 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size)
|
|||||||
(t,
|
(t,
|
||||||
classFlags(t, class_),
|
classFlags(t, class_),
|
||||||
classVmFlags(t, class_),
|
classVmFlags(t, class_),
|
||||||
classArrayDimensions(t, class_),
|
|
||||||
classFixedSize(t, class_),
|
classFixedSize(t, class_),
|
||||||
classArrayElementSize(t, class_),
|
classArrayElementSize(t, class_),
|
||||||
|
classArrayDimensions(t, class_),
|
||||||
classObjectMask(t, class_),
|
classObjectMask(t, class_),
|
||||||
className(t, class_),
|
className(t, class_),
|
||||||
classSuper(t, class_),
|
classSuper(t, class_),
|
||||||
@ -2867,6 +2916,95 @@ resolveClass(Thread* t, object loader, object spec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
linkClass(Thread* t, object loader, object class_)
|
||||||
|
{
|
||||||
|
PROTECT(t, loader);
|
||||||
|
PROTECT(t, class_);
|
||||||
|
|
||||||
|
ACQUIRE(t, t->m->classLock);
|
||||||
|
|
||||||
|
if ((classVmFlags(t, class_) & LinkFlag) == 0) {
|
||||||
|
if (classSuper(t, class_)) {
|
||||||
|
linkClass(t, loader, classSuper(t, class_));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classInterfaceTable(t, class_)) {
|
||||||
|
unsigned increment = 2;
|
||||||
|
if (classFlags(t, class_) & ACC_INTERFACE) {
|
||||||
|
increment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < arrayLength(t, classInterfaceTable(t, class_));
|
||||||
|
i += increment)
|
||||||
|
{
|
||||||
|
linkClass(t, loader, arrayBody(t, classInterfaceTable(t, class_), i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classMethodTable(t, class_)) {
|
||||||
|
bool resolvedPool = false;
|
||||||
|
for (unsigned i = 0;
|
||||||
|
i < arrayLength(t, classMethodTable(t, class_)); ++i)
|
||||||
|
{
|
||||||
|
object method = arrayBody(t, classMethodTable(t, class_), i);
|
||||||
|
PROTECT(t, method);
|
||||||
|
|
||||||
|
object code = methodCode(t, method);
|
||||||
|
if ((not resolvedPool)
|
||||||
|
and code
|
||||||
|
and codePool(t, code)
|
||||||
|
and objectClass(t, codePool(t, code))
|
||||||
|
== arrayBody(t, t->m->types, Machine::SingletonType))
|
||||||
|
{
|
||||||
|
object pool = codePool(t, code);
|
||||||
|
PROTECT(t, pool);
|
||||||
|
unsigned count = singletonCount(t, pool);
|
||||||
|
for (unsigned j = 0; j < count; ++j) {
|
||||||
|
if (singletonIsObject(t, pool, j)) {
|
||||||
|
object entry = singletonObject(t, pool, j);
|
||||||
|
if (objectClass(t, entry)
|
||||||
|
== arrayBody(t, t->m->types, Machine::ReferenceType))
|
||||||
|
{
|
||||||
|
if (referenceSpec(t, entry) == 0) {
|
||||||
|
resolveClassInPool(t, loader, method, j);
|
||||||
|
} else if (byteArrayBody(t, referenceSpec(t, entry), 0) == '(')
|
||||||
|
{
|
||||||
|
resolveMethod(t, loader, method, j);
|
||||||
|
} else {
|
||||||
|
resolveField(t, loader, method, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UNLIKELY(t->exception)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedPool = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
object spec = methodSpec(t, method);
|
||||||
|
PROTECT(t, spec);
|
||||||
|
for (unsigned j = 1; j < byteArrayLength(t, spec);) {
|
||||||
|
j = resolveSpec(t, loader, spec, j);
|
||||||
|
if (UNLIKELY(t->exception)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classFieldTable(t, class_)) {
|
||||||
|
for (unsigned i = 0; i < arrayLength(t, classFieldTable(t, class_)); ++i)
|
||||||
|
{
|
||||||
|
resolveSpec(t, loader, fieldSpec
|
||||||
|
(t, arrayBody(t, classFieldTable(t, class_), i)), 0);
|
||||||
|
if (UNLIKELY(t->exception)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classVmFlags(t, class_) |= LinkFlag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object
|
object
|
||||||
resolveMethod(Thread* t, object class_, const char* methodName,
|
resolveMethod(Thread* t, object class_, const char* methodName,
|
||||||
const char* methodSpec)
|
const char* methodSpec)
|
||||||
@ -3286,7 +3424,7 @@ walk(Thread* t, Heap::Walker* w, object o, unsigned start)
|
|||||||
intArrayLength(t, objectMask) * 4);
|
intArrayLength(t, objectMask) * 4);
|
||||||
|
|
||||||
more = ::walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start);
|
more = ::walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start);
|
||||||
} else if (classFlags(t, class_) & SingletonFlag) {
|
} else if (classVmFlags(t, class_) & SingletonFlag) {
|
||||||
unsigned length = singletonLength(t, o);
|
unsigned length = singletonLength(t, o);
|
||||||
if (length) {
|
if (length) {
|
||||||
more = ::walk(t, w, singletonMask(t, o),
|
more = ::walk(t, w, singletonMask(t, o),
|
||||||
@ -3298,7 +3436,7 @@ walk(Thread* t, Heap::Walker* w, object o, unsigned start)
|
|||||||
more = w->visit(0);
|
more = w->visit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (more and classFlags(t, class_) & ContinuationFlag) {
|
if (more and classVmFlags(t, class_) & ContinuationFlag) {
|
||||||
t->m->processor->walkContinuationBody(t, w, o, start);
|
t->m->processor->walkContinuationBody(t, w, o, start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
109
src/machine.h
109
src/machine.h
@ -74,12 +74,6 @@ enum StackTag {
|
|||||||
const int NativeLine = -1;
|
const int NativeLine = -1;
|
||||||
const int UnknownLine = -2;
|
const int UnknownLine = -2;
|
||||||
|
|
||||||
// class flags (note that we must be careful not to overlap the
|
|
||||||
// standard ACC_* flags):
|
|
||||||
const unsigned HasFinalMemberFlag = 1 << 13;
|
|
||||||
const unsigned SingletonFlag = 1 << 14;
|
|
||||||
const unsigned ContinuationFlag = 1 << 15;
|
|
||||||
|
|
||||||
// class vmFlags:
|
// class vmFlags:
|
||||||
const unsigned ReferenceFlag = 1 << 0;
|
const unsigned ReferenceFlag = 1 << 0;
|
||||||
const unsigned WeakReferenceFlag = 1 << 1;
|
const unsigned WeakReferenceFlag = 1 << 1;
|
||||||
@ -89,6 +83,10 @@ const unsigned InitErrorFlag = 1 << 4;
|
|||||||
const unsigned PrimitiveFlag = 1 << 5;
|
const unsigned PrimitiveFlag = 1 << 5;
|
||||||
const unsigned BootstrapFlag = 1 << 6;
|
const unsigned BootstrapFlag = 1 << 6;
|
||||||
const unsigned HasFinalizerFlag = 1 << 7;
|
const unsigned HasFinalizerFlag = 1 << 7;
|
||||||
|
const unsigned LinkFlag = 1 << 8;
|
||||||
|
const unsigned HasFinalMemberFlag = 1 << 9;
|
||||||
|
const unsigned SingletonFlag = 1 << 10;
|
||||||
|
const unsigned ContinuationFlag = 1 << 11;
|
||||||
|
|
||||||
// method vmFlags:
|
// method vmFlags:
|
||||||
const unsigned ClassInitFlag = 1 << 0;
|
const unsigned ClassInitFlag = 1 << 0;
|
||||||
@ -1816,6 +1814,7 @@ makeNew(Thread* t, object class_)
|
|||||||
|
|
||||||
PROTECT(t, class_);
|
PROTECT(t, class_);
|
||||||
unsigned sizeInBytes = pad(classFixedSize(t, class_));
|
unsigned sizeInBytes = pad(classFixedSize(t, class_));
|
||||||
|
assert(t, sizeInBytes);
|
||||||
object instance = allocate(t, sizeInBytes, classObjectMask(t, class_));
|
object instance = allocate(t, sizeInBytes, classObjectMask(t, class_));
|
||||||
setObjectClass(t, instance, class_);
|
setObjectClass(t, instance, class_);
|
||||||
|
|
||||||
@ -2103,6 +2102,9 @@ resolveSystemClass(Thread* t, const char* name)
|
|||||||
return resolveSystemClass(t, makeByteArray(t, "%s", name));
|
return resolveSystemClass(t, makeByteArray(t, "%s", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
linkClass(Thread* t, object loader, object class_);
|
||||||
|
|
||||||
object
|
object
|
||||||
resolveMethod(Thread* t, object class_, const char* methodName,
|
resolveMethod(Thread* t, object class_, const char* methodName,
|
||||||
const char* methodSpec);
|
const char* methodSpec);
|
||||||
@ -2444,6 +2446,101 @@ makeSingletonOfSize(Thread* t, unsigned count)
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline object
|
||||||
|
resolveClassInObject(Thread* t, object loader, object container,
|
||||||
|
unsigned classOffset)
|
||||||
|
{
|
||||||
|
object o = cast<object>(container, classOffset);
|
||||||
|
if (objectClass(t, o) == arrayBody(t, t->m->types, Machine::ByteArrayType)) {
|
||||||
|
PROTECT(t, container);
|
||||||
|
|
||||||
|
o = resolveClass(t, loader, o);
|
||||||
|
if (UNLIKELY(t->exception)) return 0;
|
||||||
|
|
||||||
|
set(t, container, classOffset, o);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object
|
||||||
|
resolveClassInPool(Thread* t, object loader, object method, unsigned index)
|
||||||
|
{
|
||||||
|
object o = singletonObject(t, codePool(t, methodCode(t, method)), index);
|
||||||
|
if (objectClass(t, o) == arrayBody(t, t->m->types, Machine::ReferenceType)) {
|
||||||
|
PROTECT(t, method);
|
||||||
|
|
||||||
|
o = resolveClass(t, loader, referenceName(t, o));
|
||||||
|
if (UNLIKELY(t->exception)) return 0;
|
||||||
|
|
||||||
|
set(t, codePool(t, methodCode(t, method)),
|
||||||
|
SingletonBody + (index * BytesPerWord), o);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object
|
||||||
|
resolveClassInPool(Thread* t, object method, unsigned index)
|
||||||
|
{
|
||||||
|
return resolveClassInPool(t, classLoader(t, methodClass(t, method)),
|
||||||
|
method, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object
|
||||||
|
resolve(Thread* t, object loader, object method, unsigned index,
|
||||||
|
object (*find)(vm::Thread*, object, object, object),
|
||||||
|
object (*makeError)(vm::Thread*, object))
|
||||||
|
{
|
||||||
|
object o = singletonObject(t, codePool(t, methodCode(t, method)), index);
|
||||||
|
if (objectClass(t, o) == arrayBody(t, t->m->types, Machine::ReferenceType))
|
||||||
|
{
|
||||||
|
PROTECT(t, method);
|
||||||
|
|
||||||
|
object reference = o;
|
||||||
|
PROTECT(t, reference);
|
||||||
|
|
||||||
|
object class_ = resolveClassInObject(t, loader, o, ReferenceClass);
|
||||||
|
if (UNLIKELY(t->exception)) return 0;
|
||||||
|
|
||||||
|
o = findInHierarchy
|
||||||
|
(t, class_, referenceName(t, reference), referenceSpec(t, reference),
|
||||||
|
find, makeError);
|
||||||
|
if (UNLIKELY(t->exception)) return 0;
|
||||||
|
|
||||||
|
set(t, codePool(t, methodCode(t, method)),
|
||||||
|
SingletonBody + (index * BytesPerWord), o);
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object
|
||||||
|
resolveField(Thread* t, object loader, object method, unsigned index)
|
||||||
|
{
|
||||||
|
return resolve(t, loader, method, index, findFieldInClass,
|
||||||
|
makeNoSuchFieldError);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object
|
||||||
|
resolveField(Thread* t, object method, unsigned index)
|
||||||
|
{
|
||||||
|
return resolveField
|
||||||
|
(t, classLoader(t, methodClass(t, method)), method, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object
|
||||||
|
resolveMethod(Thread* t, object loader, object method, unsigned index)
|
||||||
|
{
|
||||||
|
return resolve(t, loader, method, index, findMethodInClass,
|
||||||
|
makeNoSuchMethodError);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object
|
||||||
|
resolveMethod(Thread* t, object method, unsigned index)
|
||||||
|
{
|
||||||
|
return resolveMethod
|
||||||
|
(t, classLoader(t, methodClass(t, method)), method, index);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
dumpHeap(Thread* t, FILE* out);
|
dumpHeap(Thread* t, FILE* out);
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ class MySystem: public System {
|
|||||||
|
|
||||||
virtual void join() {
|
virtual void join() {
|
||||||
int rv UNUSED = pthread_join(thread, 0);
|
int rv UNUSED = pthread_join(thread, 0);
|
||||||
expect(s, rv == 0);
|
//expect(s, rv == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void dispose() {
|
virtual void dispose() {
|
||||||
|
@ -36,79 +36,6 @@ codeReadInt32(Thread* t, object code, unsigned& ip)
|
|||||||
return ((v1 << 24) | (v2 << 16) | (v3 << 8) | v4);
|
return ((v1 << 24) | (v2 << 16) | (v3 << 8) | v4);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline object
|
|
||||||
resolveClassInObject(Thread* t, object loader, object container,
|
|
||||||
unsigned classOffset)
|
|
||||||
{
|
|
||||||
object o = cast<object>(container, classOffset);
|
|
||||||
if (objectClass(t, o) == arrayBody(t, t->m->types, Machine::ByteArrayType)) {
|
|
||||||
PROTECT(t, container);
|
|
||||||
|
|
||||||
o = resolveClass(t, loader, o);
|
|
||||||
if (UNLIKELY(t->exception)) return 0;
|
|
||||||
|
|
||||||
set(t, container, classOffset, o);
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline object
|
|
||||||
resolveClassInPool(Thread* t, object method, unsigned index)
|
|
||||||
{
|
|
||||||
object o = singletonObject(t, codePool(t, methodCode(t, method)), index);
|
|
||||||
if (objectClass(t, o) == arrayBody(t, t->m->types, Machine::ByteArrayType)) {
|
|
||||||
PROTECT(t, method);
|
|
||||||
|
|
||||||
o = resolveClass(t, classLoader(t, methodClass(t, method)), o);
|
|
||||||
if (UNLIKELY(t->exception)) return 0;
|
|
||||||
|
|
||||||
set(t, codePool(t, methodCode(t, method)),
|
|
||||||
SingletonBody + (index * BytesPerWord), o);
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline object
|
|
||||||
resolve(Thread* t, object method, unsigned index,
|
|
||||||
object (*find)(vm::Thread*, object, object, object),
|
|
||||||
object (*makeError)(vm::Thread*, object))
|
|
||||||
{
|
|
||||||
object o = singletonObject(t, codePool(t, methodCode(t, method)), index);
|
|
||||||
if (objectClass(t, o) == arrayBody(t, t->m->types, Machine::ReferenceType))
|
|
||||||
{
|
|
||||||
PROTECT(t, method);
|
|
||||||
|
|
||||||
object reference = o;
|
|
||||||
PROTECT(t, reference);
|
|
||||||
|
|
||||||
object class_ = resolveClassInObject
|
|
||||||
(t, classLoader(t, methodClass(t, method)), o, ReferenceClass);
|
|
||||||
if (UNLIKELY(t->exception)) return 0;
|
|
||||||
|
|
||||||
o = findInHierarchy
|
|
||||||
(t, class_, referenceName(t, reference), referenceSpec(t, reference),
|
|
||||||
find, makeError);
|
|
||||||
if (UNLIKELY(t->exception)) return 0;
|
|
||||||
|
|
||||||
set(t, codePool(t, methodCode(t, method)),
|
|
||||||
SingletonBody + (index * BytesPerWord), o);
|
|
||||||
}
|
|
||||||
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline object
|
|
||||||
resolveField(Thread* t, object method, unsigned index)
|
|
||||||
{
|
|
||||||
return resolve(t, method, index, findFieldInClass, makeNoSuchFieldError);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline object
|
|
||||||
resolveMethod(Thread* t, object method, unsigned index)
|
|
||||||
{
|
|
||||||
return resolve(t, method, index, findMethodInClass, makeNoSuchMethodError);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
isSuperclass(Thread* t, object class_, object base)
|
isSuperclass(Thread* t, object class_, object base)
|
||||||
{
|
{
|
||||||
|
@ -60,10 +60,10 @@ class Processor {
|
|||||||
virtual object
|
virtual object
|
||||||
makeClass(Thread* t,
|
makeClass(Thread* t,
|
||||||
uint16_t flags,
|
uint16_t flags,
|
||||||
uint8_t vmFlags,
|
uint16_t vmFlags,
|
||||||
uint8_t arrayDimensions,
|
|
||||||
uint16_t fixedSize,
|
uint16_t fixedSize,
|
||||||
uint16_t arrayElementSize,
|
uint8_t arrayElementSize,
|
||||||
|
uint8_t arrayDimensions,
|
||||||
object objectMask,
|
object objectMask,
|
||||||
object name,
|
object name,
|
||||||
object super,
|
object super,
|
||||||
|
Loading…
Reference in New Issue
Block a user