mirror of
https://github.com/corda/corda.git
synced 2025-01-03 19:54:13 +00:00
implement lazy class/field/method resolution in JIT compiler
Unlike the interpreter, the JIT compiler tries to resolve all the symbols referenced by a method when compiling that method. However, this can backfire if a symbol cannot be resolved: we end up throwing an e.g. NoClassDefFoundError for code which may never be executed. This is particularly troublesome for code which supports multiple APIs, choosing one at runtime. The solution is to defer to stub code for symbols which can't be resolved at JIT compile time. Such a stub will try again at runtime to resolve the needed symbol and throw an appropriate error if it still can't be found.
This commit is contained in:
parent
48e569c65a
commit
453ceb42ab
@ -15,6 +15,7 @@ public class VMField {
|
||||
public byte code;
|
||||
public short flags;
|
||||
public short offset;
|
||||
public short index;
|
||||
public byte[] name;
|
||||
public byte[] spec;
|
||||
public FieldAddendum addendum;
|
||||
|
1423
src/compile.cpp
1423
src/compile.cpp
File diff suppressed because it is too large
Load Diff
@ -1460,27 +1460,11 @@ interpret3(Thread* t, const int base)
|
||||
|
||||
assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0);
|
||||
|
||||
if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE)
|
||||
and BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField)))
|
||||
{
|
||||
PROTECT(t, field);
|
||||
acquire(t, field);
|
||||
}
|
||||
PROTECT(t, field);
|
||||
|
||||
ACQUIRE_FIELD_FOR_READ(t, field);
|
||||
|
||||
pushField(t, popObject(t), field);
|
||||
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
release(t, field);
|
||||
} else {
|
||||
loadMemoryBarrier();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
goto throw_;
|
||||
@ -1498,26 +1482,9 @@ interpret3(Thread* t, const int base)
|
||||
|
||||
if (UNLIKELY(classInit(t, fieldClass(t, field), 3))) goto invoke;
|
||||
|
||||
if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE)
|
||||
and BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField)))
|
||||
{
|
||||
acquire(t, field);
|
||||
}
|
||||
ACQUIRE_FIELD_FOR_READ(t, field);
|
||||
|
||||
pushField(t, classStaticTable(t, fieldClass(t, field)), field);
|
||||
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
release(t, field);
|
||||
} else {
|
||||
loadMemoryBarrier();
|
||||
}
|
||||
}
|
||||
} goto loop;
|
||||
|
||||
case goto_: {
|
||||
@ -2460,80 +2427,61 @@ interpret3(Thread* t, const int base)
|
||||
assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0);
|
||||
PROTECT(t, field);
|
||||
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
acquire(t, field);
|
||||
} else {
|
||||
storeStoreMemoryBarrier();
|
||||
}
|
||||
}
|
||||
{ ACQUIRE_FIELD_FOR_WRITE(t, field);
|
||||
|
||||
switch (fieldCode(t, field)) {
|
||||
case ByteField:
|
||||
case BooleanField:
|
||||
case CharField:
|
||||
case ShortField:
|
||||
case FloatField:
|
||||
case IntField: {
|
||||
int32_t value = popInt(t);
|
||||
object o = popObject(t);
|
||||
if (LIKELY(o)) {
|
||||
switch (fieldCode(t, field)) {
|
||||
case ByteField:
|
||||
case BooleanField:
|
||||
cast<int8_t>(o, fieldOffset(t, field)) = value;
|
||||
break;
|
||||
switch (fieldCode(t, field)) {
|
||||
case ByteField:
|
||||
case BooleanField:
|
||||
case CharField:
|
||||
case ShortField:
|
||||
case FloatField:
|
||||
case IntField: {
|
||||
int32_t value = popInt(t);
|
||||
object o = popObject(t);
|
||||
if (LIKELY(o)) {
|
||||
switch (fieldCode(t, field)) {
|
||||
case ByteField:
|
||||
case BooleanField:
|
||||
cast<int8_t>(o, fieldOffset(t, field)) = value;
|
||||
break;
|
||||
|
||||
case CharField:
|
||||
case ShortField:
|
||||
cast<int16_t>(o, fieldOffset(t, field)) = value;
|
||||
break;
|
||||
case CharField:
|
||||
case ShortField:
|
||||
cast<int16_t>(o, fieldOffset(t, field)) = value;
|
||||
break;
|
||||
|
||||
case FloatField:
|
||||
case IntField:
|
||||
cast<int32_t>(o, fieldOffset(t, field)) = value;
|
||||
break;
|
||||
case FloatField:
|
||||
case IntField:
|
||||
cast<int32_t>(o, fieldOffset(t, field)) = value;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
} else {
|
||||
exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
} break;
|
||||
} break;
|
||||
|
||||
case DoubleField:
|
||||
case LongField: {
|
||||
int64_t value = popLong(t);
|
||||
object o = popObject(t);
|
||||
if (LIKELY(o)) {
|
||||
cast<int64_t>(o, fieldOffset(t, field)) = value;
|
||||
} else {
|
||||
exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
} break;
|
||||
case DoubleField:
|
||||
case LongField: {
|
||||
int64_t value = popLong(t);
|
||||
object o = popObject(t);
|
||||
if (LIKELY(o)) {
|
||||
cast<int64_t>(o, fieldOffset(t, field)) = value;
|
||||
} else {
|
||||
exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
} break;
|
||||
|
||||
case ObjectField: {
|
||||
object value = popObject(t);
|
||||
object o = popObject(t);
|
||||
if (LIKELY(o)) {
|
||||
set(t, o, fieldOffset(t, field), value);
|
||||
} else {
|
||||
exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
} break;
|
||||
case ObjectField: {
|
||||
object value = popObject(t);
|
||||
object o = popObject(t);
|
||||
if (LIKELY(o)) {
|
||||
set(t, o, fieldOffset(t, field), value);
|
||||
} else {
|
||||
exception = makeThrowable(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: abort(t);
|
||||
}
|
||||
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
release(t, field);
|
||||
} else {
|
||||
storeLoadMemoryBarrier();
|
||||
default: abort(t);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2551,16 +2499,7 @@ interpret3(Thread* t, const int base)
|
||||
|
||||
PROTECT(t, field);
|
||||
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
acquire(t, field);
|
||||
} else {
|
||||
storeStoreMemoryBarrier();
|
||||
}
|
||||
}
|
||||
ACQUIRE_FIELD_FOR_WRITE(t, field);
|
||||
|
||||
if (UNLIKELY(classInit(t, fieldClass(t, field), 3))) goto invoke;
|
||||
|
||||
@ -2603,17 +2542,6 @@ interpret3(Thread* t, const int base)
|
||||
|
||||
default: abort(t);
|
||||
}
|
||||
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
release(t, field);
|
||||
} else {
|
||||
storeLoadMemoryBarrier();
|
||||
}
|
||||
}
|
||||
} goto loop;
|
||||
|
||||
case ret: {
|
||||
|
@ -1075,6 +1075,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool)
|
||||
code,
|
||||
flags,
|
||||
0, // offset
|
||||
i,
|
||||
singletonObject(t, pool, name - 1),
|
||||
singletonObject(t, pool, spec - 1),
|
||||
addendum,
|
||||
@ -1270,20 +1271,6 @@ parseCode(Thread* t, Stream& s, object pool)
|
||||
return code;
|
||||
}
|
||||
|
||||
void
|
||||
scanMethodSpec(Thread* t, const char* s, unsigned* parameterCount,
|
||||
unsigned* returnCode)
|
||||
{
|
||||
unsigned count = 0;
|
||||
MethodSpecIterator it(t, s);
|
||||
for (; it.hasNext(); it.next()) {
|
||||
++ count;
|
||||
}
|
||||
|
||||
*parameterCount = count;
|
||||
*returnCode = fieldCode(t, *it.returnSpec());
|
||||
}
|
||||
|
||||
object
|
||||
addInterfaceMethods(Thread* t, object class_, object virtualMap,
|
||||
unsigned* virtualCount, bool makeList)
|
||||
@ -2225,6 +2212,17 @@ doCollect(Thread* t, Heap::CollectionType type)
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
invokeLoadClass(Thread* t, uintptr_t* arguments)
|
||||
{
|
||||
object method = reinterpret_cast<object>(arguments[0]);
|
||||
object loader = reinterpret_cast<object>(arguments[1]);
|
||||
object specString = reinterpret_cast<object>(arguments[2]);
|
||||
|
||||
return reinterpret_cast<uintptr_t>
|
||||
(t->m->processor->invoke(t, method, loader, specString));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace vm {
|
||||
@ -3342,8 +3340,6 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
|
||||
if (objectClass(t, loader) == type(t, Machine::SystemClassLoaderType)) {
|
||||
return resolveSystemClass(t, loader, spec, throw_);
|
||||
} else {
|
||||
expect(t, throw_);
|
||||
|
||||
PROTECT(t, loader);
|
||||
PROTECT(t, spec);
|
||||
|
||||
@ -3384,9 +3380,23 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
|
||||
|
||||
object specString = makeString(t, "%s", RUNTIME_ARRAY_BODY(s));
|
||||
|
||||
object jc = t->m->processor->invoke(t, method, loader, specString);
|
||||
uintptr_t arguments[] = { reinterpret_cast<uintptr_t>(method),
|
||||
reinterpret_cast<uintptr_t>(loader),
|
||||
reinterpret_cast<uintptr_t>(specString) };
|
||||
|
||||
object jc = reinterpret_cast<object>
|
||||
(runRaw(t, invokeLoadClass, arguments));
|
||||
|
||||
if (LIKELY(jc)) {
|
||||
c = jclassVmClass(t, jc);
|
||||
} else if (t->exception) {
|
||||
if (throw_) {
|
||||
object e = t->exception;
|
||||
t->exception = 0;
|
||||
vm::throw_(t, e);
|
||||
} else {
|
||||
t->exception = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3402,12 +3412,12 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
|
||||
|
||||
hashMapInsert
|
||||
(t, classLoaderMap(t, loader), spec, c, byteArrayHash);
|
||||
|
||||
return c;
|
||||
} else {
|
||||
} else if (throw_) {
|
||||
throwNew(t, Machine::ClassNotFoundExceptionType, "%s",
|
||||
&byteArrayBody(t, spec, 0));
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
|
191
src/machine.h
191
src/machine.h
@ -33,6 +33,12 @@
|
||||
#define ACQUIRE_OBJECT(t, x) \
|
||||
ObjectMonitorResource MAKE_NAME(monitorResource_) (t, x)
|
||||
|
||||
#define ACQUIRE_FIELD_FOR_READ(t, field) \
|
||||
FieldReadResource MAKE_NAME(monitorResource_) (t, field)
|
||||
|
||||
#define ACQUIRE_FIELD_FOR_WRITE(t, field) \
|
||||
FieldReadResource MAKE_NAME(monitorResource_) (t, field)
|
||||
|
||||
#define ACQUIRE_RAW(t, x) RawMonitorResource MAKE_NAME(monitorResource_) (t, x)
|
||||
|
||||
#define ENTER(t, state) StateResource MAKE_NAME(stateResource_) (t, state)
|
||||
@ -2430,6 +2436,20 @@ fieldSize(Thread* t, object field)
|
||||
return fieldSize(t, fieldCode(t, field));
|
||||
}
|
||||
|
||||
inline void
|
||||
scanMethodSpec(Thread* t, const char* s, unsigned* parameterCount,
|
||||
unsigned* returnCode)
|
||||
{
|
||||
unsigned count = 0;
|
||||
MethodSpecIterator it(t, s);
|
||||
for (; it.hasNext(); it.next()) {
|
||||
++ count;
|
||||
}
|
||||
|
||||
*parameterCount = count;
|
||||
*returnCode = fieldCode(t, *it.returnSpec());
|
||||
}
|
||||
|
||||
object
|
||||
findLoadedClass(Thread* t, object loader, object spec);
|
||||
|
||||
@ -2639,11 +2659,11 @@ findInHierarchyOrNull(Thread* t, object class_, object name, object spec,
|
||||
inline object
|
||||
findInHierarchy(Thread* t, object class_, object name, object spec,
|
||||
object (*find)(Thread*, object, object, object),
|
||||
Machine::Type errorType)
|
||||
Machine::Type errorType, bool throw_ = true)
|
||||
{
|
||||
object o = findInHierarchyOrNull(t, class_, name, spec, find);
|
||||
|
||||
if (o == 0) {
|
||||
if (throw_ and o == 0) {
|
||||
throwNew(t, errorType, "%s %s not found in %s",
|
||||
&byteArrayBody(t, name, 0),
|
||||
&byteArrayBody(t, spec, 0),
|
||||
@ -3332,93 +3352,208 @@ poolSize(Thread* t, object pool)
|
||||
|
||||
inline object
|
||||
resolveClassInObject(Thread* t, object loader, object container,
|
||||
unsigned classOffset)
|
||||
unsigned classOffset, bool throw_ = true)
|
||||
{
|
||||
object o = cast<object>(container, classOffset);
|
||||
|
||||
loadMemoryBarrier();
|
||||
|
||||
if (objectClass(t, o) == type(t, Machine::ByteArrayType)) {
|
||||
PROTECT(t, container);
|
||||
|
||||
o = resolveClass(t, loader, o);
|
||||
o = resolveClass(t, loader, o, throw_);
|
||||
|
||||
set(t, container, classOffset, o);
|
||||
if (o) {
|
||||
storeStoreMemoryBarrier();
|
||||
|
||||
set(t, container, classOffset, o);
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
inline object
|
||||
resolveClassInPool(Thread* t, object loader, object method, unsigned index)
|
||||
resolveClassInPool(Thread* t, object loader, object method, unsigned index,
|
||||
bool throw_ = true)
|
||||
{
|
||||
object o = singletonObject(t, codePool(t, methodCode(t, method)), index);
|
||||
|
||||
loadMemoryBarrier();
|
||||
|
||||
if (objectClass(t, o) == type(t, Machine::ReferenceType)) {
|
||||
PROTECT(t, method);
|
||||
|
||||
o = resolveClass(t, loader, referenceName(t, o));
|
||||
o = resolveClass(t, loader, referenceName(t, o), throw_);
|
||||
|
||||
set(t, codePool(t, methodCode(t, method)),
|
||||
SingletonBody + (index * BytesPerWord), o);
|
||||
if (o) {
|
||||
storeStoreMemoryBarrier();
|
||||
|
||||
set(t, codePool(t, methodCode(t, method)),
|
||||
SingletonBody + (index * BytesPerWord), o);
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
inline object
|
||||
resolveClassInPool(Thread* t, object method, unsigned index)
|
||||
resolveClassInPool(Thread* t, object method, unsigned index,
|
||||
bool throw_ = true)
|
||||
{
|
||||
return resolveClassInPool(t, classLoader(t, methodClass(t, method)),
|
||||
method, index);
|
||||
method, index, throw_);
|
||||
}
|
||||
|
||||
inline object
|
||||
resolve(Thread* t, object loader, object method, unsigned index,
|
||||
object (*find)(vm::Thread*, object, object, object),
|
||||
Machine::Type errorType)
|
||||
Machine::Type errorType, bool throw_ = true)
|
||||
{
|
||||
object o = singletonObject(t, codePool(t, methodCode(t, method)), index);
|
||||
if (objectClass(t, o) == type(t, Machine::ReferenceType))
|
||||
{
|
||||
|
||||
loadMemoryBarrier();
|
||||
|
||||
if (objectClass(t, o) == type(t, Machine::ReferenceType)) {
|
||||
PROTECT(t, method);
|
||||
|
||||
object reference = o;
|
||||
PROTECT(t, reference);
|
||||
|
||||
object class_ = resolveClassInObject(t, loader, o, ReferenceClass);
|
||||
object class_ = resolveClassInObject(t, loader, o, ReferenceClass, throw_);
|
||||
|
||||
o = findInHierarchy
|
||||
(t, class_, referenceName(t, reference), referenceSpec(t, reference),
|
||||
find, errorType);
|
||||
if (class_) {
|
||||
o = findInHierarchy
|
||||
(t, class_, referenceName(t, reference), referenceSpec(t, reference),
|
||||
find, errorType, throw_);
|
||||
|
||||
set(t, codePool(t, methodCode(t, method)),
|
||||
SingletonBody + (index * BytesPerWord), o);
|
||||
if (o) {
|
||||
storeStoreMemoryBarrier();
|
||||
|
||||
set(t, codePool(t, methodCode(t, method)),
|
||||
SingletonBody + (index * BytesPerWord), o);
|
||||
}
|
||||
} else {
|
||||
o = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
inline object
|
||||
resolveField(Thread* t, object loader, object method, unsigned index)
|
||||
resolveField(Thread* t, object loader, object method, unsigned index,
|
||||
bool throw_ = true)
|
||||
{
|
||||
return resolve(t, loader, method, index, findFieldInClass,
|
||||
Machine::NoSuchFieldErrorType);
|
||||
Machine::NoSuchFieldErrorType, throw_);
|
||||
}
|
||||
|
||||
inline object
|
||||
resolveField(Thread* t, object method, unsigned index)
|
||||
resolveField(Thread* t, object method, unsigned index, bool throw_ = true)
|
||||
{
|
||||
return resolveField
|
||||
(t, classLoader(t, methodClass(t, method)), method, index);
|
||||
(t, classLoader(t, methodClass(t, method)), method, index, throw_);
|
||||
}
|
||||
|
||||
inline void
|
||||
acquireFieldForRead(Thread* t, object field)
|
||||
{
|
||||
if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE)
|
||||
and BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField)))
|
||||
{
|
||||
acquire(t, field);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
releaseFieldForRead(Thread* t, object field)
|
||||
{
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
release(t, field);
|
||||
} else {
|
||||
loadMemoryBarrier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FieldReadResource {
|
||||
public:
|
||||
FieldReadResource(Thread* t, object o): o(o), protector(t, &(this->o)) {
|
||||
acquireFieldForRead(protector.t, o);
|
||||
}
|
||||
|
||||
~FieldReadResource() {
|
||||
releaseFieldForRead(protector.t, o);
|
||||
}
|
||||
|
||||
private:
|
||||
object o;
|
||||
Thread::SingleProtector protector;
|
||||
};
|
||||
|
||||
inline void
|
||||
acquireFieldForWrite(Thread* t, object field)
|
||||
{
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
acquire(t, field);
|
||||
} else {
|
||||
storeStoreMemoryBarrier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
releaseFieldForWrite(Thread* t, object field)
|
||||
{
|
||||
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
|
||||
if (BytesPerWord == 4
|
||||
and (fieldCode(t, field) == DoubleField
|
||||
or fieldCode(t, field) == LongField))
|
||||
{
|
||||
release(t, field);
|
||||
} else {
|
||||
storeLoadMemoryBarrier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FieldWriteResource {
|
||||
public:
|
||||
FieldWriteResource(Thread* t, object o): o(o), protector(t, &(this->o)) {
|
||||
acquireFieldForWrite(protector.t, o);
|
||||
}
|
||||
|
||||
~FieldWriteResource() {
|
||||
releaseFieldForWrite(protector.t, o);
|
||||
}
|
||||
|
||||
private:
|
||||
object o;
|
||||
Thread::SingleProtector protector;
|
||||
};
|
||||
|
||||
inline object
|
||||
resolveMethod(Thread* t, object loader, object method, unsigned index)
|
||||
resolveMethod(Thread* t, object loader, object method, unsigned index,
|
||||
bool throw_ = true)
|
||||
{
|
||||
return resolve(t, loader, method, index, findMethodInClass,
|
||||
Machine::NoSuchMethodErrorType);
|
||||
Machine::NoSuchMethodErrorType, throw_);
|
||||
}
|
||||
|
||||
inline object
|
||||
resolveMethod(Thread* t, object method, unsigned index)
|
||||
resolveMethod(Thread* t, object method, unsigned index, bool throw_ = true)
|
||||
{
|
||||
return resolveMethod
|
||||
(t, classLoader(t, methodClass(t, method)), method, index);
|
||||
(t, classLoader(t, methodClass(t, method)), method, index, throw_);
|
||||
}
|
||||
|
||||
object
|
||||
|
@ -1,5 +1,9 @@
|
||||
THUNK(tryInitClass)
|
||||
THUNK(findInterfaceMethodFromInstance)
|
||||
THUNK(findInterfaceMethodFromInstanceAndReference)
|
||||
THUNK(findSpecialMethodFromReference)
|
||||
THUNK(findStaticMethodFromReference)
|
||||
THUNK(findVirtualMethodFromReference)
|
||||
THUNK(compareDoublesG)
|
||||
THUNK(compareDoublesL)
|
||||
THUNK(compareFloatsG)
|
||||
@ -36,17 +40,31 @@ THUNK(intToFloat)
|
||||
THUNK(longToDouble)
|
||||
THUNK(longToFloat)
|
||||
THUNK(makeBlankObjectArray)
|
||||
THUNK(makeBlankObjectArrayFromReference)
|
||||
THUNK(makeBlankArray)
|
||||
THUNK(lookUpAddress)
|
||||
THUNK(setMaybeNull)
|
||||
THUNK(acquireMonitorForObject)
|
||||
THUNK(releaseMonitorForObject)
|
||||
THUNK(makeMultidimensionalArray)
|
||||
THUNK(makeMultidimensionalArrayFromReference)
|
||||
THUNK(throw_)
|
||||
THUNK(checkCast)
|
||||
THUNK(checkCastFromReference)
|
||||
THUNK(getStaticFieldValueFromReference)
|
||||
THUNK(getFieldValueFromReference)
|
||||
THUNK(setStaticFieldValueFromReference)
|
||||
THUNK(setFieldValueFromReference)
|
||||
THUNK(setStaticLongFieldValueFromReference)
|
||||
THUNK(setLongFieldValueFromReference)
|
||||
THUNK(setStaticObjectFieldValueFromReference)
|
||||
THUNK(setObjectFieldValueFromReference)
|
||||
THUNK(instanceOf64)
|
||||
THUNK(instanceOfFromReference)
|
||||
THUNK(makeNewGeneral64)
|
||||
THUNK(makeNew64)
|
||||
THUNK(makeNewFromReference)
|
||||
THUNK(set)
|
||||
THUNK(getJClass64)
|
||||
THUNK(getJClassFromReference)
|
||||
THUNK(gcIfNecessary)
|
||||
|
@ -33,6 +33,7 @@ class Vector {
|
||||
void dispose() {
|
||||
if (data and minimumCapacity >= 0) {
|
||||
allocator->free(data, capacity);
|
||||
data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,8 @@ class Zone: public Allocator {
|
||||
next = seg->next;
|
||||
allocator->free(seg, sizeof(Segment) + seg->size);
|
||||
}
|
||||
|
||||
segment = 0;
|
||||
}
|
||||
|
||||
bool ensure(unsigned space) {
|
||||
|
184
test/LazyLoading.java
Normal file
184
test/LazyLoading.java
Normal file
@ -0,0 +1,184 @@
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LazyLoading {
|
||||
private static boolean loadLazy;
|
||||
|
||||
private static void expect(boolean v) {
|
||||
if (! v) throw new RuntimeException();
|
||||
}
|
||||
|
||||
private static File findClass(String name, File directory) {
|
||||
for (File file: directory.listFiles()) {
|
||||
if (file.isFile()) {
|
||||
if (file.getName().equals(name + ".class")) {
|
||||
return file;
|
||||
}
|
||||
} else if (file.isDirectory()) {
|
||||
File result = findClass(name, file);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte[] read(File file) throws IOException {
|
||||
byte[] bytes = new byte[(int) file.length()];
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
try {
|
||||
if (in.read(bytes) != (int) file.length()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return bytes;
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Class c = new MyClassLoader(LazyLoading.class.getClassLoader()).loadClass
|
||||
("LazyLoading$Test");
|
||||
|
||||
c.getMethod("test").invoke(null);
|
||||
}
|
||||
|
||||
private static class MyClassLoader extends ClassLoader {
|
||||
public MyClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
protected Class findClass(String name) throws ClassNotFoundException {
|
||||
try {
|
||||
return defineClass
|
||||
(name, read
|
||||
(LazyLoading.findClass
|
||||
(name, new File(System.getProperty("user.dir")))));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Class loadClass(String name) throws ClassNotFoundException {
|
||||
if ("LazyLoading$Test".equals(name)) {
|
||||
return findClass(name);
|
||||
} else if ("LazyLoading$Lazy".equals(name)
|
||||
|| "LazyLoading$Interface".equals(name))
|
||||
{
|
||||
if (loadLazy) {
|
||||
return findClass(name);
|
||||
} else {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
} else {
|
||||
return super.loadClass(name);
|
||||
}
|
||||
}
|
||||
|
||||
private Class defineClass(String name, byte[] bytes) {
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Test {
|
||||
public static void test() {
|
||||
doTest();
|
||||
loadLazy = true;
|
||||
doTest();
|
||||
}
|
||||
|
||||
private static void doTest() {
|
||||
if (loadLazy) {
|
||||
// anewarray
|
||||
Lazy[] array = new Lazy[1];
|
||||
|
||||
// new and invokespecial
|
||||
Object lazy = new Lazy();
|
||||
|
||||
// checkcast
|
||||
array[0] = (Lazy) lazy;
|
||||
|
||||
// instanceof
|
||||
expect(lazy instanceof Lazy);
|
||||
|
||||
// invokeinterface
|
||||
Interface i = array[0];
|
||||
expect(i.interfaceMethod() == 42);
|
||||
|
||||
// invokestatic
|
||||
expect(Lazy.staticMethod() == 43);
|
||||
|
||||
// invokevirtual
|
||||
expect(array[0].virtualMethod() == 44);
|
||||
|
||||
// ldc
|
||||
expect(Lazy.class == lazy.getClass());
|
||||
|
||||
// multianewarray
|
||||
Lazy[][] multiarray = new Lazy[5][6];
|
||||
multiarray[2][3] = array[0];
|
||||
expect(multiarray[2][3] == array[0]);
|
||||
|
||||
// getfield
|
||||
expect(array[0].intField == 45);
|
||||
|
||||
// getstatic
|
||||
expect(Lazy.intStaticField == 46);
|
||||
|
||||
// putfield int
|
||||
array[0].intField = 47;
|
||||
expect(array[0].intField == 47);
|
||||
|
||||
// putfield long
|
||||
array[0].longField = 48;
|
||||
expect(array[0].longField == 48);
|
||||
|
||||
// putfield object
|
||||
Object x = new Object();
|
||||
array[0].objectField = x;
|
||||
expect(array[0].objectField == x);
|
||||
|
||||
// putstatic int
|
||||
array[0].intStaticField = 49;
|
||||
expect(array[0].intStaticField == 49);
|
||||
|
||||
// putstatic long
|
||||
array[0].longStaticField = 50;
|
||||
expect(array[0].longStaticField == 50);
|
||||
|
||||
// putstatic object
|
||||
Object y = new Object();
|
||||
array[0].objectStaticField = y;
|
||||
expect(array[0].objectStaticField == y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface Interface {
|
||||
public int interfaceMethod();
|
||||
}
|
||||
|
||||
private static class Lazy implements Interface {
|
||||
public static int intStaticField = 46;
|
||||
public static long longStaticField;
|
||||
public static Object objectStaticField;
|
||||
|
||||
public int intField = 45;
|
||||
public long longField;
|
||||
public Object objectField;
|
||||
|
||||
public int interfaceMethod() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
public static int staticMethod() {
|
||||
return 43;
|
||||
}
|
||||
|
||||
public int virtualMethod() {
|
||||
return 44;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user