diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index c84a394b3f..578d2fda6b 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -10,6 +10,7 @@ public class Method extends AccessibleObject implements Member { private byte[] spec; private Class class_; private Object code; + private Object compiled; private Method() { } diff --git a/makefile b/makefile index 5c7f63fc0f..7f7b427d58 100644 --- a/makefile +++ b/makefile @@ -24,6 +24,8 @@ else ld-library-path = LD_LIBRARY_PATH endif +process = interpret + mode = debug bld = build/$(platform)/$(arch)/$(mode) @@ -32,7 +34,7 @@ src = src classpath = classpath test = test -input = $(cls)/Threads.class +input = $(cls)/Instructions.class cxx = g++ cc = gcc @@ -104,7 +106,7 @@ interpreter-sources = \ $(src)/finder.cpp \ $(src)/machine.cpp \ $(src)/heap.cpp \ - $(src)/interpret.cpp \ + $(src)/$(process).cpp \ $(src)/builtin.cpp \ $(src)/jnienv.cpp \ $(src)/main.cpp diff --git a/src/common.h b/src/common.h index 77ea27ad12..9a6f13cf8f 100644 --- a/src/common.h +++ b/src/common.h @@ -4,6 +4,7 @@ #include "stdint.h" #include "stdlib.h" #include "stdarg.h" +#include "stddef.h" #include "string.h" #include "stdio.h" #include "types.h" @@ -44,6 +45,22 @@ inline void* operator new(size_t, void* p) throw() { return p; } +#ifdef __i386__ + +extern "C" uint64_t +cdeclCall(void* function, void* stack, unsigned stackSize, + unsigned returnType); + +#elif defined __x86_64__ + +extern "C" uint64_t +amd64Call(void* function, void* stack, unsigned stackSize, + void* gprTable, void* sseTable, unsigned returnType); + +#else +# error unsupported platform +#endif + namespace vm { const unsigned BytesPerWord = sizeof(uintptr_t); diff --git a/src/compile.cpp b/src/compile.cpp index 704f2a0f0e..e4dcbefb2c 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -7,6 +7,11 @@ using namespace vm; namespace { +const unsigned FrameThread = 8; +const unsigned FrameMethod = 12; +const unsigned FrameNext = 16; +const unsigned FrameFootprint = 12; + class Rope { public: class Node { @@ -21,6 +26,8 @@ class Rope { uint8_t data[Size]; }; + Rope() { } + Rope(System* s): s(s), front(0), @@ -31,12 +38,16 @@ class Rope { void append(uint8_t v) { if (position == Node::Size) { - Node* n = new (s->allocate(sizeof(Node))) Node; - if (front == 0) { - front = rear = n; + if (front == 0 or rear->next == 0) { + Node* n = new (s->allocate(sizeof(Node))) Node; + if (front == 0) { + front = rear = n; + } else { + rear->next = n; + rear = n; + } } else { - rear->next = n; - rear = n; + rear = rear->next; } position = 0; ++ count; @@ -45,6 +56,13 @@ class Rope { rear->data[position++] = v; } + void append4(uint32_t v) { + append((v >> 0) & 0xFF); + append((v >> 8) & 0xFF); + append((v >> 16) & 0xFF); + append((v >> 24) & 0xFF); + } + unsigned length() { return (count * Node::Size) + position; } @@ -72,16 +90,233 @@ class Rope { unsigned position; }; -class Assembler { +class ArgumentList; + +class MyThread: public Thread { public: - Assembler(System* s): - rope(s) + MyThread(Machine* m, object javaThread, vm::Thread* parent): + vm::Thread(m, javaThread, parent), + argumentList(0), + frame(0) { } - Rope rope; + ArgumentList* argumentList; + void* frame; }; -class Compiler: private Assembler { +class Assembler { + public: + class Label { + public: + class Snapshot { + public: + Rope rope; + unsigned ip; + }; + + static const unsigned Capacity = 8; + + Label(): + unresolvedCount(0), + mark_(-1) + { } + + void reference(Rope* r, unsigned ip) { + if (mark_ == -1) { + expect(r->s, unresolvedCount < Capacity); + unresolved[unresolvedCount].rope = *r; + unresolved[unresolvedCount].ip = ip; + ++ unresolvedCount; + + r->append4(0); + } else { + r->append4(mark_ - ip); + } + } + + void mark(Rope* r) { + mark_ = r->length(); + for (unsigned i = 0; i < unresolvedCount; ++i) { + unresolved[i].rope.append4(mark_ - unresolved[i].ip); + } + } + + Snapshot unresolved[Capacity]; + unsigned unresolvedCount; + int mark_; + }; + + enum Register { + eax = 0, + ecx = 1, + edx = 2, + ebx = 3, + esp = 4, + ebp = 5 + }; + + Assembler(System* s): + r(s) + { } + + void mov(Register src, Register dst) { + r.append(0x89); + r.append(0xc0 | (src << 3) | dst); + } + + void mov(Register src, int srcOffset, Register dst) { + r.append(0x8b); + if (srcOffset) { + r.append(0x40 | (dst << 3) | src); + r.append(srcOffset); + } else { + r.append((dst << 3) | src); + } + } + + void mov(Register src, Register dst, int dstOffset) { + r.append(0x89); + if (dstOffset) { + r.append(0x40 | (src << 3) | dst); + r.append(dstOffset); + } else { + r.append((src << 3) | dst); + } + } + + void mov(uintptr_t src, Register dst) { + r.append(0xb8 | dst); + r.append4(src); + } + + void push(Register reg) { + r.append(0x50 | reg); + } + + void push(Register reg, int offset) { + r.append(0xff); + r.append(0x70 | reg); + r.append(offset); + } + + void push(int v) { + r.append(0x6a); + r.append(v); + } + + void pop(Register dst) { + r.append(0x58 | dst); + } + + void pop(Register dst, int offset) { + r.append(0x8f); + r.append(0x40 | dst); + r.append(offset); + } + + void add(Register src, Register dst) { + r.append(0x01); + r.append(0xc0 | (src << 3) | dst); + } + + void add(int src, Register dst) { + r.append(0x83); + r.append(0xc0 | dst); + r.append(src); + } + + void sub(Register src, Register dst) { + r.append(0x29); + r.append(0xc0 | (src << 3) | dst); + } + + void sub(int src, Register dst) { + r.append(0x83); + r.append(0xe8 | dst); + r.append(src); + } + + void or_(Register src, Register dst) { + r.append(0x09); + r.append(0xc0 | (src << 3) | dst); + } + + void or_(int src, Register dst) { + r.append(0x83); + r.append(0xc8 | dst); + r.append(src); + } + + void and_(Register src, Register dst) { + r.append(0x21); + r.append(0xc0 | (src << 3) | dst); + } + + void and_(int src, Register dst) { + r.append(0x83); + r.append(0xe0 | dst); + r.append(src); + } + + void ret() { + r.append(0xc3); + } + + void jmp(Label& label) { + r.append(0xE9); + label.reference(&r, r.length() + 4); + } + + void jmp(Register reg) { + r.append(0xff); + r.append(0xe0 | reg); + } + + void jmp(Register reg, int offset) { + r.append(0xff); + r.append(0x60 | reg); + r.append(offset); + } + + void jnz(Label& label) { + r.append(0x0F); + r.append(0x85); + label.reference(&r, r.length() + 4); + } + + void jne(Label& label) { + jnz(label); + } + + void cmp(int v, Register reg) { + r.append(0x83); + r.append(0xf8 | reg); + r.append(v); + } + + void call(Register reg) { + r.append(0xff); + r.append(0xd0 | reg); + } + + Rope r; +}; + +void +compileMethod(Thread* t, object method); + +int +localOffset(int v, int parameterFootprint) +{ + v *= 4; + if (v < parameterFootprint) { + return v + 8 + FrameFootprint; + } else { + return -(v + 4 - parameterFootprint); + } +} + +class Compiler: public Assembler { public: Compiler(System* s): Assembler(s) @@ -92,9 +327,10 @@ class Compiler: private Assembler { mov(esp, ebp); object code = methodCode(t, method); + unsigned parameterFootprint = methodParameterFootprint(t, method) * 4; // reserve space for local variables - sub(codeMaxLocals(t, code), esp); + sub((codeMaxLocals(t, code) * 4) - parameterFootprint, esp); for (unsigned i = 0; i < codeLength(t, code);) { switch (codeBody(t, code, i++)) { @@ -135,50 +371,42 @@ class Compiler: private Assembler { case iload_0: case fload_0: - mov(ebp, 0, eax); - push(eax); + push(ebp, localOffset(0, parameterFootprint)); break; case iload_1: case fload_1: - mov(ebp, -1, eax); - push(eax); + push(ebp, localOffset(1, parameterFootprint)); break; case iload_2: case fload_2: - mov(ebp, -2, eax); - push(eax); + push(ebp, localOffset(2, parameterFootprint)); break; case iload_3: case fload_3: - mov(ebp, -3, eax); - push(eax); + push(ebp, localOffset(3, parameterFootprint)); break; case istore_0: case fstore_0: - pop(eax); - mov(eax, ebp, 0); + pop(ebp, localOffset(0, parameterFootprint)); break; case istore_1: case fstore_1: - pop(eax); - mov(eax, ebp, -1); + pop(ebp, localOffset(1, parameterFootprint)); break; case istore_2: case fstore_2: - pop(eax); - mov(eax, ebp, -2); + pop(ebp, localOffset(2, parameterFootprint)); break; case istore_3: case fstore_3: - pop(eax); - mov(eax, ebp, -3); + pop(ebp, localOffset(3, parameterFootprint)); break; case return_: @@ -192,26 +420,345 @@ class Compiler: private Assembler { } } } + + void compileStub(MyThread* t) { + unsigned frameOffset = reinterpret_cast(&(t->frame)) + - reinterpret_cast(t); + + push(ebp); + mov(esp, ebp); + + mov(ebp, FrameThread, eax); + mov(ebp, eax, frameOffset); // set thread frame to current + + push(ebp, FrameMethod); + push(ebp, FrameThread); + mov(reinterpret_cast(compileMethod), eax); + call(eax); + add(8, esp); + + mov(ebp, FrameMethod, eax); + mov(eax, MethodCompiled, eax); // load compiled code + + mov(ebp, esp); + pop(ebp); + + add(CompiledBody, eax); + jmp(eax); // call compiled code + } +}; + +void +compileMethod(Thread* t, object method) +{ + if (methodCompiled(t, method) == t->m->processor->methodStub(t)) { + PROTECT(t, method); + + ACQUIRE(t, t->m->classLock); + + if (methodCompiled(t, method) == t->m->processor->methodStub(t)) { + Compiler c(t->m->system); + c.compile(t, method); + + object compiled = makeCompiled(t, 0, c.r.length(), false); + c.r.copyTo(&compiledBody(t, compiled, 0)); + + set(t, methodCompiled(t, method), compiled); + } + } +} + +object +compileStub(Thread* t) +{ + Compiler c(t->m->system); + c.compileStub(static_cast(t)); + + object stub = makeCompiled(t, 0, c.r.length(), false); + c.r.copyTo(&compiledBody(t, stub, 0)); + + return stub; +} + +class ArgumentList { + public: + ArgumentList(Thread* t, uintptr_t* array, bool* objectMask, object this_, + const char* spec, bool indirectObjects, va_list arguments): + t(static_cast(t)), + next(this->t->argumentList), + array(array), + objectMask(objectMask), + position(0) + { + this->t->argumentList = this; + + addInt(reinterpret_cast(t)); + addObject(0); // reserve space for method + addInt(reinterpret_cast(this->t->frame)); + + if (this_) { + addObject(this_); + } + + const char* s = spec; + ++ s; // skip '(' + while (*s and *s != ')') { + switch (*s) { + case 'L': + while (*s and *s != ';') ++ s; + ++ s; + + if (indirectObjects) { + object* v = va_arg(arguments, object*); + addObject(v ? *v : 0); + } else { + addObject(va_arg(arguments, object)); + } + break; + + case '[': + while (*s == '[') ++ s; + switch (*s) { + case 'L': + while (*s and *s != ';') ++ s; + ++ s; + break; + + default: + ++ s; + break; + } + + if (indirectObjects) { + object* v = va_arg(arguments, object*); + addObject(v ? *v : 0); + } else { + addObject(va_arg(arguments, object)); + } + break; + + case 'J': + case 'D': + ++ s; + addLong(va_arg(arguments, uint64_t)); + break; + + default: + ++ s; + addInt(va_arg(arguments, uint32_t)); + break; + } + } + } + + ArgumentList(Thread* t, uintptr_t* array, bool* objectMask, object this_, + const char* spec, object arguments): + t(static_cast(t)), + next(this->t->argumentList), + array(array), + objectMask(objectMask), + position(0) + { + this->t->argumentList = this; + + addInt(0); // reserve space for trace pointer + addObject(0); // reserve space for method pointer + + if (this_) { + addObject(this_); + } + + unsigned index = 0; + const char* s = spec; + ++ s; // skip '(' + while (*s and *s != ')') { + switch (*s) { + case 'L': + while (*s and *s != ';') ++ s; + ++ s; + addObject(objectArrayBody(t, arguments, index++)); + break; + + case '[': + while (*s == '[') ++ s; + switch (*s) { + case 'L': + while (*s and *s != ';') ++ s; + ++ s; + break; + + default: + ++ s; + break; + } + addObject(objectArrayBody(t, arguments, index++)); + break; + + case 'J': + case 'D': + ++ s; + addLong(cast(objectArrayBody(t, arguments, index++), + BytesPerWord)); + break; + + default: + ++ s; + addInt(cast(objectArrayBody(t, arguments, index++), + BytesPerWord)); + break; + } + } + } + + ~ArgumentList() { + t->argumentList = next; + } + + void addObject(object v) { + array[position] = reinterpret_cast(v); + objectMask[position] = true; + ++ position; + } + + void addInt(uint32_t v) { + array[position] = v; + objectMask[position] = false; + ++ position; + } + + void addLong(uint64_t v) { + memcpy(array + position, &v, 8); + objectMask[position] = false; + objectMask[position] = false; + position += 2; + } + + MyThread* t; + ArgumentList* next; + uintptr_t* array; + bool* objectMask; + unsigned position; }; object -compile(Thread* t, object method) +invoke(Thread* thread, object method, ArgumentList* arguments) { - Compiler c(t->vm->system); - c.compile(t, method); + MyThread* t = static_cast(thread); + + arguments->array[1] = reinterpret_cast(method); - object r = makeByteArray(t, c.rope.length(), false); - c.rope.copyTo(&byteArrayBody(t, r, 0)); + const char* s = reinterpret_cast + (&byteArrayBody(t, methodSpec(t, method), 0)); + while (*s and *s != ')') ++s; + unsigned returnCode = fieldCode(t, s[1]); + unsigned returnType = fieldType(t, returnCode); + + uint64_t result = cdeclCall + (&compiledBody(t, methodCompiled(t, method), 0), arguments->array, + arguments->position * 4, returnType); + + object r; + switch (returnCode) { + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case FloatField: + case IntField: + r = makeInt(t, result); + break; + + case LongField: + case DoubleField: + r = makeLong(t, result); + break; + + case ObjectField: + r = (result == 0 ? 0 : + *reinterpret_cast(static_cast(result))); + break; + + case VoidField: + r = 0; + break; + + default: + abort(t); + }; return r; } -class MyInvoker: public Invoker { +class MyProcessor: public Processor { public: - MyInvoker(System* s): - s(s) + MyProcessor(System* s): + s(s), + stub(0) { } + virtual Thread* + makeThread(Machine* m, object javaThread, Thread* parent) + { + return new (s->allocate(sizeof(MyThread))) MyThread(m, javaThread, parent); + } + + virtual object + methodStub(Thread* t) + { + if (stub == 0) { + stub = compileStub(t); + } + return stub; + } + + virtual void + visitObjects(Thread* t, Heap::Visitor*) + { + abort(t); + } + + virtual uintptr_t + frameStart(Thread* t) + { + abort(t); + } + + virtual uintptr_t + frameNext(Thread* t, uintptr_t) + { + abort(t); + } + + virtual bool + frameValid(Thread* t, uintptr_t) + { + abort(t); + } + + virtual object + frameMethod(Thread* t, uintptr_t) + { + abort(t); + } + + virtual unsigned + frameIp(Thread* t, uintptr_t) + { + abort(t); + } + + virtual object* + makeLocalReference(Thread* t, object) + { + abort(t); + } + + virtual void + disposeLocalReference(Thread* t, object*) + { + abort(t); + } + virtual object invokeArray(Thread* t, object method, object this_, object arguments) { @@ -220,10 +767,15 @@ class MyInvoker: public Invoker { assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0)); - uintptr_t a[methodParameterCount(t, method) * 2]; - ArgumentArray array(t, a, method, this_, spec, arguments); + const char* spec = reinterpret_cast + (&byteArrayBody(t, methodSpec(t, method), 0)); + + unsigned size = methodParameterCount(t, method) * 2; + uintptr_t array[size]; + bool objectMask[size]; + ArgumentList list(t, array, objectMask, this_, spec, arguments); - return invoke(t, method, &array); + return ::invoke(t, method, &list); } virtual object @@ -235,10 +787,16 @@ class MyInvoker: public Invoker { assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0)); - uintptr_t a[methodParameterCount(t, method) * 2]; - ArgumentArray array(t, a, method, this_, spec, indirectObjects, arguments); + const char* spec = reinterpret_cast + (&byteArrayBody(t, methodSpec(t, method), 0)); - return invoke(t, method, &array); + unsigned size = methodParameterCount(t, method) * 2; + uintptr_t array[size]; + bool objectMask[size]; + ArgumentList list + (t, array, objectMask, this_, spec, indirectObjects, arguments); + + return ::invoke(t, method, &list); } virtual object @@ -248,14 +806,17 @@ class MyInvoker: public Invoker { assert(t, t->state == Thread::ActiveState or t->state == Thread::ExclusiveState); - uintptr_t a[methodParameterCount(t, method) * 2]; - ArgumentArray array(t, a, method, this_, methodSpec, false, arguments); + unsigned size = parameterCount(methodSpec) * 2; + uintptr_t array[size]; + bool objectMask[size]; + ArgumentList list + (t, array, objectMask, this_, methodSpec, false, arguments); object method = resolveMethod(t, className, methodName, methodSpec); if (LIKELY(t->exception == 0)) { assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0)); - return invoke(t, method, &array); + return ::invoke(t, method, &list); } else { return 0; } @@ -266,16 +827,18 @@ class MyInvoker: public Invoker { } System* s; + object stub; }; } // namespace namespace vm { -Invoker* -makeInvoker(System* system) +Processor* +makeProcessor(System* system) { - return new (system->allocate(sizeof(MyInvoker))) MyInvoker(system); + return new (system->allocate(sizeof(MyProcessor))) MyProcessor(system); } } // namespace vm + diff --git a/src/interpret.cpp b/src/interpret.cpp index 3a4fb5ac33..3b51924565 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -8,6 +8,12 @@ using namespace vm; namespace { +const unsigned FrameBaseOffset = 0; +const unsigned FrameNextOffset = 1; +const unsigned FrameMethodOffset = 2; +const unsigned FrameIpOffset = 3; +const unsigned FrameFootprint = 4; + class Thread: public vm::Thread { public: static const unsigned StackSizeInBytes = 64 * 1024; @@ -3012,6 +3018,12 @@ class MyProcessor: public Processor { return new (s->allocate(sizeof(Thread))) Thread(m, javaThread, parent); } + virtual object + methodStub(vm::Thread*) + { + return 0; + } + virtual void visitObjects(vm::Thread* vmt, Heap::Visitor* v) { @@ -3112,8 +3124,7 @@ class MyProcessor: public Processor { virtual object invokeList(vm::Thread* vmt, object method, object this_, - bool indirectObjects, - va_list arguments) + bool indirectObjects, va_list arguments) { Thread* t = static_cast(vmt); diff --git a/src/machine.cpp b/src/machine.cpp index 48011f44ba..afb5616b11 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -917,6 +917,7 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) methodName(t, method), methodSpec(t, method), methodClass(t, method), + 0, 0); hashMapInsert(t, virtualMap, method, method, methodHash); } @@ -986,7 +987,8 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) arrayBody(t, pool, name - 1), arrayBody(t, pool, spec - 1), class_, - code); + code, + (code ? t->m->processor->methodStub(t) : 0)); PROTECT(t, method); if (flags & ACC_STATIC) { diff --git a/src/machine.h b/src/machine.h index 890fa7f9c5..c2a625a4b9 100644 --- a/src/machine.h +++ b/src/machine.h @@ -29,12 +29,6 @@ const bool DebugReferences = false; const uintptr_t HashTakenMark = 1; const uintptr_t ExtendedMark = 2; -const unsigned FrameBaseOffset = 0; -const unsigned FrameNextOffset = 1; -const unsigned FrameMethodOffset = 2; -const unsigned FrameIpOffset = 3; -const unsigned FrameFootprint = 4; - enum FieldCode { VoidField, ByteField, @@ -65,6 +59,7 @@ const unsigned PrimitiveFlag = 1 << 4; // method flags: const unsigned ClassInitFlag = 1 << 0; +const unsigned CompiledFlag = 1 << 1; typedef Machine JavaVM; typedef Thread JNIEnv; diff --git a/src/processor.h b/src/processor.h index fa91fadbdc..cd4165057a 100644 --- a/src/processor.h +++ b/src/processor.h @@ -14,6 +14,9 @@ class Processor { virtual Thread* makeThread(Machine* m, object javaThread, Thread* parent) = 0; + virtual object + methodStub(Thread* t) = 0; + virtual void visitObjects(Thread* t, Heap::Visitor* v) = 0; diff --git a/src/system.cpp b/src/system.cpp index 493d311833..67e7ca96cf 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -15,14 +15,10 @@ #define ACQUIRE(x) MutexResource MAKE_NAME(mutexResource_) (x) -#ifdef __i386__ - -extern "C" uint64_t -cdeclCall(void* function, void* stack, unsigned stackSize, - unsigned returnType); - namespace { +#ifdef __i386__ + inline uint64_t dynamicCall(void* function, uintptr_t* arguments, uint8_t*, unsigned, unsigned argumentsSize, unsigned returnType) @@ -30,16 +26,8 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t*, return cdeclCall(function, arguments, argumentsSize, returnType); } -} // namespace - #elif defined __x86_64__ -extern "C" uint64_t -amd64Call(void* function, void* stack, unsigned stackSize, - void* gprTable, void* sseTable, unsigned returnType); - -namespace { - uint64_t dynamicCall(void* function, uint64_t* arguments, uint8_t* argumentTypes, unsigned argumentCount, unsigned, unsigned returnType) @@ -80,12 +68,12 @@ dynamicCall(void* function, uint64_t* arguments, uint8_t* argumentTypes, (sseIndex ? sseTable : 0), returnType); } -} // namespace - #else # error unsupported platform #endif +} + using namespace vm; namespace { diff --git a/src/type-generator.cpp b/src/type-generator.cpp index 6409cfb964..3adff315ce 100644 --- a/src/type-generator.cpp +++ b/src/type-generator.cpp @@ -1035,6 +1035,15 @@ writeAccessor(Output* out, Object* member, Object* offset, bool unsafe = false) const char* typeName = memberTypeName(member); if (memberTypeObject(member)) typeName = capitalize(typeName); + if (not unsafe) { + out->write("const unsigned "); + out->write(capitalize(::typeName(memberOwner(member)))); + out->write(capitalize(memberName(member))); + out->write(" = "); + writeOffset(out, offset); + out->write(";\n\n"); + } + out->write("inline "); out->write(typeName); if (member->type != Object::Scalar and memberTypeObject(member)) { @@ -1094,7 +1103,10 @@ writeAccessor(Output* out, Object* member, Object* offset, bool unsafe = false) } else { out->write("["); } - writeOffset(out, offset); + + out->write(capitalize(::typeName(memberOwner(member)))); + out->write(capitalize(memberName(member))); + if (member->type != Object::Scalar) { out->write(" + (i * "); unsigned elementSize = (memberTypeObject(member) ? diff --git a/src/types.def b/src/types.def index 737771b152..4e0dfe8bfe 100644 --- a/src/types.def +++ b/src/types.def @@ -48,7 +48,12 @@ (object name) (object spec) (object class) - (object code)) + (object code) + (object compiled)) + +(type compiled + (nogc object method) + (array uint8_t body)) (type nativeMethodData (void* function)