// $Header: /m/src/ns/sun-java/jit/win32/Attic/jinterf.c,v 1.9.8.2 1996/09/30 02:46:51 dhopwood Exp $
/*
* Copyright (c) 1996 Borland International. All Rights Reserved.
*
* AppAccelerator(tm) for x86
*
* JInterf.c, R. Crelier, 9/24/96
*
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "oobj.h"
#include "interpreter.h"
#include "exceptions.h"
#include "jinterf.h"
static char logo0 [] = "AppAccelerator(tm) 1.0.2a for Java, x86 version.\n";
static char logo1 [] = "Copyright (c) 1996 Borland International. All Rights Reserved.\n";
//#define CHECK_REGISTRY
#ifdef CHECK_REGISTRY
static HKEY baseKey1 = HKEY_LOCAL_MACHINE;
static char regKeyStr1[] = "Software\\Borland\\AppAccelerator(tm)\\1.0\\Licensed Software\\Borland C++ 5.0 Suite";
static HKEY baseKey2 = HKEY_CLASSES_ROOT;
static char regKeyStr2[] = "AppAccelerator(tm), Borland International, Inc.";
static char regKeyStr3[] = "B\\CHG_~JD 2V:!998HM1/>#OG#<0>O95\">1W9;<5M56t";
//static char regKeyStr3[] = "CLSID\\{FA23B531-5AA1-11CF-807A-0000C0424A96}";
#endif
/* Undefine this variable for freeing the bytecode after a compilation.
* Has no effect if a control panel is present
*/
#define KEEP_BYTECODE
extern void
InitCompiler(void);
extern bool_t
jitCompile(struct methodblock *mb, void **code, void **info, char **err, ExecEnv *ee);
#ifdef BINARY_DEBUG
static unsigned count = 0; // number of succesfully compiled methods
#endif
static int
CompilerEnabled = 0;
/* Declaration of the links to the virtual machine */
static int
*p_JavaVersion = NULL; // NULL indicates that the VM is not linked
static void
(**pp_InitializeForCompiler)(ClassClass *cb);
static bool_t
(**pp_invokeCompiledMethod)(JHandle *o, struct methodblock *mb,
int args_size, ExecEnv *ee);
static void
(**pp_CompiledCodeSignalHandler)(int sig, void *info, void *uc);
static void
(**pp_CompilerFreeClass)(ClassClass *cb);
#ifdef IN_NETSCAPE_NAVIGATOR
static void
(**pp_CompilerFreeMethod)(struct methodblock *mb);
#endif
static bool_t
(**pp_CompilerCompileClass)(ClassClass *cb);
static bool_t
(**pp_CompilercompileClasses)(Hjava_lang_String *name);
static Hjava_lang_Object *
(**pp_CompilerCommand)(Hjava_lang_Object *any);
static void
(**pp_CompilerEnable)();
static void
(**pp_CompilerDisable)();
static bool_t
(**pp_PCinCompiledCode)(unsigned char *pc, struct methodblock *mb);
static unsigned char *
(**pp_CompiledCodePC)(JavaFrame *frame, struct methodblock *mb);
static void
(**pp_ReadInCompiledCode)(void *context,
struct methodblock *mb,
int attribute_length,
unsigned long (*get1byte)(),
unsigned long (*get2bytes)(),
unsigned long (*get4bytes)(),
void (*getNbytes)());
static JavaFrame *
(**pp_CompiledFramePrev)(JavaFrame *frame, JavaFrame *buf);
#ifdef IN_NETSCAPE_NAVIGATOR
static void
(**pp_SetCompiledFrameAnnotation)(JavaFrame *frame, JHandle *annotation);
#endif
static char *
*p_CompiledCodeAttribute;
static int
*p_UseLosslessQuickOpcodes;
void *
(*p_malloc)(size_t size);
void *
(*p_calloc)(size_t nitems, size_t size);
void *
(*p_realloc)(void *block, size_t size);
void
(*p_free)(void *block);
static ClassClass **
*p_binclasses;
static long
*p_nbinclasses;
static void
(*p_lock_classes)(void);
static void
(*p_unlock_classes)(void);
static ClassClass *
*p_classJavaLangClass;
static ClassClass *
*p_classJavaLangObject;
static ClassClass *
*p_classJavaLangString;
static ClassClass *
*p_classJavaLangThrowable;
static ClassClass *
*p_classJavaLangException;
static ClassClass *
*p_classJavaLangRuntimeException;
static ClassClass *
*p_interfaceJavaLangCloneable;
ExecEnv *
(*p_EE)(void);
void
(*p_SignalError)(struct execenv *ee, char *ename, char *DetailMessage);
static JHandle *
(*p_exceptionInternalObject)(internal_exception_t exc);
char *
(*p_GetClassConstantClassName)(cp_item_type *constant_pool, int index);
bool_t
(*p_ResolveClassConstant)(cp_item_type *constant_pool, unsigned index,
struct execenv *ee, unsigned mask);
bool_t
(*p_ResolveClassConstantFromClass)(ClassClass *class, unsigned index,
struct execenv *ee, unsigned mask);
bool_t
(*p_VerifyClassAccess)(ClassClass *current_class, ClassClass *new_class,
bool_t classloader_only);
ClassClass *
(*p_FindClass)(struct execenv *ee, char *name, bool_t resolve);
ClassClass *
(*p_FindClassFromClass)(struct execenv *ee, char *name,
bool_t resolve, ClassClass *from);
bool_t
(*p_dynoLink)(struct methodblock *mb);
long
(*p_do_execute_java_method_vararg)(ExecEnv *ee, void *obj,
char *method_name, char *signature,
struct methodblock *mb,
bool_t isStaticCall, va_list args,
long *highBits, bool_t shortFloats);
bool_t
(*p_is_subclass_of)(ClassClass *cb, ClassClass *dcb, ExecEnv *ee);
static bool_t
(*p_invokeJavaMethod)(JHandle *o, struct methodblock *mb,
int args_size, ExecEnv *ee);
static bool_t
(*p_invokeSynchronizedJavaMethod)(JHandle *o, struct methodblock *mb,
int args_size, ExecEnv *ee);
static bool_t
(*p_invokeAbstractMethod)(JHandle *o, struct methodblock *mb,
int args_size, ExecEnv *ee);
static bool_t
(*p_invokeLazyNativeMethod)(JHandle *o, struct methodblock *mb,
int args_size, ExecEnv *ee);
static bool_t
(*p_invokeSynchronizedNativeMethod)(JHandle *o, struct methodblock *mb,
int args_size, ExecEnv *ee);
static bool_t
(*p_invokeCompiledMethod)(JHandle *o, struct methodblock *mb,
int args_size, ExecEnv *ee);
void
(*p_monitorEnter)(unsigned int key);
void
(*p_monitorExit)(unsigned int key);
void
(*p_monitorRegister)(sys_mon_t *mid, char *name);
int
(*p_sysMonitorSizeof)(void);
int
(*p_sysMonitorEnter)(sys_mon_t *mid);
int
(*p_sysMonitorExit)(sys_mon_t *mid);
HObject *
(*p_ObjAlloc)(ClassClass *cb, long n0);
HObject *
(*p_ArrayAlloc)(int t, int l);
HObject *
(*p_MultiArrayAlloc)(int dimensions, ClassClass *array_cb, stack_item *sizes);
int
(*p_sizearray)(int t, int l);
HObject *
(*p_newobject)(ClassClass *cb, unsigned char *pc, struct execenv *ee);
bool_t
(*p_is_instance_of)(JHandle *h, ClassClass *dcb, ExecEnv *ee);
int
(*p_jio_snprintf)(char *str, size_t count, const char *fmt, ...);
static char *
(*p_javaString2CString)(Hjava_lang_String *s, char *buf, int buflen);
char *
(*p_classname2string)(char *str, char *dst, int size);
char *
(*p_IDToNameAndTypeString)(unsigned short ID);
char *
(*p_IDToType)(unsigned short ID);
static void
(*p_DumpThreads)(void);
static void
(*p_printToConsole)(const char *);
static JavaStack *
(*p_CreateNewJavaStack)(ExecEnv *ee, JavaStack *previous_stack);
static long
(*p_execute_java_static_method)(ExecEnv *ee, ClassClass *cb,
char *method_name, char *signature, ...);
static bool_t
(*p_ExecuteJava)(unsigned char *initial_pc, ExecEnv *ee);
static long
(*p_now)(void);
static int
*p_java_monitor;
static void
(*p_java_mon)(struct methodblock *caller, struct methodblock *callee, int time);
static unsigned long
*p_JavaStackSize;
/* Declaration of the calls to the control panel */
static void
(*p_notifyCompilation)(struct methodblock *mb, char *err, bool_t *freeByteCode);
static void
(*p_notifyClassInit)(ClassClass *cb);
static void
(*p_notifyClassFree)(ClassClass *cb);
static char *DefaultControlName = "JAVACTRL.DLL";
static char *ControlEnvVarName = "JAVA_CONTROL";
static HMODULE controlHandle = 0;
/* local copy of class java.lang.object */
ClassClass *
classJavaLangObject;
/* local copy of class java.lang.Cloneable */
ClassClass *
interfaceJavaLangCloneable;
/* The methodtable for java.lang.object */
struct methodtable *ObjectMethodTable;
/*
* Lock around changing a method invoker and accessing the code_ranges array
*/
static sys_mon_t *_invoker_lock;
#define INVOKER_LOCK_INIT() (*p_monitorRegister)(_invoker_lock, "Invoker change lock")
#define INVOKER_LOCK() (*p_sysMonitorEnter)(_invoker_lock)
#define INVOKER_UNLOCK() (*p_sysMonitorExit)(_invoker_lock)
/* Data structure and function to map a pc to a compiled method.
* This could be avoided by introducing a frame pointer to crawl
* the compiled stack. We don't have to unwind the stack for handling
* exceptions, since we map exceptions to native exceptions.
* However, we need to be able to crawl the stack to correctly implement
* Throwable.fillInStackTrace and different methods of SecurityManager.
* Last bit of efficiency is not an issue here.
*/
typedef struct CodeRange
{
unsigned char *start_pc;
unsigned char *end_pc;
struct methodblock *mb;
} CodeRange;
static CodeRange *
code_ranges = 0;
static int
ncode_ranges = 0;
static int
max_ncode_ranges = 1000;
static unsigned char *
StubRetAddr;
static struct methodblock *
PC2CompiledMethod(unsigned char *pc)
{
int i, l, r;
CodeRange *range;
struct methodblock *mb = 0;
INVOKER_LOCK();
l = 0;
r = ncode_ranges - 1;
while (l <= r)
{
i = (l + r)/2;
range = code_ranges + i;
if (pc >= range->end_pc)
l = i + 1;
else if (pc < range->start_pc)
r = i - 1;
else
{
mb = range->mb;
break;
}
}
INVOKER_UNLOCK();
return mb;
}
/* Native code expects a JavaFrame for the caller on the interpreter stack.
* It is used to check access rights and to fill the stack trace in case of
* an exception.
* We also want to walk the stack (both compiled and interpreted) for security
* checks. So we push a JavaFrame before calling into the interpreter from
* compiled code in order to find the compiled frame from the interpreted frame.
* stub_base points to the return address into the caller.
*/
static void __stdcall
PushJavaFrame(unsigned char *stub_base,
struct methodblock *caller_mb,
long stub_nargs,
ExecEnv *ee)
{
int nlocals = caller_mb->nlocals;
JavaFrame *old_frame = ee->current_frame;
stack_item *optop = old_frame->optop;
JavaStack *javastack = old_frame->javastack;
JavaFrame *frame = (JavaFrame *)(optop + nlocals);
CodeInfo *ci = (CodeInfo *)caller_mb->CompiledCodeInfo;
unsigned char *caller_base;
// optop points to the first argument passed by the owner of the last
// pushed frame, i.e. the last interpreted method.
// These arguments are consumed by the compiled code already.
// The real arguments for the callee are on the compiled stack.
sysAssert(caller_mb->fb.access & ACC_MACHINE_COMPILED &&
PC2CompiledMethod(*(unsigned char **)stub_base) == caller_mb);
if (frame->ostack + caller_mb->maxstack > javastack->end_data)
{
if (javastack->stack_so_far >= *p_JavaStackSize)
{
(*p_SignalError)(ee, JAVAPKG "StackOverflowError", 0);
ErrorUnwind(ee); // do not try SignalErrorUnwind
}
javastack = javastack->next
? javastack->next : (*p_CreateNewJavaStack)(ee, javastack);
if (javastack == 0)
{
(*p_SignalError)(ee, JAVAPKG "OutOfMemoryError", 0);
ErrorUnwind(ee); // do not try SignalErrorUnwind
}
frame = (JavaFrame *)(javastack->data + nlocals);
// no arguments to copy
optop = javastack->data;
}
frame->vars = optop;
frame->javastack = javastack;
frame->prev = old_frame;
frame->optop = frame->ostack;
frame->current_method = caller_mb;
frame->constant_pool = 0; // not used
// so that the method is recognized by PCinCompiledCode:
frame->lastpc = *(unsigned char **)stub_base;
frame->monitor = 0;
caller_base = stub_base + 4*stub_nargs + ci->localSize + ci->frameSize;
frame->returnpc = caller_base;
#ifdef IN_NETSCAPE_NAVIGATOR
frame->annotation = *((JHandle **)(caller_base - ci->frameSize));
#endif
if (caller_mb->args_size) {
(optop++)->p = *(unsigned char **)(caller_base + 4); // caller's this, passed in memory
nlocals--;
}
// clear locals to reduce false gc roots
while (nlocals--) (optop++)->p = 0;
ee->current_frame = frame;
}
#define UNWINDING 2
#define UNWINDING_FOR_EXIT 4
#define UNWIND_IN_PROGRESS (UNWINDING + UNWINDING_FOR_EXIT)
/* Our Java exception code */
#define EXCEPTION_JAVA 0xcafedead
/* Our Java exception registration record */
typedef struct _EXCEPTION_FRAME {
struct _EXCEPTION_FRAME *prev;
PVOID handler;
} EXCEPTION_FRAME, *PEXCEPTION_FRAME;
/* Compiled methods use __stdcall calling convention, this simplifies
* the interfacing with native methods which use __cdecl.
* The method block pointer is always passed in edx.
method call:
push last param
...
mov reg, this
push reg
mov edx, [reg]Hjava_lang_Object.methods
mov edx, [edx]methodtable.methods[slot]
mov ecx, caller_mb
call dword ptr [edx]methodblock.CompiledCode
// edx is an implicite parameter
try:
mov dword ptr [esp + localSize + 4], new_xctxt
...
mov dword ptr [esp + localSize + 4], old_xctxt
catch:
exception object is passed in eax
method prologue (with exception handling, or synchronized):
push ebx
push esi
push edi
xor eax, eax
push ebp
push offset HandleException
push dword ptr fs:[eax]
mov dword ptr fs:[eax], esp
push xctxt // xctxt of first opcode
push edx // mb parameter
sub esp, -xframe2esp-8 // esp = bottom of operand stack (- 0, 4, or 8,
// i.e. max function result size, may be trashed
// by native call results)
epilogue:
add esp, -xframe2esp
pop dword ptr fs:[0]
pop ecx
pop ebp
pop edi
pop esi
pop ebx
ret 4*nargs
method prologue (without exception handling, and not synchronized):
push callee-saved // as used
push edx // if mb used (never if jit compilation)
sub esp, localSize // if non-zero
epilogue:
add esp, localSize // (+4 if mb used) if non-zero
pop callee-saved // as used
ret 4*nargs
*/
extern VOID __stdcall
RtlUnwind(PEXCEPTION_FRAME ef, DWORD ra, PEXCEPTION_RECORD er, DWORD dw);
static JHandle * __stdcall
InstantiateExceptionObject(char *ename)
{
ExecEnv *ee = (*p_EE)();
ClassClass *cb;
JHandle *ret;
if ((cb = (*p_FindClassFromClass)(ee, ename, TRUE, NULL)) == 0 &&
(cb = (*p_FindClassFromClass)(ee, JAVAPKG "UnknownException", TRUE, NULL)) == 0)
return (*p_exceptionInternalObject)(IEXC_NoClassDefinitionFound);
if ((ret = (*p_ObjAlloc)(cb, 0)) == 0)
return (*p_exceptionInternalObject)(IEXC_OutOfMemory);
memset((char *)(unhand(ret)), 0, cbInstanceSize(cb));
return ret;
}
/* This function translates a native exception code to a Java exception name.
*/
static JHandle * __stdcall
GetJavaExceptionObject(DWORD exceptionCode, PCONTEXT pcontext)
{
struct methodblock *mb;
unsigned char *ra, *base, *stub_base;
long stub_nargs, espLevel;
ExecEnv *ee;
JavaFrame *frame;
char *ename = JAVAPKG "UnknownException";
char *message = 0;
if ((unsigned)exceptionCode < EXCEPTION_ACCESS_VIOLATION)
{
/* switch (exceptionCode)
{
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_BREAKPOINT:
case EXCEPTION_SINGLE_STEP:
case EXCEPTION_GUARD_PAGE:
ename = JAVAPKG "UnknownException"; break;
}
*/ }
else if ((unsigned)exceptionCode < EXCEPTION_ARRAY_BOUNDS_EXCEEDED)
{
switch (exceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
ename = JAVAPKG "NullPointerException"; break;
/* case EXCEPTION_IN_PAGE_ERROR:
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
case EXCEPTION_INVALID_DISPOSITION:
ename = JAVAPKG "UnknownException"; break;
*/ }
}
else if ((unsigned)exceptionCode < EXCEPTION_STACK_OVERFLOW)
{
switch (exceptionCode)
{
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
ename = JAVAPKG "ArrayIndexOutOfBoundsException"; break;
// An EXCEPTION_FLT_xxxx occurs only if its corresponding bit in the
// fpu control word is cleared. Will never occur for Java real or double
// arithmetic, but may occur for Java long (64-bit) division and
// remainder which are computed by the fpu.
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
message = "/ by zero";
// fall thru
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_STACK_CHECK:
case EXCEPTION_FLT_UNDERFLOW:
case EXCEPTION_INT_OVERFLOW:
ename = JAVAPKG "ArithmeticException"; break;
/* case EXCEPTION_PRIV_INSTRUCTION:
ename = JAVAPKG "UnknownException"; break;
*/ }
}
else if (exceptionCode == EXCEPTION_STACK_OVERFLOW)
ename = JAVAPKG "StackOverflowException";
/* else if (exceptionCode == CONTROL_C_EXIT)
ename = JAVAPKG "UnknownException";
*/
/* We could simply call
*
* return InstantiateExceptionObject(ename);
*
* but we would not have a stack trace.
* Instead, we try to call SignalError after pushing a JavaFrame.
* We have to be careful only to push a frame if we are executing
* in a compiled method.
*/
mb = PC2CompiledMethod((unsigned char *)pcontext->Eip);
if (mb)
{
base = (unsigned char *)pcontext->Esp - 4 +
((CodeInfo *)mb->CompiledCodeInfo)->localSize +
((CodeInfo *)mb->CompiledCodeInfo)->frameSize;
espLevel = 0;
while (espLevel > -64)
{
ra = *(unsigned char **)(base - espLevel);
if (ra == StubRetAddr || PC2CompiledMethod(ra))
{
ee = (*p_EE)();
frame = ee->current_frame;
// stub_base must point to a return address in mb:
stub_base = (unsigned char *)&pcontext->Eip;
// stub_nargs is the number of dwords between stub_base and esp - 4:
stub_nargs = ((unsigned char *)pcontext->Esp - espLevel - stub_base)/4 - 1;
PushJavaFrame(stub_base, mb, stub_nargs, ee);
(*p_SignalError)(ee, ename, message);
ee->current_frame = frame; // pop frame before handling native exception
return ee->exception.exc; // don't need to clear exception, done in handler
}
espLevel -= 4;
}
}
return InstantiateExceptionObject(ename);
}
/* The following code is derived from, and modeled after ProcedureFindThrowTag()
in runtime/interpreter.c. Common code should be factored out, so that the near
cut/paste status can be discontinued (and bugs fixed in one place) */
static void * __stdcall
GetJavaExceptionHandler(long pcTarget, struct methodblock *mb, JHandle *object)
{
/* Search current class for catch frame, based on pc value,
matching specified type. */
ClassClass *methodClass = fieldclass(&mb->fb);
ClassClass *objectClass = obj_array_classblock(object);
struct CatchFrame *cf = mb->exception_table;
struct CatchFrame *cfEnd = cf + mb->exception_table_length;
cp_item_type *constant_pool = methodClass->constantpool;
for (; cf < cfEnd; cf++)
{
if (cf->start_pc <= pcTarget && pcTarget < cf->end_pc)
{
/* Is the class name a superclass of object's class. */
if (cf->catchType == 0)
/* Special hack put in for finally. Always succeed. */
goto found;
else
{
/* The usual case */
int catchType = cf->catchType;
char *catchName =
(*p_GetClassConstantClassName)(constant_pool, catchType);
ClassClass *catchClass = NULL;
ClassClass *cb;
for (cb = objectClass;; cb = unhand(cbSuperclass(cb)))
{
if (strcmp(classname(cb), catchName) == 0)
{
if (cbLoader(cb) == cbLoader(methodClass))
goto found;
if (catchClass == NULL)
{
/* I haven't yet found the actual
* class corresponding to catchType, but it's
* got the right name. Find the actual class */
if (!(*p_ResolveClassConstantFromClass)(
methodClass, catchType, (*p_EE)(),
1 << CONSTANT_Class))
return 0;
catchClass =
(ClassClass *)(constant_pool[catchType].p);
}
if (cb == catchClass)
goto found;
}
if (cbSuperclass(cb) == 0)
/* Not found. Try next catch frame. */
break;
}
}
}
}
/* not found in this execution environment */
return 0;
found:
return (char *)mb->CompiledCode + (long)cf->compiled_CatchFrame;
// compiled_CatchFrame is actually the relative
// address of the compiled handler
}
void __stdcall
ErrorUnwind(struct execenv *ee)
{
/* This function does not unwind frames as the solaris version does.
It just raises a native exception. Exception handlers will do unwinding
and code cleanup.
However, when the exception drops on the floor, a stack trace is available
if ErrorUnwind was called by SignalErrorUnwind.
*/
if (!ee)
ee = (*p_EE)();
if (ee->exceptionKind == EXCKIND_STKOVRFLW)
// We hope we have now enough stack space to raise the exception.
// If not, the OS will do it for us.
ee->exception.exc = InstantiateExceptionObject(JAVAPKG "StackOverflowException");
RaiseException(EXCEPTION_JAVA, EXCEPTION_NONCONTINUABLE, 1, &((DWORD)ee->exception.exc));
}
void __stdcall
SignalErrorUnwind(struct execenv *ee, char *ename, char *DetailMessage,
unsigned char *stub_base, long stub_nargs)
{
JavaFrame *frame;
struct methodblock *caller_mb;
if (!ee)
ee = (*p_EE)();
caller_mb = PC2CompiledMethod(*(unsigned char **)stub_base);
sysAssert(caller_mb != 0);
frame = ee->current_frame;
PushJavaFrame(stub_base, caller_mb, stub_nargs, ee);
(*p_SignalError)(ee, ename, DetailMessage);
ee->current_frame = frame; // pop frame before raising native exception
ErrorUnwind(ee);
}
static void __stdcall
CompiledCodeCleanup(struct methodblock *mb, HObject *o)
{
if (mb->fb.access & ACC_SYNCHRONIZED)
if (mb->fb.access & ACC_STATIC)
(*p_monitorExit)(obj_monitor(cbHandle(fieldclass(&mb->fb))));
else
(*p_monitorExit)(obj_monitor(o));
}
typedef struct methodblock methodblock; /* for use in in-line assembly */
typedef struct fieldblock fieldblock; /* for use in in-line assembly */
__declspec(naked) int
HandleException(void)
// [esp+ 4] PEXCEPTION_RECORD
// [esp+ 8] PEXCEPTION_FRAME
// [esp+12] PCONTEXT
// [esp+16] PVOID
{
__asm {
mov eax, [esp+4]
test dword ptr [eax]EXCEPTION_RECORD.ExceptionFlags, UNWIND_IN_PROGRESS
jne Unwinding
mov ecx, [eax]EXCEPTION_RECORD.ExceptionCode
cmp ecx, EXCEPTION_JAVA
jne HandleNonJavaException
mov eax, [eax]EXCEPTION_RECORD.ExceptionInformation[0] // exception object
jmp FindHandler
HandleNonJavaException:
mov eax, [esp+12]
push eax
push ecx
call GetJavaExceptionObject
FindHandler:
mov edx, [esp+8]
push eax // save exception object
mov ecx, [edx-8] // mb
mov edx, [edx-4] // xctxt
cmp edx, 0
jl NoHandler
push eax // object
push ecx // mb
push edx // xctxt
call GetJavaExceptionHandler
cmp eax, 0
jne Unwind // handler found, call RtlUnwind
NoHandler:
mov eax, [esp+4+4]
mov ecx, [eax]EXCEPTION_RECORD.ExceptionCode
cmp ecx, EXCEPTION_JAVA
jne ReRaise // to avoid same conversion again
pop ecx // discard exception object
jmp Return1
ReRaise:
push esp // address of exception object
push 1
push EXCEPTION_NONCONTINUABLE
push EXCEPTION_JAVA
push [eax]EXCEPTION_RECORD.ExceptionAddress // user address
jmp dword ptr [RaiseException]
Unwind:
push eax // handler address
finit // 0x137F, round to nearest, extended precision, no exceptions
fwait
push 0x127F // round to nearest, double precision, no exceptions
fldcw [esp]
pop ecx
mov eax, [esp+4+8]
mov edx, [esp+8+8]
or dword ptr [eax]EXCEPTION_RECORD.ExceptionFlags, UNWINDING
push 0
push eax
push offset ReturnInHandleException
push edx
call RtlUnwind
ReturnInHandleException:
// clear exception
call dword ptr [p_EE]
mov byte ptr [eax]ExecEnv.exceptionKind, EXCKIND_NONE
pop ecx // handler address
pop eax // exception object
mov edx, [esp+8] // edx points to the exception frame
mov ebp, [edx-8] // ebp = mb
mov ebp, [ebp]methodblock.CompiledCodeInfo
mov ebp, [ebp]CodeInfo.xframe2esp
add ebp, edx // ebp += exception frame address
mov esp, ebp // ebp is used as a scratch register here
jmp ecx // eax contains the exception object
Unwinding:
mov edx, [esp+8]
mov eax, [edx-4]
cmp eax, NO_CLEANUP // if exception signaled in CompSupport_monitorexit
je Return1 // no cleanup
mov eax, [edx+28] // this if virtual, else not used
mov ecx, [edx-8] // mb
push eax
push ecx
call CompiledCodeCleanup
Return1:
mov eax, 1
ret
}
}
/* ReportException catches every exception, performs a global unwind,
* translates a native exception to a Java exception (if necessary), reports
* it into the current execenv, puts the fpu into a consistent state, pushes a
* dummy result onto the fpu stack if necessary, restores registers, and returns
* from the __stdcall stub.
*/
__declspec(naked) static int
ReportException(void)
// [esp+ 4] PEXCEPTION_RECORD
// [esp+ 8] PSTUB_EXCEPTION_FRAME
// [esp+12] PCONTEXT
// [esp+16] PVOID
{
__asm {
mov eax, [esp+4]
test dword ptr [eax]EXCEPTION_RECORD.ExceptionFlags, UNWIND_IN_PROGRESS
jne GiveUp /* Something really bad must have happened:
* we should never see unwinding here, since we
* catch everything.
*/
mov ecx, [eax]EXCEPTION_RECORD.ExceptionCode
cmp ecx, EXCEPTION_JAVA
jne HandleNonJavaException
mov eax, [eax]EXCEPTION_RECORD.ExceptionInformation[0] // exception object
jmp Unwind
HandleNonJavaException:
mov eax, [esp+12]
push eax
push ecx
call GetJavaExceptionObject
Unwind:
push eax // save exception object
finit // 0x137F, round to nearest, extended precision, no exceptions
fwait
push 0x127F // round to nearest, double precision, no exceptions
fldcw [esp]
pop ecx
mov eax, [esp+4+4]
mov edx, [esp+8+4]
or dword ptr [eax]EXCEPTION_RECORD.ExceptionFlags, UNWINDING
push 0
push eax
push offset ReturnInReportException
push edx
call RtlUnwind
ReturnInReportException:
// If the method returns a float (flag == 2) or a double (flag == 3),
// push 0 onto the fpu stack.
// This is necessary only if the invalid op exception is enabled,
// but we do it anyway.
mov eax, [esp+8+4] // eax points to the exception frame
mov eax, [eax-4] // mb (no xframe2esp, nor xctxt in stub)
mov eax, [eax]methodblock.CompiledCodeFlags
and eax, 7
sub eax, 2
cmp eax, 1
ja SetExecEnv
fldz // the fpu stack needs to hold a result
SetExecEnv:
call dword ptr [p_EE]
mov byte ptr [eax]ExecEnv.exceptionKind, EXCKIND_THROW
pop ecx
mov [eax]ExecEnv.exception.exc, ecx
mov esp, [esp+8] // esp = exception frame address
// same epilogue as in InvokeCompiledMethodStub:
pop dword ptr fs:[0] // unlink exception frame
pop ecx // discard handler address
pop ebp
pop edi
pop esi
pop ebx
ret 12
GiveUp:
mov eax, 1
ret
}
}
/* InvokeCompiledMethodStub(mb, optop, args_size) is used to
* trampoline from interpreted code to compiled code. It is called using
* in-line assembly, because the return value type is variable.
* This function also catches any exception raised in the invoked method and
* reports it into the current execenv before returning.
*/
__declspec(naked) static void __stdcall
InvokeCompiledMethodStub(struct methodblock *mb, stack_item *optop,
int argument_count)
{
// -> [esp+4 ] struct methodblock *mb
// -> [esp+8 ] stack_item *optop
// -> [esp+12] int argument_count
__asm {
push ebx // Callee-saved registers have to be saved
push esi // because they need to be restored in case
push edi // of an exception.
push ebp
xor eax, eax
mov edx, [esp+16+4 ] // method block
mov ebx, [edx]methodblock.CompiledCode // code address
mov ecx, [esp+16+12] // argument count
push offset ReportException
push dword ptr fs:[eax]
mov dword ptr fs:[eax], esp
push edx // mb for ReportException
sub ecx, 1
jl CallMethod // no arguments
mov esi, [esp+28+8] // pointer to arguments
CopyArgs:
mov eax, [esi+ecx*4]
sub ecx, 1
push eax
jge CopyArgs
CallMethod:
mov StubRetAddr, offset ReturnInStub
call ebx // call the __stdcall method, mb is in edx
ReturnInStub: // result may be in eax (and edx), or st(0)
pop ecx // discard mb
pop dword ptr fs:[0] // unlink exception frame
pop ecx // discard handler address
pop ebp
pop edi
pop esi
pop ebx
ret 12
}
}
/* These functions are used to trampoline from compiled code to interpreted
* code. They are stored in the CompiledCode slot of an interpreted method.
*/
__declspec(naked) static void __stdcall
CallbackInterpretedMethod()
{
// -> edx callee mb
// -> ecx caller mb
// -> ebx args
// -> ebp isStaticCall
// -> [esp+20+4*i] arg(i), i = 0, 1, ...
// <- reg(s) method result, if any
__asm {
mov edi, edx // save callee mb
mov esi, ecx // save caller mb
call dword ptr [p_EE]
lea edx, [esp+16] // stub base
push eax // ee
movzx ecx, word ptr [edi]methodblock.args_size
push ecx // callee->args_size
push esi // caller
push edx // stub base
mov esi, eax // save ee
call PushJavaFrame
push eax // place holder for hiBits
push eax // place holder for lowBits
mov edx, esp // &lowBits
// call the interpreted method:
// do_execute_java_method_vararg(ExecEnv *ee, void *obj,
// char *method_name, char *method_signature,
// struct methodblock *mb,
// bool_t isStaticCall, va_list args,
// long *otherBits, bool_t shortFloats)
push 1 // shortFloats param == true
push edx // otherBits
push ebx // args
push ebp // isStaticCall
push edi // mb
mov edx, [edx+20+8] // obj = arg0, not used if isStaticCall
push 0 // method_signature (not needed)
push 0 // method_name (not needed)
push edx // obj
push esi // ee
call dword ptr [p_do_execute_java_method_vararg]
add esp, 36
// pop JavaFrame
mov edx, [esi]ExecEnv.current_frame
mov ecx, [edx]JavaFrame.prev
mov [esi]ExecEnv.current_frame, ecx
// if there is an error reported in the ee, then raise an exception
cmp byte ptr [esi]ExecEnv.exceptionKind, 0
jne CallErrorUnwind // indirect for breakpoint setting
mov ecx, [edi]methodblock.CompiledCodeFlags
// 32-bit result is returned in eax
// 64-bit result is returned in eax (hiBits) and [esp] (lowBits)
// move result into result reg(s) according to ecx:
// 0 for returning an int
// 1 for returning a long
// 2 for returning a float
// 3 for returning a double
// 4 for returning a void
and ecx, 3 // test 2 bits, only, handle void like int
cmp ecx, 1
jl Return // int in eax or void
jg Float_Double
mov edx, eax // hiBits were returned in eax
pop eax // lowBits at [esp]
pop ecx
// this is tricky, we need to do a __stdcall return, but the number
// of args is variable, eax and edx are live, only ecx is available,
// so we move the return address
movzx ecx, word ptr [edi]methodblock.args_size
mov esi, [esp+16] // read return address
mov [esp+ecx*4+16], esi // write return address
pop ebp
pop edi
pop esi
pop ebx
lea esp, [esp+ecx*4]
ret
Float_Double:
cmp ecx, 3
je Double
mov [esp], eax // lowBits
fld dword ptr [esp]
jmp Return
Double:
mov [esp+4], eax // hiBits
fld qword ptr [esp]
Return:
pop ecx // discard lowBits
pop ecx // discard hiBits
movzx ecx, word ptr [edi]methodblock.args_size
pop ebp
pop edi
pop esi
pop ebx
pop edx // return address
lea esp, [esp+ecx*4]
jmp edx
CallErrorUnwind:
push esi
call ErrorUnwind
}
}
__declspec(naked) static void __stdcall
CallbackDynamicMethod()
{
// -> edx callee mb
// -> ecx caller mb
// -> [esp+4+4*i] arg(i), i = 0, 1, ...
// <- reg(s) method result, if any
__asm {
push ebx
push esi
push edi
push ebp
lea ebx, [esp+24] // &arg1
mov ebp, 0 // isStaticCall = false
jmp CallbackInterpretedMethod
}
}
__declspec(naked) static void __stdcall
CallbackStaticMethod()
{
// -> edx callee mb
// -> ecx caller mb
// -> [esp+4+4*i] arg(i), i = 0, 1, ...
// <- reg(s) method result, if any
__asm {
push ebx
push esi
push edi
push ebp
lea ebx, [esp+20] // &arg0
mov ebp, 1 // isStaticCall = true
jmp CallbackInterpretedMethod
}
}
/* Returns the address of the native code stub after loading it if necessary.
*/
static void * __stdcall
GetStubAddr(struct methodblock *mb)
{
if (mb->code == 0)
{
(*p_dynoLink)(mb);
if (mb->code == 0)
{
ExecEnv *ee = (*p_EE)();
// JavaFrame is already pushed for correct stack trace
(*p_SignalError)(ee, JAVAPKG "UnsatisfiedLinkError", fieldname(&mb->fb));
// pop frame before raising native exception
ee->current_frame = ee->current_frame->prev;
ErrorUnwind(ee);
}
}
return mb->code;
}
__declspec(naked) static void __stdcall
exitNativeInvoker()
{
// -> ecx returned top of operand stack
// <- reg(s) method result, if any
__asm {
// pop JavaFrame
mov edx, [esi]ExecEnv.current_frame
mov eax, [edx]JavaFrame.prev
mov [esi]ExecEnv.current_frame, eax
// if there is an error reported in the ee, then raise an exception
cmp byte ptr [esi]ExecEnv.exceptionKind, 0
jne CallErrorUnwind // indirect for breakpoint setting
mov esi, [edi]methodblock.CompiledCodeFlags
// read result from [ecx] into result reg(s) according to esi:
// 0 for returning an int
// 1 for returning a long
// 2 for returning a float
// 3 for returning a double
// 4 for returning a void
and esi, 7
cmp esi, 1
jg Float_Double_Void
je Long_Return
mov eax, [ecx-4]
jmp Return
Float_Double_Void:
cmp esi, 3
jg Return
je Double
fld dword ptr [ecx-4]
jmp Return
Double:
fld qword ptr [ecx-8]
Return:
movzx ecx, word ptr [edi]methodblock.args_size
pop edi
pop esi
pop edx // return address
lea esp, [esp+ecx*4]
jmp edx
Long_Return:
mov eax, [ecx-8]
mov edx, [ecx-4]
// this is tricky, we need to do a __stdcall return, but the number
// of args is variable, eax and edx are live, only ecx is available,
// so we move the return address
movzx ecx, word ptr [edi]methodblock.args_size
mov esi, [esp+8] // read return address
mov [esp+ecx*4+8], esi // write return address
pop edi
pop esi
lea esp, [esp+ecx*4]
ret
CallErrorUnwind:
push esi
call ErrorUnwind
}
}
__declspec(naked) static void __stdcall
InvokeNativeMethod()
{
// -> edx callee mb
// -> ecx caller mb
// -> [esp+4+4*i] arg(i), i = 0, 1, ...
// <- reg(s) method result, if any
__asm {
push esi
push edi
mov edi, edx // save callee mb
mov esi, ecx // save caller mb
call dword ptr [p_EE]
lea edx, [esp+8] // stub base
push eax // ee
movzx ecx, word ptr [edi]methodblock.args_size
push ecx // callee->args_size
push esi // caller
push edx // stub base
mov esi, eax // save ee
call PushJavaFrame
push edi // callee
call GetStubAddr
// call the native method __cdecl stub(stack_item *p, struct execenv *ee)
lea ecx, [esp+8+4]
push esi // ee
push ecx // pointer to the arguments
call eax // call native stub
add esp, 8
mov ecx, eax // save returned top of operand stack
jmp exitNativeInvoker
}
}
__declspec(naked) static void __stdcall
InvokeSynchronizedNativeMethod()
{
// -> edi callee mb
// -> esi caller mb
// -> ebp monitor, i.e. this or class handle
// -> [esp+20+4*i] arg(i), i = 0, 1, ...
// <- reg(s) method result, if any
__asm {
push ebp
call dword ptr [p_monitorEnter] // __cdecl
pop ecx
call dword ptr [p_EE]
lea edx, [esp+16] // stub base
push eax // ee
movzx ecx, word ptr [edi]methodblock.args_size
push ecx // callee->args_size
push esi // caller
push edx // stub base
mov esi, eax // save ee
call PushJavaFrame
push edi // callee
call GetStubAddr
// call the native method __cdecl stub(stack_item *p, struct execenv *ee)
lea ecx, [esp+16+4]
push esi // ee
push ecx // pointer to the arguments
call eax // call native stub
add esp, 8
mov ebx, eax // save returned top of operand stack
push ebp
call dword ptr [p_monitorExit] // __cdecl
pop ecx
mov ecx, ebx
pop ebp
pop ebx
jmp exitNativeInvoker
}
}
__declspec(naked) static void __stdcall
InvokeSynchronizedNativeDynamicMethod()
{
// -> edx callee mb
// -> ecx caller mb
// -> [esp+4+4*i] arg(i), i = 0, 1, ...
// <- reg(s) method result, if any
__asm {
push esi
push edi
push ebx
push ebp
mov edi, edx // save callee mb
mov esi, ecx // save caller mb
mov ebp, [esp+16+4] // the receiver to be synchronized
jmp InvokeSynchronizedNativeMethod
}
}
__declspec(naked) static void __stdcall
InvokeSynchronizedNativeStaticMethod()
{
// -> edx callee mb
// -> ecx caller mb
// -> [esp+4+4*i] arg(i), i = 0, 1, ...
// <- reg(s) method result, if any
__asm {
push esi
push edi
push ebx
push ebp
mov edi, edx // save callee mb
mov esi, ecx // save caller mb
mov edx, [edx]fieldblock.clazz // first field in methodblock is fieldblock
mov ebp, [edx]ClassClass.HandleToSelf // the handle to be synchronized
jmp InvokeSynchronizedNativeMethod
}
}
bool_t
invokeCompiledMethod(JHandle * o, struct methodblock * mb,
int args_size, struct execenv * ee)
{
// object o not used
JavaFrame *frame = ee->current_frame;
stack_item *optop = frame->optop;
// Java8 t;
switch((mb->CompiledCodeFlags >> 24) & 0xFF)
{
default:
// optop->i = InvokeCompiledMethodInt(mb, optop, args_size);
__asm {
push args_size
push optop
push mb
call InvokeCompiledMethodStub
mov ecx, optop
mov [ecx], eax
}
sysAssert(frame->optop == optop);
frame->optop = optop + 1;
break;
case SIGNATURE_LONG:
// SET_INT64(t, optop,
// InvokeCompiledMethodLong(mb, optop, args_size));
__asm {
push args_size
push optop
push mb
call InvokeCompiledMethodStub
mov ecx, optop
mov [ecx], eax
mov [ecx+4], edx
}
sysAssert(frame->optop == optop);
frame->optop = optop + 2;
break;
case SIGNATURE_FLOAT:
// optop->f = InvokeCompiledMethodFloat(mb, optop, args_size);
__asm {
push args_size
push optop
push mb
call InvokeCompiledMethodStub
mov ecx, optop
fstp dword ptr [ecx]
}
sysAssert(frame->optop == optop);
frame->optop = optop + 1;
break;
case SIGNATURE_DOUBLE:
// SET_DOUBLE(t, optop,
// InvokeCompiledMethodDouble(mb, optop, args_size));
__asm {
push args_size
push optop
push mb
call InvokeCompiledMethodStub
mov ecx, optop
fstp qword ptr [ecx]
}
sysAssert(frame->optop == optop);
frame->optop = optop + 2;
break;
case SIGNATURE_VOID:
InvokeCompiledMethodStub(mb, optop, args_size);
sysAssert(frame->optop == optop);
break;
}
return !exceptionOccurred(ee);
}
/* Perform runtime patches on the compiled code. */
static bool_t PatchCompiledCode(struct methodblock *mb, unsigned char *code,
unsigned char *patch, int patchSize)
{
// precompiled method not supported
return 0;
}
bool_t
invokeCompiler(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee);
/* Modify the V-table for mb, so that mb is compiled and executed natively
* the next time it is invoked.
*/
static bool_t
compileAndGoOnInvoke(struct methodblock *mb)
{
unsigned int access;
bool_t res;
INVOKER_LOCK();
access = mb->fb.access;
if ((access & ACC_MACHINE_COMPILED) && mb->code == 0)
{
res = FALSE;
// method was precompiled and no byte code was loaded:
// mb->invoker initialized to invokeCompiledMethod in
// InitializeInvoker (classruntime.c)
// mb->CompiledCode initialized in
// ReadInCompiledCode (classloader.c)
// or the byte code was discarded after a compilation:
// mb->invoker and mb->CompiledCode initialized in goOnInvoke
}
else if ((access & (ACC_ABSTRACT | ACC_NATIVE)) == 0)
{
sysAssert(mb->code);
res = TRUE;
mb->invoker = invokeCompiler;
if ((access & ACC_MACHINE_COMPILED) && mb->CompiledCode)
{
/* we cannot free code here, may still be in use,
* should be garbage collected, how???
(*p_free)(mb->CompiledCode); // discard old compiled code
(*p_free)(mb->CompiledCodeInfo);
*/
mb->CompiledCode = 0;
mb->CompiledCodeInfo = 0;
}
mb->CompiledCode = (access & ACC_STATIC) ?
&CallbackStaticMethod : &CallbackDynamicMethod;
mb->fb.access &= ~ACC_MACHINE_COMPILED;
}
else if (access & ACC_ABSTRACT)
{
res = FALSE;
// mb->invoker initialized to invokeAbstractMethod in
// InitializeInvoker (classruntime.c)
mb->CompiledCode = (access & ACC_STATIC) ?
&CallbackStaticMethod : &CallbackDynamicMethod;
}
else
{
res = FALSE;
// mb->invoker initialized to invokeLazyNativeMethod in
// InitializeInvoker (classruntime.c)
mb->CompiledCode = (access & ACC_SYNCHRONIZED) ?
( (access & ACC_STATIC) ?
&InvokeSynchronizedNativeStaticMethod :
&InvokeSynchronizedNativeDynamicMethod )
: &InvokeNativeMethod;
}
INVOKER_UNLOCK();
return res;
}
/* Insert the compiled pc range in an array sorted in increasing order for
* fast pc-to-mb mapping.
*/
static char *
insertCodeRange(struct methodblock *mb, CodeInfo *codeInfo)
{
CodeRange *new_code_ranges, *range;
unsigned char *start_pc = codeInfo->start_pc;
unsigned char *end_pc = codeInfo->end_pc;
int len, i, l, r;
if (ncode_ranges == max_ncode_ranges)
{
new_code_ranges = (*p_realloc)(code_ranges, 2*max_ncode_ranges*sizeof(CodeRange));
if (new_code_ranges == 0)
return "Not enough memory";
max_ncode_ranges *= 2;
code_ranges = new_code_ranges;
}
// optimize the cases where blocks are allocated in sorted order (incr or decr)
if (ncode_ranges == 0 || end_pc < code_ranges->start_pc)
{
range = code_ranges;
len = ncode_ranges;
}
else if (start_pc >= code_ranges[ncode_ranges-1].end_pc)
{
range = code_ranges + ncode_ranges;
len = 0;
}
else
{
// binary search
l = 0;
r = ncode_ranges - 1;
while (l <= r)
{
i = (l + r)/2;
range = code_ranges + i;
if (start_pc >= range->end_pc)
l = i + 1;
else if (end_pc < range->start_pc)
r = i - 1;
else
sysAssert(!"Range overlap");
}
sysAssert(r == l - 1);
range = code_ranges + l;
len = ncode_ranges - l;
sysAssert(start_pc < range->start_pc && start_pc >= range[-1].end_pc);
}
if (len)
memmove(range + 1, range, len*sizeof(CodeRange)); // overlapping copy
range->start_pc = start_pc;
range->end_pc = end_pc;
range->mb = mb;
ncode_ranges++;
return 0;
}
static void
deleteCodeRange(CodeInfo *codeInfo)
{
CodeRange *range;
unsigned char *start_pc = codeInfo->start_pc;
unsigned char *end_pc = codeInfo->end_pc;
int len, i, l, r;
sysAssert(ncode_ranges != 0);
// binary search
l = 0;
r = ncode_ranges - 1;
while (l <= r)
{
i = (l + r)/2;
range = code_ranges + i;
if (start_pc >= range->end_pc)
l = i + 1;
else if (end_pc < range->start_pc)
r = i - 1;
else
break;
}
sysAssert(range->start_pc == start_pc && range->end_pc == end_pc);
len = code_ranges + ncode_ranges - range - 1;
if (len)
memmove(range, range + 1, len*sizeof(CodeRange)); // overlapping copy
ncode_ranges--;
}
static char *
goOnInvoke(struct methodblock *mb, char *compiledCode, CodeInfo *codeInfo)
{
char *err = 0;
INVOKER_LOCK();
err = insertCodeRange(mb, codeInfo);
if (!err)
{
mb->invoker = invokeCompiledMethod;
mb->CompiledCode = compiledCode;
mb->CompiledCodeInfo = codeInfo;
mb->fb.access |= ACC_MACHINE_COMPILED;
}
INVOKER_UNLOCK();
return err;
}
/* Modify the V-table for mb, so that mb is interpreted
* the next time it is invoked.
*/
static bool_t
interpretOnInvoke(struct methodblock *mb, bool_t freeCompiledCode)
{
unsigned int access;
bool_t res;
INVOKER_LOCK();
access = mb->fb.access;
if ((access & ACC_MACHINE_COMPILED) && mb->code == 0)
{
res = FALSE;
// method was precompiled and no byte code was loaded:
// mb->invoker initialized to invokeCompiledMethod in
// InitializeInvoker (classruntime.c)
// mb->CompiledCode initialized in
// ReadInCompiledCode (classloader.c)
// or the byte code was discarded after a compilation:
// mb->invoker and mb->CompiledCode initialized in goOnInvoke
}
else if ((access & (ACC_ABSTRACT | ACC_NATIVE)) == 0)
{
/*
if ((access & ACC_MACHINE_COMPILED) && mb->exception_table_length)
// handler_pc was modified, cannot return to interpreted code
// before a new field compiled_CatchFrame is introduced and used
res = FALSE;
// mb->invoker and mb->CompiledCode initialized in goOnInvoke,
// cannot be changed
else
*/ {
res = TRUE;
mb->invoker = (access & ACC_SYNCHRONIZED) ?
p_invokeSynchronizedJavaMethod : p_invokeJavaMethod;
if (freeCompiledCode && (access & ACC_MACHINE_COMPILED))
{
/* we cannot free code here, may still be in use,
* should be garbage collected, how???
(*p_free)(mb->CompiledCode); // discard old compiled code
(*p_free)(mb->CompiledCodeInfo);
*/
mb->CompiledCode = 0;
mb->CompiledCodeInfo = 0;
}
mb->CompiledCode = (access & ACC_STATIC) ?
&CallbackStaticMethod : &CallbackDynamicMethod;
mb->fb.access &= ~ACC_MACHINE_COMPILED;
}
}
else if (access & ACC_ABSTRACT)
{
res = FALSE;
// mb->invoker initialized to invokeAbstractMethod in
// InitializeInvoker (classruntime.c)
mb->CompiledCode = (access & ACC_STATIC) ?
&CallbackStaticMethod : &CallbackDynamicMethod;
}
else
{
res = FALSE;
// mb->invoker initialized to invokeLazyNativeMethod in
// InitializeInvoker (classruntime.c)
mb->CompiledCode = (access & ACC_SYNCHRONIZED) ?
( (access & ACC_STATIC) ?
&InvokeSynchronizedNativeStaticMethod :
&InvokeSynchronizedNativeDynamicMethod )
: &InvokeNativeMethod;
}
INVOKER_UNLOCK();
return res;
}
bool_t __cdecl
invokeCompiler(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
char *compiledCode;
CodeInfo *codeInfo;
char *err;
bool_t res, freeByteCode;
classJavaLangObject = *p_classJavaLangObject;
interfaceJavaLangCloneable = *p_interfaceJavaLangCloneable;
ObjectMethodTable = cbMethodTable(classJavaLangObject);
/* args were popped from the operand stack by the interpreter.
* repush them during compilation, otherwise they could be destroyed
* by a call to do_execute_java_method_vararg (static initializers are
* invoked during constant pool resolution).
*/
ee->current_frame->optop += args_size;
res = jitCompile(mb, &compiledCode, &codeInfo, &err, ee);
ee->current_frame->optop -= args_size;
if (res)
{
#ifdef BINARY_DEBUG
static unsigned div = 1;
static unsigned rem = 1; // div == 1 -> go == rem
static unsigned always = 0xffffffff;
static unsigned from = 0; // go if in range [from..to[
static unsigned to = 0xffffffff;
if (!(count >= from && count < to) || (count % div == rem && count != always))
{
count++;
interpretOnInvoke(mb, FALSE);
}
else
count++,
#endif
(err = goOnInvoke(mb, compiledCode, codeInfo)) ?
interpretOnInvoke(mb, FALSE) : (void)0;
}
else
// could not compile, do not try again at next invocation
interpretOnInvoke(mb, FALSE);
#ifdef DEBUG
if (err /*&& !strcmp(err, "opc_invokevirtual_quick")*/)
cprintf(stderr, "\nJIT compiler: %s,\n Method %s%s of class %s\n",
err, mb->fb.name, mb->fb.signature, mb->fb.clazz->name);
#endif
if (p_notifyCompilation)
{
freeByteCode = FALSE;
(*p_notifyCompilation)(mb, err, &freeByteCode);
// control panel may call interpretOnInvoke at this point
}
else
#ifdef KEEP_BYTECODE
freeByteCode = FALSE;
#else
freeByteCode = TRUE; // danger! only works if first invocation
// code may still be in use otherwise
#endif
if (!err && freeByteCode)
{
mb->code_length = 0;
(*p_free)(mb->code);
mb->code = 0;
}
return mb->invoker(o, mb, args_size, ee);
}
/* Determine the value that should code in the CompiledCodeFlags field.
* For all implementations:
* Bits 24-31 give the return type character of the signature.
* For Intel:
* Bits 0-2 give a number describing the return type of the function.
*/
static long calculateFlags(struct methodblock *mb)
{
char *signature = fieldsig(&mb->fb);
char *p;
long flags = 0;
for (p = signature + 1; *p != SIGNATURE_ENDFUNC; p++);
switch (p[1])
{
case SIGNATURE_LONG:
flags = 1; break;
case SIGNATURE_FLOAT:
flags = 2; break;
case SIGNATURE_DOUBLE:
flags = 3; break;
case SIGNATURE_VOID:
flags = 4; break;
}
flags |= p[1] << 24;
return flags;
}
/* Called from the initializer or from init when the compiler class is loaded.
* Initialize the methods in this class in whatever way is needed by
* Compiled code. In particular, we should
* 1) Patch compiled code in whatever way necessary
* 2) Initialize the mb->CompiledCode field for non-compiled functions.
* 3) Initialize the mb->CompiledCodeFlags field.
* 4) Change the mb->invoker field to invokeCompiler for jit compilation if
* no control panel is present, otherwise, the panel decided already.
*
* This function runs under a lock for cb or under lock_classes.
*/
static void __cdecl
InitializeForCompiler(ClassClass *cb)
{
struct methodblock *mb = cbMethods(cb);
int size;
// debugging:
// CompilerEnabled = strncmp(cb->name, "java", 4) && strncmp(cb->name, "sun", 3);
for (size = 0; size < (int) cb->methods_count; size++, mb++)
{
if (mb->fb.access & ACC_MACHINE_COMPILED)
// precompiled methods not supported, invokeCompiledMethod in
// compiler.c will complain
{
// The mb->code field temporarily holds the patches. We should
// free them when we're done.
PatchCompiledCode(mb, mb->CompiledCode, mb->code, mb->code_length);
(*p_free)(mb->code);
mb->code = NULL;
mb->code_length = 0;
}
else if (p_notifyClassInit == 0)
// otherwise, control panel will do the initialization
{
if (CompilerEnabled)
compileAndGoOnInvoke(mb);
else
interpretOnInvoke(mb, FALSE);
}
mb->CompiledCodeFlags = calculateFlags(mb);
}
if (p_notifyClassInit)
(*p_notifyClassInit)(cb);
// control panel must call compileAndGoOnInvoke or interpretOnInvoke for
// each method of cb
}
static void __cdecl
CompilerFreeClass(ClassClass *cb)
{
struct methodblock *mb = cbMethods(cb);
int size;
INVOKER_LOCK();
if (p_notifyClassFree)
(*p_notifyClassFree)(cb);
for (size = 0; size < (int) cb->methods_count; size++, mb++)
{
if ((mb->fb.access & ACC_MACHINE_COMPILED) && mb->CompiledCode)
{
/* can we really free CompiledCode here?
* were compiled methods on the stack properly marked by the gc?
(*p_free)(mb->CompiledCode);
(*p_free)(mb->CompiledCodeInfo);
*/
mb->CompiledCode = 0;
mb->CompiledCodeInfo = 0;
// kept mb->code of a compiled method freed by VM
}
}
INVOKER_UNLOCK();
}
#ifdef IN_NETSCAPE_NAVIGATOR
static void __cdecl
CompilerFreeMethod(struct methodblock *mb)
{
INVOKER_LOCK();
if ((mb->fb.access & ACC_MACHINE_COMPILED) && mb->CompiledCode)
{
deleteCodeRange(mb->CompiledCodeInfo);
(*p_free)(mb->CompiledCode);
(*p_free)(mb->CompiledCodeInfo);
mb->CompiledCode = 0;
mb->CompiledCodeInfo = 0;
// kept mb->code of a compiled method freed by VM
}
INVOKER_UNLOCK();
}
#endif
static bool_t __cdecl
CompilercompileClass(ClassClass *cb)
{
struct methodblock *mb;
int size;
mb = cbMethods(cb);
for (size = 0; size < (int) cb->methods_count; size++, mb++)
if ((mb->fb.access & ACC_MACHINE_COMPILED) == 0) // do not recompile
compileAndGoOnInvoke(mb);
return TRUE;
}
/* The methods of all the loaded classes with a name matching the given prefix
* will be compiled at next invocation.
*/
static bool_t __cdecl
CompilercompileClasses(Hjava_lang_String *name)
{
struct methodblock *mb;
int j, size, len;
ClassClass **pcb, *cb;
char clname[256], *p;
int c;
bool_t found_one = FALSE;
(*p_javaString2CString)(name, clname, sizeof(clname));
for (p = clname ; (c = *p); p++) {
if (c == '.') {
*p = '/';
}
}
len = p - clname;
(*p_lock_classes)();
pcb = *p_binclasses;
for (j = *p_nbinclasses; --j >= 0; pcb++)
{
cb = *pcb;
if (strncmp(cb->name, clname, len) == 0) // test classloader ??????
{
found_one = TRUE;
mb = cbMethods(cb);
for (size = 0; size < (int) cb->methods_count; size++, mb++)
if ((mb->fb.access & ACC_MACHINE_COMPILED) == 0) // do not recompile
compileAndGoOnInvoke(mb);
}
}
(*p_unlock_classes)();
return found_one;
}
static Hjava_lang_Object * __cdecl
CompilerCommand(Hjava_lang_Object *any)
{
return NULL; // no command implemented
}
/* The semantic of CompilerEnable and CompilerDisable is not well
* defined in the Java API Doc. We implement them as follows:
* Calling these functions does not modify the state (ACC_MACHINE_COMPILED)
* of already executed methods. Otherwise, it breaks our stack crawling
* mechanism. Also, it has no effect on methods of already loaded classes, i.e.
* a method will be compiled at its first invocation if CompilerEnabled was true
* at the time its class was loaded, it will never be compiled otherwise.
* These functions define the compiler behavior for methods of classes being
* loaded after calling these functions.
*/
static void __cdecl
CompilerEnable(void)
{
CompilerEnabled = 1;
}
static void __cdecl
CompilerDisable(void)
{
CompilerEnabled = 0;
}
static bool_t __cdecl
PCinCompiledCode(unsigned char *pc, struct methodblock *mb)
{
CodeInfo *ci = (CodeInfo *)mb->CompiledCodeInfo;
return pc >= ci->start_pc && pc < ci->end_pc;
}
static unsigned char * __cdecl
CompiledCodePC(JavaFrame *frame, struct methodblock *mb)
{
return frame->lastpc;
}
static JavaFrame * __cdecl
CompiledFramePrev(JavaFrame *frame, JavaFrame *buf)
{
struct methodblock *caller_mb;
unsigned char *ra, *base;
CodeInfo *ci;
sysAssert(frame->current_method != 0 &&
(frame->current_method->fb.access & ACC_MACHINE_COMPILED));
ra = *((unsigned char **)frame->returnpc);
if (ra == StubRetAddr || (caller_mb = PC2CompiledMethod(ra)) == 0)
return frame->prev;
ci = (CodeInfo *)caller_mb->CompiledCodeInfo;
base = frame->returnpc + 4*frame->current_method->args_size +
ci->localSize + ci->frameSize;
buf->current_method = caller_mb;
buf->vars = (stack_item *)(base + 4);
#ifdef IN_NETSCAPE_NAVIGATOR
buf->annotation = *((JHandle **)(base - ci->frameSize));
#endif
buf->lastpc = ra; // used in CompiledCodePC
buf->returnpc = base;
buf->prev = frame->prev; // frame may be buf !
return buf;
}
#ifdef IN_NETSCAPE_NAVIGATOR
static void __cdecl
SetCompiledFrameAnnotation(JavaFrame *frame, JHandle *annotation)
{
struct methodblock *mb = frame->current_method;
CodeInfo *ci = (CodeInfo *)mb->CompiledCodeInfo;
sysAssert(mb->fb.access & ACC_MACHINE_COMPILED);
*((JHandle **)(frame->returnpc - ci->frameSize)) = annotation;
}
#endif
/* 2 functions used by sysAssert */
void
DumpThreads(void)
{
(*p_DumpThreads)();
}
void
printToConsole(const char *txt)
{
if(p_printToConsole)
(*p_printToConsole)(txt);
}
int
cprintf(FILE *strm, const char *format, .../* args */)
{
va_list ap;
char buf[512];
int r;
va_start(ap, format);
/* cant use safe variant because of linkage */
r = vsprintf(buf, format, ap);
printToConsole(buf);
va_end(ap);
return r;
}
static ClassClass** __cdecl
get_binclasses(void)
{
return *p_binclasses;
}
static long __cdecl
get_nbinclasses(void)
{
return *p_nbinclasses;
}
static void
LinkCTRL(void)
{
int (*p_link)(void **);
void* linkVect[] = {
&p_notifyCompilation,
&p_notifyClassInit,
&p_notifyClassFree,
&get_binclasses,
&get_nbinclasses,
&compileAndGoOnInvoke,
&goOnInvoke,
&interpretOnInvoke,
};
(FARPROC)p_link = GetProcAddress(controlHandle, "LinkCTRL");
if (p_link)
(*p_link)(linkVect);
}
static void
LoadCTRL(void)
{
char envVar[256];
char *controlName = DefaultControlName;
long len;
len = GetEnvironmentVariable(ControlEnvVarName, envVar, sizeof(envVar));
if (len > 0 && len < sizeof(envVar))
controlName = envVar;
controlHandle = LoadLibrary(controlName);
if (controlHandle)
LinkCTRL();
}
static void
Init(void)
{
int j;
ClassClass **pcb, *cb;
InitCompiler();
_invoker_lock = (sys_mon_t *) (*p_malloc)((*p_sysMonitorSizeof)());
memset((char*) _invoker_lock, 0, (*p_sysMonitorSizeof)());
INVOKER_LOCK_INIT();
cprintf(stderr, logo0);
cprintf(stderr, logo1);
// LoadCTRL();
code_ranges = (*p_calloc)(max_ncode_ranges, sizeof(CodeRange));
(*p_lock_classes)();
CompilerEnabled = 1;
pcb = *p_binclasses;
for (j = *p_nbinclasses; --j >= 0; pcb++)
{
cb = *pcb;
InitializeForCompiler(cb);
}
(*p_unlock_classes)();
}
#define LINK(p) (void *)(p) = vect[i++]
/* this is a deliberate attempt to make this jit not work
with other runtimes at Borland's request */
#define COMPILER_VERSION 60
/* Link the virtual machine if its version is compatible
* with this version of the jit compiler.
*/
__declspec(dllexport) void
java_lang_Compiler_start(void **vect)
{
int i = 0;
#ifdef CHECK_REGISTRY
{
HKEY ctrlKey1, ctrlKey2;
char regKeyStr[45];
int i;
RegOpenKeyEx(baseKey1, regKeyStr1, 0, KEY_READ, &ctrlKey1);
if (ctrlKey1)
RegCloseKey(ctrlKey1);
else
{
fprintf(stderr, "*** AppAccelerator was not properly installed.\n");
fflush(stderr);
return;
}
for (i = 0; i < 44; i++)
regKeyStr[i] = regKeyStr3[i] ^ (regKeyStr2[i] & 31);
regKeyStr[44] = 0;
RegOpenKeyEx(baseKey2, regKeyStr, 0, KEY_READ, &ctrlKey2);
if (ctrlKey2)
RegCloseKey(ctrlKey2);
else
return;
}
#endif
LINK(p_JavaVersion);
if (*p_JavaVersion != ((COMPILER_VERSION << 24) | (JAVA_VERSION << 16) |
JAVA_MINOR_VERSION))
{
fprintf(stderr, "JIT incompatible with this Java runtime.\n");
fflush(stderr);
return;
}
LINK(pp_InitializeForCompiler);
*pp_InitializeForCompiler = &InitializeForCompiler;
LINK(pp_invokeCompiledMethod); // no precompiled methods
LINK(pp_CompiledCodeSignalHandler); // not used
LINK(pp_CompilerFreeClass);
*pp_CompilerFreeClass = &CompilerFreeClass;
#ifdef IN_NETSCAPE_NAVIGATOR
LINK(pp_CompilerFreeMethod);
*pp_CompilerFreeMethod = &CompilerFreeMethod;
#endif
LINK(pp_CompilerCompileClass);
*pp_CompilerCompileClass = &CompilercompileClass;
LINK(pp_CompilercompileClasses);
*pp_CompilercompileClasses = &CompilercompileClasses;
LINK(pp_CompilerEnable);
*pp_CompilerEnable = &CompilerEnable;
LINK(pp_CompilerDisable);
*pp_CompilerDisable = &CompilerDisable;
LINK(pp_ReadInCompiledCode); // no precompiled methods
LINK(pp_PCinCompiledCode);
*pp_PCinCompiledCode = &PCinCompiledCode;
LINK(pp_CompiledCodePC);
*pp_CompiledCodePC = &CompiledCodePC;
LINK(pp_CompiledFramePrev);
*pp_CompiledFramePrev = &CompiledFramePrev;
#ifdef IN_NETSCAPE_NAVIGATOR
LINK(pp_SetCompiledFrameAnnotation);
*pp_SetCompiledFrameAnnotation = &SetCompiledFrameAnnotation;
#endif
LINK(p_CompiledCodeAttribute); // no precompiled methods
LINK(p_UseLosslessQuickOpcodes);
//*p_UseLosslessQuickOpcodes = 1;
LINK(p_malloc);
LINK(p_calloc);
LINK(p_realloc);
LINK(p_free);
LINK(p_binclasses);
LINK(p_nbinclasses);
LINK(p_lock_classes);
LINK(p_unlock_classes);
LINK(p_classJavaLangClass);
LINK(p_classJavaLangObject);
LINK(p_classJavaLangString);
LINK(p_classJavaLangThrowable);
LINK(p_classJavaLangException);
LINK(p_classJavaLangRuntimeException);
LINK(p_interfaceJavaLangCloneable);
LINK(p_EE);
LINK(p_SignalError);
LINK(p_exceptionInternalObject);
LINK(p_GetClassConstantClassName);
LINK(p_ResolveClassConstant);
LINK(p_ResolveClassConstantFromClass);
LINK(p_VerifyClassAccess);
LINK(p_FindClass);
LINK(p_FindClassFromClass);
LINK(p_dynoLink);
LINK(p_do_execute_java_method_vararg);
LINK(p_is_subclass_of);
LINK(p_invokeJavaMethod);
LINK(p_invokeSynchronizedJavaMethod);
LINK(p_invokeAbstractMethod);
LINK(p_invokeLazyNativeMethod);
LINK(p_invokeSynchronizedNativeMethod);
LINK(p_invokeCompiledMethod);
LINK(p_monitorEnter);
LINK(p_monitorExit);
LINK(p_monitorRegister);
LINK(p_sysMonitorSizeof);
LINK(p_sysMonitorEnter);
LINK(p_sysMonitorExit);
LINK(p_ObjAlloc);
LINK(p_ArrayAlloc);
LINK(p_MultiArrayAlloc);
LINK(p_sizearray);
LINK(p_newobject);
LINK(p_is_instance_of);
LINK(p_jio_snprintf);
LINK(p_javaString2CString);
LINK(p_classname2string);
LINK(p_IDToNameAndTypeString);
LINK(p_IDToType);
LINK(p_DumpThreads);
LINK(p_printToConsole);
LINK(p_CreateNewJavaStack);
LINK(p_execute_java_static_method);
LINK(p_ExecuteJava);
LINK(p_now);
LINK(p_java_monitor);
LINK(p_java_mon);
LINK(p_JavaStackSize);
Init();
}