ikvm-fork/IK.VM.JNI/jni.cpp

330 строки
6.6 KiB
C++

/*
Copyright (C) 2002 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
#include "stdafx.h"
#include <malloc.h>
#include "jnienv.h"
#include "jni.h"
#pragma managed
#include <stdio.h>
using namespace System;
using namespace System::Collections;
using namespace System::Runtime::InteropServices;
using namespace System::Reflection;
int JNI::LoadNativeLibrary(String* name)
{
HMODULE hMod = LoadLibrary(name);
if(!hMod)
{
return 0;
}
libs->Add(__box((int)hMod));
return 1;
}
Type* JNI::GetLocalRefStructType()
{
return __typeof(LocalRefStruct);
}
MethodInfo* JNI::GetJniFuncPtrMethod()
{
return __typeof(JNI)->GetMethod("GetJniFuncPtr");
}
jobject JNI::MakeGlobalRef(Object* o)
{
if(!o)
{
return 0;
}
// TODO search for an empty slot before adding it to the end...
return (jobject)-(globalRefs->Add(o) + 1);
}
void JNI::DeleteGlobalRef(jobject o)
{
if(o)
{
int i = int(o);
if(i < 0)
{
globalRefs->Item[(-i) - 1] = 0;
return;
}
DebugBreak();
}
}
Object* JNI::UnwrapGlobalRef(jobject o)
{
if(!o)
{
return 0;
}
int i = int(o);
if(i < 0)
{
return globalRefs->Item[(-i) - 1];
}
DebugBreak();
return 0;
}
IntPtr JNI::GetJniFuncPtr(String* method, String* sig, String* clazz)
{
System::Text::StringBuilder* mangledSig = new System::Text::StringBuilder();
int sp = 0;
for(int i = 1; sig->Chars[i] != ')'; i++)
{
switch(sig->Chars[i])
{
case '[':
mangledSig->Append(S"_3");
sp += 4;
while(sig->Chars[++i] == '[')
{
mangledSig->Append(S"_3");
}
mangledSig->Append(sig->Chars[i]);
if(sig->Chars[i] == 'L')
{
while(sig->Chars[++i] != ';')
{
if(sig->Chars[i] == '/')
{
mangledSig->Append(S"_");
}
else if(sig->Chars[i] == '_')
{
mangledSig->Append(S"_1");
}
else
{
mangledSig->Append(sig->Chars[i]);
}
}
mangledSig->Append(S"_2");
}
break;
case 'L':
sp += 4;
mangledSig->Append(S"L");
while(sig->Chars[++i] != ';')
{
if(sig->Chars[i] == '/')
{
mangledSig->Append(S"_");
}
else if(sig->Chars[i] == '_')
{
mangledSig->Append(S"_1");
}
else
{
mangledSig->Append(sig->Chars[i]);
}
}
mangledSig->Append(S"_2");
break;
case 'J':
case 'D':
mangledSig->Append(sig->Chars[i]);
sp += 8;
break;
case 'F':
case 'I':
case 'C':
case 'Z':
case 'S':
case 'B':
mangledSig->Append(sig->Chars[i]);
sp += 4;
break;
default:
DebugBreak();
throw new NotImplementedException();
}
}
void* func = 0;
// TODO implement this correctly
String* methodName = String::Format(S"_Java_{0}_{1}@{2}", clazz->Replace(S"_", S"_1")->Replace('/', '_'), method->Replace(S"_", S"_1"), __box(sp + 8));
for(int i = 0; i < libs->Count; i++)
{
HMODULE hMod = (HMODULE)__unbox<int>(libs->Item[i]);
func = GetProcAddress(hMod, methodName);
if(func)
{
return (IntPtr)func;
}
}
methodName = String::Concat(String::Format(S"_Java_{0}_{1}__{2}@", clazz->Replace(S"_", S"_1")->Replace('/', '_'), method->Replace(S"_", S"_1"), mangledSig), __box(sp + 8));
for(int i = 0; i < libs->Count; i++)
{
HMODULE hMod = (HMODULE)__unbox<int>(libs->Item[i]);
func = GetProcAddress(hMod, methodName);
if(func)
{
return (IntPtr)func;
}
}
throw VM::UnsatisfiedLinkError(methodName);
}
// NOTE we have only one global JNIEnv*, because allocating the JNIEnv is TLS proved too much of a headache,
// we only keep the pointer to the current frame (LocalRefStruct) in a TLS variable
static JNIEnv jniEnv;
// If we put the ThreadStatic in LocalRefStruct we get an ExecutionEngineException (?!)
__gc class TlsHack
{
public:
[ThreadStatic]
static void* currentLocalRefStruct;
};
JNIEnv* LocalRefStruct::GetEnv()
{
if(TlsHack::currentLocalRefStruct)
{
return &jniEnv;
}
return 0;
}
LocalRefStruct* LocalRefStruct::Current()
{
return (LocalRefStruct*)TlsHack::currentLocalRefStruct;
}
IntPtr LocalRefStruct::Enter()
{
pPrevLocalRefCache = TlsHack::currentLocalRefStruct;
// NOTE since this __value type can (should) only be allocated on the stack,
// it is "safe" to store the this pointer in a __nogc*, but the compiler
// doesn't know this, so we have to use a __pin* to bypass its checks.
LocalRefStruct __pin* pPinHack = this;
TlsHack::currentLocalRefStruct = pPinHack;
return (IntPtr)&jniEnv;
}
void LocalRefStruct::Leave()
{
TlsHack::currentLocalRefStruct = pPrevLocalRefCache;
if(pendingException)
{
// TODO retain the stack trace of the exception object
throw pendingException;
}
}
Exception* LocalRefStruct::get_PendingException()
{
return pendingException;
}
void LocalRefStruct::set_PendingException(Exception* exception)
{
pendingException = exception;
}
IntPtr LocalRefStruct::MakeLocalRef(Object* o)
{
if(o == 0)
{
return 0;
}
Object** p = &cache.loc1;
for(int i = 0; i < 10; i++)
{
if(p[i] == 0)
{
p[i] = o;
return i + 1;
}
}
if(!overflow)
{
// HACK we use a very large dynamic table size, because we don't yet support growing it
overflow = new Object* __gc[256];
}
for(int i = 0; i < overflow->Length; i++)
{
if(overflow[i] == 0)
{
overflow[i] = o;
return i + 11;
}
}
throw new NotImplementedException(S"Growing the localref table is not implemented");
}
void LocalRefStruct::DeleteLocalRef(jobject o)
{
int i = (int)o;
if(i < 0)
{
Console::WriteLine("bogus localref in DeleteLocalRef");
DebugBreak();
}
if(i > 0)
{
if(i <= 10)
{
Object** p = &cache.loc1;
p[i - 1] = 0;
}
else
{
overflow[i - 11] = 0;
}
}
}
Object* LocalRefStruct::UnwrapLocalRef(IntPtr localref)
{
if(localref == 0)
{
return 0;
}
if(int(localref) < 0)
{
Console::WriteLine("bogus localref in UnwrapLocalRef");
DebugBreak();
}
if(int(localref) <= 10)
{
Object** p = &cache.loc1;
return p[int(localref) - 1];
}
else
{
return overflow[int(localref) - 11];
}
}