diff options
author | jfrijters <jfrijters> | 2002-12-18 19:00:25 +0300 |
---|---|---|
committer | jfrijters <jfrijters> | 2002-12-18 19:00:25 +0300 |
commit | 59d175f0205a15dec48c593c215d6b6f560e9097 (patch) | |
tree | 3e25ec05e2f6156c193113937af3c453bbed36bb |
Initial revision
75 files changed, 29777 insertions, 0 deletions
diff --git a/IK.VM.JNI/AssemblyInfo.cpp b/IK.VM.JNI/AssemblyInfo.cpp new file mode 100644 index 00000000..0c14df55 --- /dev/null +++ b/IK.VM.JNI/AssemblyInfo.cpp @@ -0,0 +1,81 @@ +/* + 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" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("")]; +[assembly:AssemblyDescriptionAttribute("")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("")]; +[assembly:AssemblyProductAttribute("")]; +[assembly:AssemblyCopyrightAttribute("")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute("1.0.1060.22610")]; + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project directory. +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly:AssemblyDelaySignAttribute(false)]; +[assembly:AssemblyKeyFileAttribute("")]; +[assembly:AssemblyKeyNameAttribute("")]; + diff --git a/IK.VM.JNI/IK.VM.JNI.vcproj b/IK.VM.JNI/IK.VM.JNI.vcproj new file mode 100644 index 00000000..7e6d9e18 --- /dev/null +++ b/IK.VM.JNI/IK.VM.JNI.vcproj @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding = "Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.00" + Name="IK.VM.JNI" + ProjectGUID="{4D400F9D-68A1-4066-95F6-85AF0E58B710}" + Keyword="ManagedCProj"> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="Debug" + IntermediateDirectory="Debug" + ConfigurationType="2" + CharacterSet="2" + ManagedExtensions="TRUE"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="WIN32;_DEBUG" + MinimalRebuild="FALSE" + BasicRuntimeChecks="0" + RuntimeLibrary="1" + TreatWChar_tAsBuiltInType="TRUE" + ForceConformanceInForLoopScope="TRUE" + UsePrecompiledHeader="3" + WarningLevel="3" + DebugInformationFormat="3"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + OutputFile="$(OutDir)/IK.VM.JNI.dll" + LinkIncremental="2" + GenerateDebugInformation="TRUE"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="Release" + IntermediateDirectory="Release" + ConfigurationType="2" + CharacterSet="2" + ManagedExtensions="TRUE"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + InlineFunctionExpansion="1" + PreprocessorDefinitions="WIN32;NDEBUG" + MinimalRebuild="FALSE" + TreatWChar_tAsBuiltInType="TRUE" + ForceConformanceInForLoopScope="TRUE" + UsePrecompiledHeader="3" + WarningLevel="3" + DebugInformationFormat="3"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + OutputFile="$(OutDir)/IK.VM.JNI.dll" + LinkIncremental="1" + GenerateDebugInformation="TRUE"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + </Configuration> + </Configurations> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"> + <File + RelativePath="AssemblyInfo.cpp"> + </File> + <File + RelativePath="Stdafx.cpp"> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1"/> + </FileConfiguration> + </File> + <File + RelativePath="jni.cpp"> + </File> + <File + RelativePath="jnienv.cpp"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc"> + <File + RelativePath="Stdafx.h"> + </File> + <File + RelativePath="jni.h"> + </File> + <File + RelativePath="jnienv.h"> + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;r"> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/IK.VM.JNI/Stdafx.cpp b/IK.VM.JNI/Stdafx.cpp new file mode 100644 index 00000000..42f1745b --- /dev/null +++ b/IK.VM.JNI/Stdafx.cpp @@ -0,0 +1,5 @@ +// stdafx.cpp : source file that includes just the standard includes +// jni.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" diff --git a/IK.VM.JNI/Stdafx.h b/IK.VM.JNI/Stdafx.h new file mode 100644 index 00000000..91e9cc50 --- /dev/null +++ b/IK.VM.JNI/Stdafx.h @@ -0,0 +1,9 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, +// but are changed infrequently + +#pragma once + +#using <mscorlib.dll> + +#include <windows.h> diff --git a/IK.VM.JNI/ik.vm.jni.build b/IK.VM.JNI/ik.vm.jni.build new file mode 100644 index 00000000..170781be --- /dev/null +++ b/IK.VM.JNI/ik.vm.jni.build @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<project name="ik.vm.jni" default="ik.vm.jni"> + <target name="ik.vm.jni"> + <echo message="*** TODO ***" /> + </target> +</project> diff --git a/IK.VM.JNI/jni.cpp b/IK.VM.JNI/jni.cpp new file mode 100644 index 00000000..cd1a3ecc --- /dev/null +++ b/IK.VM.JNI/jni.cpp @@ -0,0 +1,178 @@ +/* + 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; + +int JNI::LoadNativeLibrary(String* name) +{ + HMODULE hMod = LoadLibrary(name); + if(!hMod) + { + return 0; + } + libs->Add(__box((int)hMod)); + return 1; +} + +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 + { + 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 + { + 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); +} diff --git a/IK.VM.JNI/jni.h b/IK.VM.JNI/jni.h new file mode 100644 index 00000000..5248f25b --- /dev/null +++ b/IK.VM.JNI/jni.h @@ -0,0 +1,245 @@ +/* + 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 + +*/ + +#pragma once + +using namespace System; +using namespace System::Collections; +using namespace System::Runtime::InteropServices; +using namespace System::Reflection; + +class JNIEnv; + +#pragma managed + +public __value class LocalRefStruct; + +// NOTE this __value type needs to have the *exact* same layout as the unmanaged JNIENv class +__value struct JNIEnvContainer +{ + void* pVtable; + LocalRefStruct __nogc* pLocalRefStruct; +}; + +public __value class LocalRefStruct +{ + [ThreadStatic] + static JNIEnvContainer jniEnv; + LocalRefStruct __nogc* pPrevLocalRefCache; + Object* loc1; + Object* loc2; + Object* loc3; + Object* loc4; + Object* loc5; + Object* loc6; + Object* loc7; + Object* loc8; + Object* loc9; + Object* loc10; + Object* overflow __gc[]; + Exception* pendingException; +public: + + static JNIEnv* GetEnv() + { + if(jniEnv.pVtable != 0) + { + return (JNIEnv*)(void*)&jniEnv; + } + return 0; + } + + IntPtr Enter() + { + if(jniEnv.pVtable == 0) + { + // HACK initialize the vtable ptr + JNIEnv env; + jniEnv.pVtable = *((void**)&env); + } + pPrevLocalRefCache = jniEnv.pLocalRefStruct; + // 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; + jniEnv.pLocalRefStruct = pPinHack; + return (IntPtr)&jniEnv; + } + + void Leave() + { + jniEnv.pLocalRefStruct = pPrevLocalRefCache; + if(pendingException) + { + // TODO retain the stack trace of the exception object + throw pendingException; + } + } + + __property Exception* get_PendingException() + { + return pendingException; + } + + __property void set_PendingException(Exception* exception) + { + pendingException = exception; + } + + IntPtr MakeLocalRef(Object* o) + { + if(o == 0) + { + return 0; + } + Object** p = &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 DeleteLocalRef(jobject o) + { + int i = (int)o; + if(i < 0) + { + DebugBreak(); + } + if(i > 0) + { + if(i <= 10) + { + Object** p = &loc1; + p[i - 1] = 0; + } + else + { + overflow[i - 11] = 0; + } + } + } + + Object* UnwrapLocalRef(IntPtr localref) + { + if(localref == 0) + { + return 0; + } + if(int(localref) <= 10) + { + Object** p = &loc1; + return p[int(localref) - 1]; + } + else + { + return overflow[int(localref) - 11]; + } + } +}; + +public __gc class JNI +{ + static ArrayList* libs = new ArrayList(); + static ArrayList* globalRefs = new ArrayList(); + [DllImport("kernel32")] + [System::Security::SuppressUnmanagedCodeSecurityAttribute] + static HMODULE LoadLibrary(String* lpLibFileName); + [DllImport("kernel32")] + [System::Security::SuppressUnmanagedCodeSecurityAttribute] + static void* GetProcAddress(HMODULE hMod, String* lpProcName); +public: + static int LoadNativeLibrary(String* name); + static IntPtr GetJniFuncPtr(String* method, String* sig, String* clazz); + static jobject MakeGlobalRef(Object* o); + static void DeleteGlobalRef(jobject o); + static Object* UnwrapGlobalRef(jobject o); +}; + +public __gc interface IJniHack +{ +public: + virtual MethodBase* GetMethod(Object* clazz, String* name, String* sig, bool isStatic)=0; + virtual FieldInfo* GetField(Object* clazz, String* name, String* sig, bool isStatic)=0; + virtual Object* FindClass(String* name)=0; + virtual Exception* UnsatisfiedLinkError(String* msg)=0; + virtual Object* GetClassFromType(Type* type)=0; +}; + +public __gc class VM +{ +private: + static IJniHack* pJniHack = 0; + static VM() + { + pJniHack = __try_cast<IJniHack*>(Activator::CreateInstance(Assembly::Load(S"ik.vm.net")->GetType(S"JniHack"))); + } +public: + // NOTE we use this nasty construct to avoid a compile time dependency on JVM.DLL (which already has a compile time + // dependency on us) + static MethodBase* GetMethod(Object* clazz, String* name, String* sig, bool isStatic) + { + return pJniHack->GetMethod(clazz, name, sig, isStatic); + } + static FieldInfo* GetField(Object* clazz, String* name, String* sig, bool isStatic) + { + return pJniHack->GetField(clazz, name, sig, isStatic); + } + static Object* FindClass(String* javaName) + { + return pJniHack->FindClass(javaName); + } + static Exception* UnsatisfiedLinkError(String* msg) + { + return pJniHack->UnsatisfiedLinkError(msg); + } + static Object* GetClassFromType(Type* type) + { + return pJniHack->GetClassFromType(type); + } +}; + +template<class T> T __unbox(Object* o) +{ + // HACK the MC++ compiler has a bug when unboxing, the static_cast<> is to work around that bug + return *(static_cast<T __gc*>(__try_cast<__box T*>(o))); +} diff --git a/IK.VM.JNI/jnienv.cpp b/IK.VM.JNI/jnienv.cpp new file mode 100644 index 00000000..eed66174 --- /dev/null +++ b/IK.VM.JNI/jnienv.cpp @@ -0,0 +1,1097 @@ +/* + 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 "jnienv.h" +#include "jni.h" +#include <malloc.h> +#include <stdarg.h> + +#define DEBUG +#undef NDEBUG + +#include <assert.h> + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::Reflection; + +// this struct exists to ensure the right compile switch is used, if it fails +// to compile, you must compile with /Zc:wchar_t +struct wchar_t_must_be_builtin +{ + void foo(wchar_t t) {} + void foo(unsigned short t) {} +}; + +#pragma managed + +String* StringFromUTF8(const char* psz) +{ + // Sun's modified UTF8 encoding is not compatible with System::Text::Encoding::UTF8, so + // we need to roll our own + int len = 0; + while(psz[len]) len++; + System::Text::StringBuilder* sb = new System::Text::StringBuilder(len); + for(int i = 0; i < len; i++) + { + int c = (unsigned char)*psz++; + int char2, char3; + switch (c >> 4) + { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + // 0xxxxxxx + break; + case 12: case 13: + // 110x xxxx 10xx xxxx + char2 = *psz++; + i++; + c = (((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = *psz++; + char3 = *psz++; + i++; + i++; + c = (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); + break; + } + sb->Append((wchar_t)c); + } + return sb->ToString(); +} + +JNIEnv::JNIEnv() +{ +} + +JNIEnv::~JNIEnv() +{ +} + +Object* JNIEnv::UnwrapRef(jobject o) +{ + int i = (int)o; + if(i >= 0) + { + return pLocalRefs->UnwrapLocalRef(i); + } + return JNI::UnwrapGlobalRef(o); +} + +jstring JNIEnv::NewStringUTF(const char *psz) +{ + return (jstring)(void*)pLocalRefs->MakeLocalRef(StringFromUTF8(psz)); +} + +jstring JNIEnv::NewString(const jchar *unicode, jsize len) +{ + return (jstring)(void*)pLocalRefs->MakeLocalRef(new String((__wchar_t*)unicode, 0, len)); +} + +const char* JNIEnv::GetStringUTFChars(jstring str, jboolean *isCopy) +{ + String* s = __try_cast<String*>(UnwrapRef(str)); + // TODO for now we use the upper limit on the number of possible bytes needed + char *buf = new char[s->Length * 3 + 1]; + // TODO if memory allocation fails, handle it by "throwing" OutOfMemoryError and returning null + int j = 0; + for(int i = 0, e = s->Length; i < e; i++) + { + jchar ch = s->Chars[i]; + if ((ch != 0) && (ch <=0x7f)) + { + buf[j++] = (char)ch; + } + else if (ch <= 0x7FF) + { + /* 11 bits or less. */ + unsigned char high_five = ch >> 6; + unsigned char low_six = ch & 0x3F; + buf[j++] = high_five | 0xC0; /* 110xxxxx */ + buf[j++] = low_six | 0x80; /* 10xxxxxx */ + } + else + { + /* possibly full 16 bits. */ + char high_four = ch >> 12; + char mid_six = (ch >> 6) & 0x3F; + char low_six = ch & 0x3f; + buf[j++] = high_four | 0xE0; /* 1110xxxx */ + buf[j++] = mid_six | 0x80; /* 10xxxxxx */ + buf[j++] = low_six | 0x80; /* 10xxxxxx*/ + } + } + buf[j] = 0; + if(isCopy) + { + *isCopy = JNI_TRUE; + } + + return buf; +} +#pragma unmanaged + +void JNIEnv::ReleaseStringUTFChars(jstring str, const char* chars) +{ + delete[] chars; +} + +#pragma managed +jint JNIEnv::ThrowNew(jclass clazz, const char *msg) +{ + jstring str = NewStringUTF(msg); + jmethodID constructor = GetMethodID(clazz, "<init>", "(Ljava/lang/String;)V"); + assert(constructor); + jobject exc = NewObject(clazz, constructor, str); + DeleteLocalRef(str); + Throw((jthrowable)exc); + DeleteLocalRef(exc); + return JNI_OK; +} + +jint JNICALL JNIEnv::Throw(jthrowable obj) +{ + pLocalRefs->PendingException = __try_cast<Exception*>(UnwrapRef(obj)); + return JNI_OK; +} + +jthrowable JNICALL JNIEnv::ExceptionOccurred() +{ + return (jthrowable)(void*)pLocalRefs->MakeLocalRef(pLocalRefs->PendingException); +} + +void JNICALL JNIEnv::ExceptionDescribe() +{ + if(pLocalRefs->PendingException) + { + // when calling JNI methods there cannot be an exception pending, so we clear the exception + // temporarily, while we print it + jobject exception = ExceptionOccurred(); + Exception* pException = pLocalRefs->PendingException; + pLocalRefs->PendingException = 0; + jclass cls = FindClass("java/lang/Throwable"); + jmethodID mid = GetMethodID(cls, "printStackTrace", "()V"); + DeleteLocalRef(cls); + CallVoidMethod(exception, mid); + pLocalRefs->DeleteLocalRef(exception); + pLocalRefs->PendingException = pException; + } +} + +void JNICALL JNIEnv::ExceptionClear() +{ + pLocalRefs->PendingException = 0; +} + +#pragma unmanaged +jclass JNICALL JNIEnv::DefineClass(const char *name, jobject loader, const jbyte *buf, jsize len) +{ + assert(false); + _asm int 3 +} + +#pragma managed +jclass JNIEnv::FindClass(const char *utf) +{ + return (jclass)(void*)pLocalRefs->MakeLocalRef(VM::FindClass(StringFromUTF8(utf))); +} +#pragma unmanaged + +jobject JNIEnv::AllocObject(jclass cls) +{ + // wicked, I just realized that serialization should have a facility to construct uninitialized objects + // this can be implemented using FormatterServices.GetUninitializedObject, the only hitch is that this + // won't supports strings, so we may have to figure out a workaround for that, or decide just not to support it + assert(false); + _asm int 3 +} + +#pragma managed + +jmethodID JNIEnv::FindMethodID(jclass cls, const char* name, const char* sig, bool isstatic) +{ + MethodBase* m = VM::GetMethod(UnwrapRef(cls), StringFromUTF8(name), StringFromUTF8(sig), isstatic); + if(!m) + { + //Console::WriteLine("Method not found: {0}{1} (static = {2})", StringFromUTF8(name), StringFromUTF8(sig), __box(isstatic)); + ThrowNew(FindClass("java/lang/NoSuchMethodError"), ""); + return 0; + } + // TODO jmethodID must be cached and delete'd!!! As it stands, we have a huge leak... + // also, don't forget to Free the GCHandle... + jmethodID mid = new _jmethodID; + mid->method = (void*)(IntPtr)GCHandle::Alloc(m); + int argc = 0; + for(int i = 1; sig[i] != ')'; i++) + { + switch(sig[i]) + { + case 'I': + case 'Z': + case 'S': + case 'C': + case 'B': + mid->args[argc++] = 'I'; + break; + case 'L': + while(sig[i] != ';') i++; + mid->args[argc++] = 'L'; + break; + case '[': + while(sig[i] == '[') i++; + if(sig[i] == 'L') while(sig[i] != ';') i++; + mid->args[argc++] = 'L'; + break; + case 'D': + mid->args[argc++] = 'D'; + break; + case 'F': + mid->args[argc++] = 'F'; + break; + case 'J': + mid->args[argc++] = 'J'; + break; + } + if(argc == 256) + { + // it isn't valid for a Java method to have more than 256 arguments, + // so this cannot happen for valid classes + throw new NotSupportedException(); + } + } + mid->args[argc] = 0; + return mid; +} + +jfieldID JNIEnv::FindFieldID(jclass cls, const char* name, const char* sig, bool isstatic) +{ + FieldInfo* f = VM::GetField(UnwrapRef(cls), StringFromUTF8(name), StringFromUTF8(sig), isstatic); + if(!f) + { + // TODO what is the proper handling of this + assert(false); + throw new NotImplementedException("field not found"); + } + // TODO jfieldID must be cached and delete'd!!! As it stands, we have a huge leak... + // also, don't forget to Free the GCHandle... + jfieldID fid = new _jfieldID; + fid->field = (void*)(IntPtr)GCHandle::Alloc(f); + return fid; +} + +jmethodID JNIEnv::GetStaticMethodID(jclass cls, const char *name, const char *sig) +{ + return FindMethodID(cls, name, sig, true); +} + +Object* JNIEnv::InvokeHelper(jobject object, jmethodID methodID, jvalue* args) +{ + MethodBase* m = __try_cast<MethodBase*>(GCHandle::op_Explicit((IntPtr)methodID->method).Target); + ParameterInfo* p __gc[] = m->GetParameters(); + Object* argarray __gc[] = new Object*[p->Length]; + for(int i = 0; i < p->Length; i++) + { + if(p[i]->ParameterType == __typeof(bool)) + { + argarray[i] = __box(args[i].z != JNI_FALSE); + } + else if(p[i]->ParameterType == __typeof(char)) + { + argarray[i] = __box((char)args[i].b); + } + else if(p[i]->ParameterType == __typeof(__wchar_t)) + { + argarray[i] = __box((__wchar_t)args[i].c); + } + else if(p[i]->ParameterType == __typeof(short)) + { + argarray[i] = __box((short)args[i].s); + } + else if(p[i]->ParameterType == __typeof(int)) + { + argarray[i] = __box((int)args[i].i); + } + else if(p[i]->ParameterType == __typeof(__int64)) + { + argarray[i] = __box((__int64)args[i].j); + } + else if(p[i]->ParameterType == __typeof(float)) + { + argarray[i] = __box((float)args[i].f); + } + else if(p[i]->ParameterType == __typeof(double)) + { + argarray[i] = __box((double)args[i].d); + } + else if(!p[i]->ParameterType->IsValueType) + { + // If we have an object specified but the method is static, we have been redirected to + // a static helper, so we have to adjust + if(i == 0 && object && m->IsStatic) + { + argarray[i] = UnwrapRef(object); + object = 0; + // HACK fix up the args ptr to correct for the missing first argument + args--; + } + else + { + argarray[i] = UnwrapRef(args[i].l); + } + } + else + { + // this can't happen, so it probably should be an assertion + assert(false); + throw new NotImplementedException(p[i]->ParameterType->FullName); + } + } + try + { + Object* obj = 0; + if(object) + { + obj = UnwrapRef(object); + } + if(m->IsConstructor) + { + return __try_cast<ConstructorInfo*>(m)->Invoke(argarray); + } + return m->Invoke(obj, argarray); + } + catch(TargetInvocationException* x) + { + // TODO retain stack trace information + pLocalRefs->PendingException = x->InnerException; + return 0; + } +} + +void JNICALL JNIEnv::CallStaticVoidMethodA(jclass cls, jmethodID methodID, jvalue* args) +{ + InvokeHelper(0, methodID, args); +} +#pragma unmanaged + +void JNICALL JNIEnv::CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args) +{ + int argc = strlen(methodID->args); + jvalue* argarray = (jvalue*)_alloca(argc * sizeof(jvalue)); + for(int i = 0; i < argc; i++) + { + switch(methodID->args[i]) + { + case 'I': + argarray[i].i = va_arg(args, int); + break; + case 'J': + argarray[i].j = va_arg(args, __int64); + break; + case 'L': + argarray[i].l = va_arg(args, jobject); + break; + case 'D': + argarray[i].d = va_arg(args, double); + break; + case 'F': + argarray[i].f = (float)va_arg(args, double); + break; + } + } + CallStaticVoidMethodA(clazz, methodID, argarray); +} + +void JNIEnv::CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...) +{ + va_list args; + va_start(args, methodID); + CallStaticVoidMethodV(clazz, methodID, args); + va_end(args); +} + +#define STATIC_METHOD_IMPL(Type,type) \ +type JNICALL JNIEnv::CallStatic##Type##MethodV(jclass clazz, jmethodID methodID, va_list args)\ +{\ + int argc = strlen(methodID->args);\ + jvalue* argarray = (jvalue*)_alloca(argc * sizeof(jvalue));\ + for(int i = 0; i < argc; i++)\ + {\ + switch(methodID->args[i])\ + {\ + case 'I':\ + argarray[i].i = va_arg(args, int);\ + break;\ + case 'J':\ + argarray[i].j = va_arg(args, __int64);\ + break;\ + case 'L':\ + argarray[i].l = va_arg(args, jobject);\ + break;\ + case 'D':\ + argarray[i].d = va_arg(args, double);\ + break;\ + case 'F':\ + argarray[i].f = (float)va_arg(args, double);\ + break;\ + }\ + }\ + return CallStatic##Type##MethodA(clazz, methodID, argarray);\ +}\ +type JNIEnv::CallStatic##Type##Method(jclass clazz, jmethodID methodID, ...)\ +{\ + va_list args;\ + va_start(args, methodID);\ + type ret = CallStatic##Type##MethodV(clazz, methodID, args);\ + va_end(args);\ + return ret;\ +} +#define STATIC_METHOD_IMPL_MANAGED(Type,type,cpptype) \ +type JNICALL JNIEnv::CallStatic##Type##MethodA(jclass cls, jmethodID methodID, jvalue* args)\ +{\ + Object* ret = InvokeHelper(0, methodID, args);\ + if(ret) return __unbox<cpptype>(ret);\ + return 0;\ +} + +STATIC_METHOD_IMPL(Object,jobject) +STATIC_METHOD_IMPL(Boolean,jboolean) +STATIC_METHOD_IMPL(Byte,jbyte) +STATIC_METHOD_IMPL(Char,jchar) +STATIC_METHOD_IMPL(Short,jshort) +STATIC_METHOD_IMPL(Int,jint) +STATIC_METHOD_IMPL(Long,jlong) +STATIC_METHOD_IMPL(Float,jfloat) +STATIC_METHOD_IMPL(Double,jdouble) +#pragma managed +STATIC_METHOD_IMPL_MANAGED(Boolean,jboolean,bool) +STATIC_METHOD_IMPL_MANAGED(Byte,jbyte,System::SByte) +STATIC_METHOD_IMPL_MANAGED(Char,jchar,wchar_t) +STATIC_METHOD_IMPL_MANAGED(Short,jshort,short) +STATIC_METHOD_IMPL_MANAGED(Int,jint,int) +STATIC_METHOD_IMPL_MANAGED(Long,jlong,__int64) +STATIC_METHOD_IMPL_MANAGED(Float,jfloat,float) +STATIC_METHOD_IMPL_MANAGED(Double,jdouble,double) + +// special case for Object +jobject JNICALL JNIEnv::CallStaticObjectMethodA(jclass cls, jmethodID methodID, jvalue* args) +{ + return (jobject)(void*)pLocalRefs->MakeLocalRef(InvokeHelper(0, methodID, args)); +} + +#pragma unmanaged +jmethodID JNIEnv::GetMethodID(jclass cls, const char *name, const char *sig) +{ + return FindMethodID(cls, name, sig, false); +} + +jfieldID JNICALL JNIEnv::GetFieldID(jclass cls, const char *name, const char *sig) +{ + return FindFieldID(cls, name, sig, false); +} + +jfieldID JNICALL JNIEnv::GetStaticFieldID(jclass cls, const char *name, const char *sig) +{ + return FindFieldID(cls, name, sig, true); +} + +#pragma managed +#define GET_SET_FIELD(Type,type,cpptype) \ +void JNICALL JNIEnv::Set##Type##Field(jobject obj, jfieldID fieldID, type val)\ +{\ + FieldInfo* pField = __try_cast<FieldInfo*>(GCHandle::op_Explicit((IntPtr)fieldID->field).Target);\ + pField->SetValue(UnwrapRef(obj), __box((cpptype)val));\ +}\ +type JNICALL JNIEnv::Get##Type##Field(jobject obj, jfieldID fieldID)\ +{\ + FieldInfo* pField = __try_cast<FieldInfo*>(GCHandle::op_Explicit((IntPtr)fieldID->field).Target);\ + return __unbox<cpptype>(pField->GetValue(UnwrapRef(obj)));\ +} + +GET_SET_FIELD(Boolean,jboolean,bool) +GET_SET_FIELD(Byte,jbyte,System::SByte) +GET_SET_FIELD(Char,jchar,wchar_t) +GET_SET_FIELD(Short,jshort,short) +GET_SET_FIELD(Int,jint,int) +GET_SET_FIELD(Long,jlong,__int64) +GET_SET_FIELD(Float,jfloat,float) +GET_SET_FIELD(Double,jdouble,double) + +void JNICALL JNIEnv::SetObjectField(jobject obj, jfieldID fieldID, jobject val) +{ + FieldInfo* pField = __try_cast<FieldInfo*>(GCHandle::op_Explicit((IntPtr)fieldID->field).Target); + pField->SetValue(UnwrapRef(obj), UnwrapRef(val)); +} + +jobject JNICALL JNIEnv::GetObjectField(jobject obj, jfieldID fieldID) +{ + FieldInfo* pField = __try_cast<FieldInfo*>(GCHandle::op_Explicit((IntPtr)fieldID->field).Target); + return (jobject)(void*)pLocalRefs->MakeLocalRef(pField->GetValue(UnwrapRef(obj))); +} +#pragma unmanaged + +void JNICALL JNIEnv::SetStaticObjectField(jclass cls, jfieldID fieldID, jobject value) +{ + assert(false); + _asm int 3 +} + +jobject JNICALL JNIEnv::GetStaticObjectField(jclass clazz, jfieldID fieldID) +{ + assert(false); + _asm int 3 +} + +jlong JNICALL JNIEnv::GetStaticLongField(jclass clazz, jfieldID fieldID) +{ + assert(false); + _asm int 3 +} + +#define METHOD_IMPL(Type,type) \ +type JNIEnv::Call##Type##Method(jobject obj, jmethodID methodID, ...) \ +{\ + va_list args;\ + va_start(args, methodID);\ + type ret = Call##Type##MethodV(obj, methodID, args);\ + va_end(args);\ + return ret;\ +}\ +type JNICALL JNIEnv::Call##Type##MethodV(jobject obj, jmethodID methodID, va_list args)\ +{\ + int argc = strlen(methodID->args);\ + jvalue* argarray = (jvalue*)_alloca(argc * sizeof(jvalue));\ + for(int i = 0; i < argc; i++)\ + {\ + switch(methodID->args[i])\ + {\ + case 'I':\ + argarray[i].i = va_arg(args, int);\ + break;\ + case 'J':\ + argarray[i].j = va_arg(args, __int64);\ + break;\ + case 'L':\ + argarray[i].l = va_arg(args, jobject);\ + break;\ + case 'D':\ + argarray[i].d = va_arg(args, double);\ + break;\ + case 'F':\ + argarray[i].f = (float)va_arg(args, double);\ + break;\ + }\ + }\ + return Call##Type##MethodA(obj, methodID, argarray);\ +} + +#define METHOD_IMPL_MANAGED(Type,type,cpptype) \ +type JNICALL JNIEnv::Call##Type##MethodA(jobject obj, jmethodID methodID, jvalue* args)\ +{\ + Object* ret = InvokeHelper(obj, methodID, args);\ + if(ret) return __unbox<cpptype>(ret);\ + return 0;\ +} + +#pragma unmanaged +METHOD_IMPL(Object,jobject) +METHOD_IMPL(Boolean,jboolean) +METHOD_IMPL(Byte,jbyte) +METHOD_IMPL(Char,jchar) +METHOD_IMPL(Short,jshort) +METHOD_IMPL(Int,jint) +METHOD_IMPL(Long,jlong) +METHOD_IMPL(Float,jfloat) +METHOD_IMPL(Double,jdouble) +#pragma managed +METHOD_IMPL_MANAGED(Boolean,jboolean,bool) +METHOD_IMPL_MANAGED(Byte,jbyte,System::SByte) +METHOD_IMPL_MANAGED(Char,jchar,wchar_t) +METHOD_IMPL_MANAGED(Short,jshort,short) +METHOD_IMPL_MANAGED(Int,jint,int) +METHOD_IMPL_MANAGED(Long,jlong,__int64) +METHOD_IMPL_MANAGED(Float,jfloat,float) +METHOD_IMPL_MANAGED(Double,jdouble,double) + +// special case for Object, because we need to convert the reference to a localref +jobject JNICALL JNIEnv::CallObjectMethodA(jobject obj, jmethodID methodID, jvalue* args) +{ + return (jobject)(void*)pLocalRefs->MakeLocalRef(InvokeHelper(obj, methodID, args)); +} +#pragma unmanaged + +void JNIEnv::CallVoidMethod(jobject obj, jmethodID methodID, ...) +{ + va_list args; + va_start(args, methodID); + CallVoidMethodV(obj, methodID, args); + va_end(args); +} + +void JNICALL JNIEnv::CallVoidMethodV(jobject obj, jmethodID methodID, va_list args) +{ + int argc = strlen(methodID->args); + jvalue* argarray = (jvalue*)_alloca(argc * sizeof(jvalue)); + for(int i = 0; i < argc; i++) + { + switch(methodID->args[i]) + { + case 'I': + argarray[i].i = va_arg(args, int); + break; + case 'J': + argarray[i].j = va_arg(args, __int64); + break; + case 'L': + argarray[i].l = va_arg(args, jobject); + break; + case 'D': + argarray[i].d = va_arg(args, double); + break; + case 'F': + argarray[i].f = (float)va_arg(args, double); + break; + } + } + CallVoidMethodA(obj, methodID, argarray); +} + +#pragma managed +void JNICALL JNIEnv::CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args) +{ + InvokeHelper(obj, methodID, args); +} +#pragma unmanaged + +void JNICALL JNIEnv::CallNonvirtualVoidMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) +{ + assert(false); + _asm int 3 +} + +void JNICALL JNIEnv::CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...) +{ + assert(false); + _asm int 3 +} + +#pragma managed +jsize JNIEnv::GetStringLength(jstring str) +{ + String* s = __try_cast<String*>(UnwrapRef(str)); + return s->Length; +} + +const jchar* JNIEnv::GetStringChars(jstring str, jboolean *isCopy) +{ + String* s = __try_cast<String*>(UnwrapRef(str)); + jchar* p = new jchar[s->Length]; + for(int i = 0; i < s->Length; i++) + { + p[i] = s->Chars[i]; + } + if(isCopy) + { + *isCopy = JNI_TRUE; + } + return p; +} + +void JNIEnv::ReleaseStringChars(jstring str, const jchar *chars) +{ + delete[] chars; +} + +jsize JNIEnv::GetArrayLength(jarray array) +{ + Array* ar = __try_cast<Array*>(UnwrapRef(array)); + return ar->Length; +} +#pragma unmanaged + +jobjectArray JNIEnv::NewObjectArray(jsize len, jclass clazz, jobject init) +{ + assert(false); + _asm int 3 +} + +jcharArray JNICALL JNIEnv::NewCharArray(jsize len) +{ + assert(false); + _asm int 3 +} + +jbyteArray JNICALL JNIEnv::NewByteArray(jsize len) +{ + assert(false); + _asm int 3 +} + +#pragma managed +jintArray JNICALL JNIEnv::NewIntArray(jsize len) +{ + return (jintArray)(void*)pLocalRefs->MakeLocalRef(new int __gc[len]); +} + +#pragma unmanaged +void JNIEnv::SetObjectArrayElement(jobjectArray array, jsize index, jobject val) +{ + assert(false); + _asm int 3 +} + +#pragma managed +jobject JNIEnv::GetObjectArrayElement(jobjectArray array, jsize index) +{ + Object* ar __gc[] = __try_cast<Object* __gc[]>(UnwrapRef(array)); + if(index >= ar->Length) + { + // TODO handle error + assert(false); + } + return (jobject)(void*)pLocalRefs->MakeLocalRef(ar[index]); +} + +#define GET_SET_ARRAY_ELEMENTS(Type,type,cpptype) \ +type* JNIEnv::Get##Type##ArrayElements(type##Array array, jboolean *isCopy)\ +{\ + cpptype ar __gc[] = __try_cast<cpptype __gc[]>(UnwrapRef(array));\ + type* p = new type[ar->Length];\ + for(int i = 0; i < ar->Length; i++)\ + {\ + p[i] = ar[i];\ + }\ + if(isCopy)\ + {\ + *isCopy = JNI_TRUE;\ + }\ + return p;\ +}\ +void JNIEnv::Release##Type##ArrayElements(type##Array array, type *elems, jint mode)\ +{\ + if(mode == 0)\ + {\ + cpptype ar __gc[] = __try_cast<cpptype __gc[]>(UnwrapRef(array));\ + for(int i = 0; i < ar->Length; i++)\ + {\ + ar[i] = elems[i];\ + }\ + delete[] elems;\ + }\ + else if(mode == JNI_COMMIT)\ + {\ + cpptype ar __gc[] = __try_cast<cpptype __gc[]>(UnwrapRef(array));\ + for(int i = 0; i < ar->Length; i++)\ + {\ + ar[i] = elems[i];\ + }\ + }\ + else if(mode == JNI_ABORT)\ + {\ + delete[] elems;\ + }\ +} + +GET_SET_ARRAY_ELEMENTS(Boolean,jboolean,bool) +GET_SET_ARRAY_ELEMENTS(Byte,jbyte,System::SByte) +GET_SET_ARRAY_ELEMENTS(Char,jchar,wchar_t) +GET_SET_ARRAY_ELEMENTS(Short,jshort,short) +GET_SET_ARRAY_ELEMENTS(Int,jint,int) +GET_SET_ARRAY_ELEMENTS(Long,jlong,__int64) +GET_SET_ARRAY_ELEMENTS(Float,jfloat,float) +GET_SET_ARRAY_ELEMENTS(Double,jdouble,double) + +#pragma unmanaged +jobject JNICALL JNIEnv::NewObject(jclass clazz, jmethodID methodID, ...) +{ + va_list args; + va_start(args, methodID); + jobject o = NewObjectV(clazz, methodID, args); + va_end(args); + return o; +} + +jobject JNICALL JNIEnv::NewObjectV(jclass clazz, jmethodID methodID, va_list args) +{ + int argc = strlen(methodID->args); + jvalue* argarray = (jvalue*)_alloca(argc * sizeof(jvalue)); + for(int i = 0; i < argc; i++) + { + switch(methodID->args[i]) + { + case 'I': + argarray[i].i = va_arg(args, int); + break; + case 'J': + argarray[i].j = va_arg(args, __int64); + break; + case 'L': + argarray[i].l = va_arg(args, jobject); + break; + case 'D': + argarray[i].d = va_arg(args, double); + break; + case 'F': + argarray[i].f = (float)va_arg(args, double); + break; + } + } + return NewObjectA(clazz, methodID, argarray); +} + +#pragma managed +jobject JNICALL JNIEnv::NewObjectA(jclass clazz, jmethodID methodID, jvalue *args) +{ + return (jobject)(void*)pLocalRefs->MakeLocalRef(InvokeHelper(0, methodID, args)); +} + +jclass JNICALL JNIEnv::GetObjectClass(jobject obj) +{ + if(obj == 0) + { + // TODO throw nullpointerexception + assert(false); + } + return (jclass)(void*)pLocalRefs->MakeLocalRef(VM::GetClassFromType(UnwrapRef(obj)->GetType())); +} +#pragma unmanaged + +jboolean JNICALL JNIEnv::IsInstanceOf(jobject obj, jclass clazz) +{ + assert(false); + _asm int 3 +} + +jboolean JNICALL JNIEnv::IsAssignableFrom(jclass sub, jclass sup) +{ + assert(false); + _asm int 3 +} + +#pragma managed +jobject JNICALL JNIEnv::NewGlobalRef(jobject lobj) +{ + return JNI::MakeGlobalRef(UnwrapRef(lobj)); +} + +void JNICALL JNIEnv::DeleteGlobalRef(jobject gref) +{ + JNI::DeleteGlobalRef(gref); +} + +void JNICALL JNIEnv::DeleteLocalRef(jobject obj) +{ + pLocalRefs->DeleteLocalRef(obj); +} +#pragma unmanaged + +jboolean JNICALL JNIEnv::IsSameObject(jobject obj1, jobject obj2) +{ + assert(false); + _asm int 3 +} + +jclass JNICALL JNIEnv::GetSuperclass(jclass sub) +{ + assert(false); + _asm int 3 +} + +jint JNICALL JNIEnv::MonitorEnter(jobject obj) +{ + assert(false); + _asm int 3 +} + +jint JNICALL JNIEnv::MonitorExit(jobject obj) +{ + assert(false); + _asm int 3 +} + +jint JNICALL JNIEnv::RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods) +{ + assert(false); + _asm int 3 +} + +jint JNICALL JNIEnv::UnregisterNatives(jclass clazz) +{ + assert(false); + _asm int 3 +} + +jint JNICALL JNIEnv::GetJavaVM(JavaVM **vm) +{ + static JavaVM theVM; + *vm = &theVM; + return 0; +} + +void JavaVM::reserved0() { assert(false); _asm int 3} +void JavaVM::reserved1() { assert(false); _asm int 3} +void JavaVM::reserved2() { assert(false); _asm int 3} + +jint JavaVM::DestroyJavaVM() +{ + assert(false); + _asm int 3 +} + +jint JavaVM::AttachCurrentThread(void **penv, void *args) +{ + assert(false); + _asm int 3 +} + +jint JavaVM::DetachCurrentThread() +{ + assert(false); + _asm int 3 +} + +#pragma managed +jint JavaVM::GetEnv(void **penv, jint version) +{ + // TODO we should check the version + JNIEnv* p = LocalRefStruct::GetEnv(); + if(p) + { + *penv = p; + return JNI_OK; + } + return JNI_EDETACHED; +} +#pragma unmanaged + +jint JavaVM::AttachCurrentThreadAsDaemon(void **penv, void *args) +{ + assert(false); + _asm int 3 +} + +//////////////////////////////////////////////////////////////////////////// +#pragma warning (disable : 4035) + +void JNIEnv::reserved0() { assert(false); _asm int 3} +void JNIEnv::reserved1() { assert(false); _asm int 3} +void JNIEnv::reserved2() { assert(false); _asm int 3} + +void JNIEnv::reserved3() { assert(false); _asm int 3} +jint JNICALL JNIEnv::GetVersion() { assert(false); _asm int 3} + +void JNIEnv::reserved4() { assert(false); _asm int 3} +void JNIEnv::reserved5() { assert(false); _asm int 3} +void JNIEnv::reserved6() { assert(false); _asm int 3} + +void JNIEnv::reserved7() { assert(false); _asm int 3} + +void JNICALL JNIEnv::FatalError(const char *msg) { assert(false); _asm int 3} +void JNIEnv::reserved8() { assert(false); _asm int 3} +void JNIEnv::reserved9() { assert(false); _asm int 3} + +void JNIEnv::reserved10() { assert(false); _asm int 3} +void JNIEnv::reserved11() { assert(false); _asm int 3} + +jobject JNICALL JNIEnv::CallNonvirtualObjectMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jobject JNICALL JNIEnv::CallNonvirtualObjectMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jobject JNICALL JNIEnv::CallNonvirtualObjectMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args) { assert(false); _asm int 3} + +jboolean JNICALL JNIEnv::CallNonvirtualBooleanMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jboolean JNICALL JNIEnv::CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jboolean JNICALL JNIEnv::CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args) { assert(false); _asm int 3} + +jbyte JNICALL JNIEnv::CallNonvirtualByteMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jbyte JNICALL JNIEnv::CallNonvirtualByteMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jbyte JNICALL JNIEnv::CallNonvirtualByteMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { assert(false); _asm int 3} + +jchar JNICALL JNIEnv::CallNonvirtualCharMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jchar JNICALL JNIEnv::CallNonvirtualCharMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jchar JNICALL JNIEnv::CallNonvirtualCharMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { assert(false); _asm int 3} + +jshort JNICALL JNIEnv::CallNonvirtualShortMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jshort JNICALL JNIEnv::CallNonvirtualShortMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jshort JNICALL JNIEnv::CallNonvirtualShortMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { assert(false); _asm int 3} + +jint JNICALL JNIEnv::CallNonvirtualIntMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jint JNICALL JNIEnv::CallNonvirtualIntMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jint JNICALL JNIEnv::CallNonvirtualIntMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { assert(false); _asm int 3} + +jlong JNICALL JNIEnv::CallNonvirtualLongMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jlong JNICALL JNIEnv::CallNonvirtualLongMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jlong JNICALL JNIEnv::CallNonvirtualLongMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { assert(false); _asm int 3} + +jfloat JNICALL JNIEnv::CallNonvirtualFloatMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jfloat JNICALL JNIEnv::CallNonvirtualFloatMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jfloat JNICALL JNIEnv::CallNonvirtualFloatMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { assert(false); _asm int 3} + +jdouble JNICALL JNIEnv::CallNonvirtualDoubleMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { assert(false); _asm int 3} +jdouble JNICALL JNIEnv::CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { assert(false); _asm int 3} +jdouble JNICALL JNIEnv::CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args) { assert(false); _asm int 3} + +void JNICALL JNIEnv::CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args) { assert(false); _asm int 3} + +jboolean JNICALL JNIEnv::GetStaticBooleanField(jclass clazz, jfieldID fieldID) { assert(false); _asm int 3} +jbyte JNICALL JNIEnv::GetStaticByteField(jclass clazz, jfieldID fieldID) { assert(false); _asm int 3} +jchar JNICALL JNIEnv::GetStaticCharField(jclass clazz, jfieldID fieldID) { assert(false); _asm int 3} +jshort JNICALL JNIEnv::GetStaticShortField(jclass clazz, jfieldID fieldID) { assert(false); _asm int 3} +jint JNICALL JNIEnv::GetStaticIntField(jclass clazz, jfieldID fieldID) { assert(false); _asm int 3} +jfloat JNICALL JNIEnv::GetStaticFloatField(jclass clazz, jfieldID fieldID) { assert(false); _asm int 3} +jdouble JNICALL JNIEnv::GetStaticDoubleField(jclass clazz, jfieldID fieldID) { assert(false); _asm int 3} + +void JNICALL JNIEnv::SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value) { assert(false); _asm int 3} + +jsize JNICALL JNIEnv::GetStringUTFLength(jstring str) { assert(false); _asm int 3} + +jbooleanArray JNICALL JNIEnv::NewBooleanArray(jsize len) { assert(false); _asm int 3} +jshortArray JNICALL JNIEnv::NewShortArray(jsize len) { assert(false); _asm int 3} +jlongArray JNICALL JNIEnv::NewLongArray(jsize len) { assert(false); _asm int 3} +jfloatArray JNICALL JNIEnv::NewFloatArray(jsize len) { assert(false); _asm int 3} +jdoubleArray JNICALL JNIEnv::NewDoubleArray(jsize len) { assert(false); _asm int 3} + +void JNICALL JNIEnv::GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize l, jboolean *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::GetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf) { assert(false); _asm int 3} + +void JNICALL JNIEnv::SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize l, jboolean *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf) { assert(false); _asm int 3} +void JNICALL JNIEnv::SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf) { assert(false); _asm int 3} diff --git a/IK.VM.JNI/jnienv.h b/IK.VM.JNI/jnienv.h new file mode 100644 index 00000000..8fca4ff3 --- /dev/null +++ b/IK.VM.JNI/jnienv.h @@ -0,0 +1,440 @@ +/* + 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 + +*/ + +#pragma unmanaged + +#define JNI_TRUE 1 +#define JNI_FALSE 0 + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 +#define JNI_ERR (-1) +#define JNI_EDETACHED (-2) +#define JNI_EVERSION (-3) +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +class JavaVM; + +#define JNIEXPORT +#define JNICALL __stdcall + +#ifndef _HELPERTYPES_ +#define _HELPERTYPES_ +class _jobject {}; +class _jclass : public _jobject {}; +class _jstring : public _jobject {}; +class _jthrowable : public _jobject {}; +class Array$ : public _jobject {}; +class ObjectArray$ : public Array$ {}; +class BooleanArray$ : public Array$ {}; +class ByteArray$ : public Array$ {}; +class CharArray$ : public Array$ {}; +class ShortArray$ : public Array$ {}; +class IntArray$ : public Array$ {}; +class LongArray$ : public Array$ {}; +class FloatArray$ : public Array$ {}; +class DoubleArray$ : public Array$ {}; +#endif //_HELPERTYPES_ + +typedef class _jclass* jclass; +typedef class _jobject* jobject; +typedef class _jstring* jstring; +typedef class _jthrowable* jthrowable; +typedef class Array$* jarray; +typedef class ObjectArray$* jobjectArray; +typedef class BooleanArray$* jbooleanArray; +typedef class ByteArray$* jbyteArray; +typedef class CharArray$* jcharArray; +typedef class ShortArray$* jshortArray; +typedef class IntArray$* jintArray; +typedef class LongArray$* jlongArray; +typedef class FloatArray$* jfloatArray; +typedef class DoubleArray$* jdoubleArray; +typedef struct _jmethodID* jmethodID; +typedef struct _jfieldID* jfieldID; + +struct _jmethodID +{ + void* method; + char args[257]; // 'I', 'J', 'L', 'D' or 'F', and terminated by '\0' +}; + +struct _jfieldID +{ + void* field; +}; + +/* + * JNI Types + */ + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef long jint; +typedef __int64 jlong; +typedef signed char jbyte; + +typedef jint jsize; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +public __value class LocalRefStruct; +public __gc class System::Object; + +class JNIEnv +{ + LocalRefStruct __nogc* pLocalRefs; + + System::Object __gc* UnwrapRef(jobject o); + jmethodID FindMethodID(jclass cls, const char* name, const char* sig, bool isstatic); + System::Object __gc* InvokeHelper(jobject object, jmethodID methodID, jvalue* args); + jfieldID FindFieldID(jclass cls, const char* name, const char* sig, bool isstatic); + +public: + JNIEnv(); + ~JNIEnv(); + + virtual void JNICALL reserved0(); + virtual void JNICALL reserved1(); + virtual void JNICALL reserved2(); + + virtual void JNICALL reserved3(); + virtual jint JNICALL GetVersion(); + + virtual jclass JNICALL DefineClass(const char *name, jobject loader, const jbyte *buf, jsize len); + virtual jclass JNICALL FindClass(const char *name); + + virtual void JNICALL reserved4(); + virtual void JNICALL reserved5(); + virtual void JNICALL reserved6(); + + virtual jclass JNICALL GetSuperclass(jclass sub); + virtual jboolean JNICALL IsAssignableFrom(jclass sub, jclass sup); + virtual void JNICALL reserved7(); + + virtual jint JNICALL Throw(jthrowable obj); + virtual jint JNICALL ThrowNew(jclass clazz, const char *msg); + virtual jthrowable JNICALL ExceptionOccurred(); + virtual void JNICALL ExceptionDescribe(); + virtual void JNICALL ExceptionClear(); + virtual void JNICALL FatalError(const char *msg); + virtual void JNICALL reserved8(); + virtual void JNICALL reserved9(); + + virtual jobject JNICALL NewGlobalRef(jobject lobj); + virtual void JNICALL DeleteGlobalRef(jobject gref); + virtual void JNICALL DeleteLocalRef(jobject obj); + virtual jboolean JNICALL IsSameObject(jobject obj1, jobject obj2); + virtual void JNICALL reserved10(); + virtual void JNICALL reserved11(); + + virtual jobject JNICALL AllocObject(jclass clazz); + virtual jobject JNICALL NewObject(jclass clazz, jmethodID methodID, ...); + virtual jobject JNICALL NewObjectV(jclass clazz, jmethodID methodID, va_list args); + virtual jobject JNICALL NewObjectA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jclass JNICALL GetObjectClass(jobject obj); + virtual jboolean JNICALL IsInstanceOf(jobject obj, jclass clazz); + + virtual jmethodID JNICALL GetMethodID(jclass clazz, const char *name, const char *sig); + + virtual jobject JNICALL CallObjectMethod(jobject obj, jmethodID methodID, ...); + virtual jobject JNICALL CallObjectMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jobject JNICALL CallObjectMethodA(jobject obj, jmethodID methodID, jvalue * args); + + virtual jboolean JNICALL CallBooleanMethod(jobject obj, jmethodID methodID, ...); + virtual jboolean JNICALL CallBooleanMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jboolean JNICALL CallBooleanMethodA(jobject obj, jmethodID methodID, jvalue * args); + + virtual jbyte JNICALL CallByteMethod(jobject obj, jmethodID methodID, ...); + virtual jbyte JNICALL CallByteMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jbyte JNICALL CallByteMethodA(jobject obj, jmethodID methodID, jvalue *args); + + virtual jchar JNICALL CallCharMethod(jobject obj, jmethodID methodID, ...); + virtual jchar JNICALL CallCharMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jchar JNICALL CallCharMethodA(jobject obj, jmethodID methodID, jvalue *args); + + virtual jshort JNICALL CallShortMethod(jobject obj, jmethodID methodID, ...); + virtual jshort JNICALL CallShortMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jshort JNICALL CallShortMethodA(jobject obj, jmethodID methodID, jvalue *args); + + virtual jint JNICALL CallIntMethod(jobject obj, jmethodID methodID, ...); + virtual jint JNICALL CallIntMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jint JNICALL CallIntMethodA(jobject obj, jmethodID methodID, jvalue *args); + + virtual jlong JNICALL CallLongMethod(jobject obj, jmethodID methodID, ...); + virtual jlong JNICALL CallLongMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jlong JNICALL CallLongMethodA(jobject obj, jmethodID methodID, jvalue *args); + + virtual jfloat JNICALL CallFloatMethod(jobject obj, jmethodID methodID, ...); + virtual jfloat JNICALL CallFloatMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jfloat JNICALL CallFloatMethodA(jobject obj, jmethodID methodID, jvalue *args); + + virtual jdouble JNICALL CallDoubleMethod(jobject obj, jmethodID methodID, ...); + virtual jdouble JNICALL CallDoubleMethodV(jobject obj, jmethodID methodID, va_list args); + virtual jdouble JNICALL CallDoubleMethodA(jobject obj, jmethodID methodID, jvalue *args); + + virtual void JNICALL CallVoidMethod(jobject obj, jmethodID methodID, ...); + virtual void JNICALL CallVoidMethodV(jobject obj, jmethodID methodID, va_list args); + virtual void JNICALL CallVoidMethodA(jobject obj, jmethodID methodID, jvalue * args); + + virtual jobject JNICALL CallNonvirtualObjectMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jobject JNICALL CallNonvirtualObjectMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jobject JNICALL CallNonvirtualObjectMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); + + virtual jboolean JNICALL CallNonvirtualBooleanMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jboolean JNICALL CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jboolean JNICALL CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); + + virtual jbyte JNICALL CallNonvirtualByteMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jbyte JNICALL CallNonvirtualByteMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jbyte JNICALL CallNonvirtualByteMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + virtual jchar JNICALL CallNonvirtualCharMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jchar JNICALL CallNonvirtualCharMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jchar JNICALL CallNonvirtualCharMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + virtual jshort JNICALL CallNonvirtualShortMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jshort JNICALL CallNonvirtualShortMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jshort JNICALL CallNonvirtualShortMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + virtual jint JNICALL CallNonvirtualIntMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jint JNICALL CallNonvirtualIntMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jint JNICALL CallNonvirtualIntMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + virtual jlong JNICALL CallNonvirtualLongMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jlong JNICALL CallNonvirtualLongMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jlong JNICALL CallNonvirtualLongMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + virtual jfloat JNICALL CallNonvirtualFloatMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jfloat JNICALL CallNonvirtualFloatMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jfloat JNICALL CallNonvirtualFloatMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + virtual jdouble JNICALL CallNonvirtualDoubleMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual jdouble JNICALL CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual jdouble JNICALL CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue *args); + + virtual void JNICALL CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...); + virtual void JNICALL CallNonvirtualVoidMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); + virtual void JNICALL CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, jvalue * args); + + virtual jfieldID JNICALL GetFieldID(jclass clazz, const char *name, const char *sig); + + virtual jobject JNICALL GetObjectField(jobject obj, jfieldID fieldID); + virtual jboolean JNICALL GetBooleanField(jobject obj, jfieldID fieldID); + virtual jbyte JNICALL GetByteField(jobject obj, jfieldID fieldID); + virtual jchar JNICALL GetCharField(jobject obj, jfieldID fieldID); + virtual jshort JNICALL GetShortField(jobject obj, jfieldID fieldID); + virtual jint JNICALL GetIntField(jobject obj, jfieldID fieldID); + virtual jlong JNICALL GetLongField(jobject obj, jfieldID fieldID); + virtual jfloat JNICALL GetFloatField(jobject obj, jfieldID fieldID); + virtual jdouble JNICALL GetDoubleField(jobject obj, jfieldID fieldID); + + virtual void JNICALL SetObjectField(jobject obj, jfieldID fieldID, jobject val); + virtual void JNICALL SetBooleanField(jobject obj, jfieldID fieldID, jboolean val); + virtual void JNICALL SetByteField(jobject obj, jfieldID fieldID, jbyte val); + virtual void JNICALL SetCharField(jobject obj, jfieldID fieldID, jchar val); + virtual void JNICALL SetShortField(jobject obj, jfieldID fieldID, jshort val); + virtual void JNICALL SetIntField(jobject obj, jfieldID fieldID, jint val); + virtual void JNICALL SetLongField(jobject obj, jfieldID fieldID, jlong val); + virtual void JNICALL SetFloatField(jobject obj, jfieldID fieldID, jfloat val); + virtual void JNICALL SetDoubleField(jobject obj, jfieldID fieldID, jdouble val); + + virtual jmethodID JNICALL GetStaticMethodID(jclass clazz, const char *name, const char *sig); + + virtual jobject JNICALL CallStaticObjectMethod(jclass clazz, jmethodID methodID, ...); + virtual jobject JNICALL CallStaticObjectMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jobject JNICALL CallStaticObjectMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jboolean JNICALL CallStaticBooleanMethod(jclass clazz, jmethodID methodID, ...); + virtual jboolean JNICALL CallStaticBooleanMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jboolean JNICALL CallStaticBooleanMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jbyte JNICALL CallStaticByteMethod(jclass clazz, jmethodID methodID, ...); + virtual jbyte JNICALL CallStaticByteMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jbyte JNICALL CallStaticByteMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jchar JNICALL CallStaticCharMethod(jclass clazz, jmethodID methodID, ...); + virtual jchar JNICALL CallStaticCharMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jchar JNICALL CallStaticCharMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jshort JNICALL CallStaticShortMethod(jclass clazz, jmethodID methodID, ...); + virtual jshort JNICALL CallStaticShortMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jshort JNICALL CallStaticShortMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jint JNICALL CallStaticIntMethod(jclass clazz, jmethodID methodID, ...); + virtual jint JNICALL CallStaticIntMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jint JNICALL CallStaticIntMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jlong JNICALL CallStaticLongMethod(jclass clazz, jmethodID methodID, ...); + virtual jlong JNICALL CallStaticLongMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jlong JNICALL CallStaticLongMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jfloat JNICALL CallStaticFloatMethod(jclass clazz, jmethodID methodID, ...); + virtual jfloat JNICALL CallStaticFloatMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jfloat JNICALL CallStaticFloatMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual jdouble JNICALL CallStaticDoubleMethod(jclass clazz, jmethodID methodID, ...); + virtual jdouble JNICALL CallStaticDoubleMethodV(jclass clazz, jmethodID methodID, va_list args); + virtual jdouble JNICALL CallStaticDoubleMethodA(jclass clazz, jmethodID methodID, jvalue *args); + + virtual void JNICALL CallStaticVoidMethod(jclass cls, jmethodID methodID, ...); + virtual void JNICALL CallStaticVoidMethodV(jclass cls, jmethodID methodID, va_list args); + virtual void JNICALL CallStaticVoidMethodA(jclass cls, jmethodID methodID, jvalue * args); + + virtual jfieldID JNICALL GetStaticFieldID(jclass clazz, const char *name, const char *sig); + virtual jobject JNICALL GetStaticObjectField(jclass clazz, jfieldID fieldID); + virtual jboolean JNICALL GetStaticBooleanField(jclass clazz, jfieldID fieldID); + virtual jbyte JNICALL GetStaticByteField(jclass clazz, jfieldID fieldID); + virtual jchar JNICALL GetStaticCharField(jclass clazz, jfieldID fieldID); + virtual jshort JNICALL GetStaticShortField(jclass clazz, jfieldID fieldID); + virtual jint JNICALL GetStaticIntField(jclass clazz, jfieldID fieldID); + virtual jlong JNICALL GetStaticLongField(jclass clazz, jfieldID fieldID); + virtual jfloat JNICALL GetStaticFloatField(jclass clazz, jfieldID fieldID); + virtual jdouble JNICALL GetStaticDoubleField(jclass clazz, jfieldID fieldID); + + virtual void JNICALL SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value); + virtual void JNICALL SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value); + virtual void JNICALL SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value); + virtual void JNICALL SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value); + virtual void JNICALL SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value); + virtual void JNICALL SetStaticIntField(jclass clazz, jfieldID fieldID, jint value); + virtual void JNICALL SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value); + virtual void JNICALL SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value); + virtual void JNICALL SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value); + + virtual jstring JNICALL NewString(const jchar *unicode, jsize len); + virtual jsize JNICALL GetStringLength(jstring str); + virtual const jchar *JNICALL GetStringChars(jstring str, jboolean *isCopy); + virtual void JNICALL ReleaseStringChars(jstring str, const jchar *chars); + + virtual jstring JNICALL NewStringUTF(const char *utf); + virtual jsize JNICALL GetStringUTFLength(jstring str); + virtual const char* JNICALL GetStringUTFChars(jstring str, jboolean *isCopy); + virtual void JNICALL ReleaseStringUTFChars(jstring str, const char* chars); + + virtual jsize JNICALL GetArrayLength(jarray array); + + virtual jobjectArray JNICALL NewObjectArray(jsize len, jclass clazz, jobject init); + virtual jobject JNICALL GetObjectArrayElement(jobjectArray array, jsize index); + virtual void JNICALL SetObjectArrayElement(jobjectArray array, jsize index, jobject val); + + virtual jbooleanArray JNICALL NewBooleanArray(jsize len); + virtual jbyteArray JNICALL NewByteArray(jsize len); + virtual jcharArray JNICALL NewCharArray(jsize len); + virtual jshortArray JNICALL NewShortArray(jsize len); + virtual jintArray JNICALL NewIntArray(jsize len); + virtual jlongArray JNICALL NewLongArray(jsize len); + virtual jfloatArray JNICALL NewFloatArray(jsize len); + virtual jdoubleArray JNICALL NewDoubleArray(jsize len); + + virtual jboolean * JNICALL GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy); + virtual jbyte * JNICALL GetByteArrayElements(jbyteArray array, jboolean *isCopy); + virtual jchar * JNICALL GetCharArrayElements(jcharArray array, jboolean *isCopy); + virtual jshort * JNICALL GetShortArrayElements(jshortArray array, jboolean *isCopy); + virtual jint * JNICALL GetIntArrayElements(jintArray array, jboolean *isCopy); + virtual jlong * JNICALL GetLongArrayElements(jlongArray array, jboolean *isCopy); + virtual jfloat * JNICALL GetFloatArrayElements(jfloatArray array, jboolean *isCopy); + virtual jdouble * JNICALL GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy); + + virtual void JNICALL ReleaseBooleanArrayElements(jbooleanArray array, jboolean *elems, jint mode); + virtual void JNICALL ReleaseByteArrayElements(jbyteArray array, jbyte *elems, jint mode); + virtual void JNICALL ReleaseCharArrayElements(jcharArray array, jchar *elems, jint mode); + virtual void JNICALL ReleaseShortArrayElements(jshortArray array, jshort *elems, jint mode); + virtual void JNICALL ReleaseIntArrayElements(jintArray array, jint *elems, jint mode); + virtual void JNICALL ReleaseLongArrayElements(jlongArray array, jlong *elems, jint mode); + virtual void JNICALL ReleaseFloatArrayElements(jfloatArray array, jfloat *elems, jint mode); + virtual void JNICALL ReleaseDoubleArrayElements(jdoubleArray array, jdouble *elems, jint mode); + + virtual void JNICALL GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize l, jboolean *buf); + virtual void JNICALL GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf); + virtual void JNICALL GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf); + virtual void JNICALL GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf); + virtual void JNICALL GetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf); + virtual void JNICALL GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf); + virtual void JNICALL GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf); + virtual void JNICALL GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf); + + virtual void JNICALL SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize l, jboolean *buf); + virtual void JNICALL SetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte *buf); + virtual void JNICALL SetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar *buf); + virtual void JNICALL SetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort *buf); + virtual void JNICALL SetIntArrayRegion(jintArray array, jsize start, jsize len, jint *buf); + virtual void JNICALL SetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong *buf); + virtual void JNICALL SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat *buf); + virtual void JNICALL SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble *buf); + + virtual jint JNICALL RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods); + virtual jint JNICALL UnregisterNatives(jclass clazz); + + virtual jint JNICALL MonitorEnter(jobject obj); + virtual jint JNICALL MonitorExit(jobject obj); + + virtual jint JNICALL GetJavaVM(JavaVM **vm); +}; + +class JavaVM +{ +public: + virtual void JNICALL reserved0(); + virtual void JNICALL reserved1(); + virtual void JNICALL reserved2(); + virtual jint JNICALL DestroyJavaVM(); + virtual jint JNICALL AttachCurrentThread(void **penv, void *args); + virtual jint JNICALL DetachCurrentThread(); + virtual jint JNICALL GetEnv(void **penv, jint version); + virtual jint JNICALL AttachCurrentThreadAsDaemon(void **penv, void *args); +}; diff --git a/IK.VM.NET/AssemblyInfo.cs b/IK.VM.NET/AssemblyInfo.cs new file mode 100644 index 00000000..1853cb9f --- /dev/null +++ b/IK.VM.NET/AssemblyInfo.cs @@ -0,0 +1,81 @@ +/* + 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 + +*/ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("IK.VM.NET")] +[assembly: AssemblyDescription("JVM for .NET")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Copyright (C) 2002 Jeroen Frijters")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\<configuration>. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/IK.VM.NET/BigEndianBinaryReader.cs b/IK.VM.NET/BigEndianBinaryReader.cs new file mode 100644 index 00000000..105b4c95 --- /dev/null +++ b/IK.VM.NET/BigEndianBinaryReader.cs @@ -0,0 +1,177 @@ +/* + 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 + +*/ +using System; +using System.IO; + +sealed class BigEndianBinaryReader +{ + private byte[] buf; + private int pos; + + internal BigEndianBinaryReader(byte[] buf) + : this(buf, 0) + { + } + + internal BigEndianBinaryReader(byte[] buf, int offset) + { + this.buf = buf; + this.pos = offset; + } + + internal BigEndianBinaryReader Duplicate() + { + return new BigEndianBinaryReader(buf, pos); + } + + internal int Position + { + get + { + return pos; + } + } + + internal void Skip(int count) + { + pos += count; + } + + internal byte ReadByte() + { + return buf[pos++]; + } + + internal sbyte ReadSByte() + { + return (sbyte)buf[pos++]; + } + + internal double ReadDouble() + { + return BitConverter.Int64BitsToDouble(ReadInt64()); + } + + internal short ReadInt16() + { + short s = (short)((buf[pos] << 8) + buf[pos + 1]); + pos += 2; + return s; + } + + internal int ReadInt32() + { + int i = (int)((buf[pos] << 24) + (buf[pos + 1] << 16) + (buf[pos + 2] << 8) + buf[pos + 3]); + pos += 4; + return i; + } + + internal long ReadInt64() + { + uint i1 = (uint)((buf[pos] << 24) + (buf[pos + 1] << 16) + (buf[pos + 2] << 8) + buf[pos + 3]); + uint i2 = (uint)((buf[pos + 4] << 24) + (buf[pos + 5] << 16) + (buf[pos + 6] << 8) + buf[pos + 7]); + long l = (((long)i1) << 32) + i2; + pos += 8; + return l; + } + + internal float ReadSingle() + { + return BitConverter.ToSingle(BitConverter.GetBytes(ReadInt32()), 0); + } + + internal string ReadString() + { + int len = ReadUInt16(); + // special code path for ASCII strings (which occur *very* frequently) + for(int j = 0; j < len; j++) + { + if(buf[pos + j] >= 128) + { + // NOTE we *cannot* use System.Text.UTF8Encoding, because this is *not* compatible + // (esp. for embedded nulls) + char[] ch = new char[len]; + int l = 0; + for(int i = 0; i < len; i++) + { + int c = buf[pos + i]; + int char2, char3; + switch (c >> 4) + { + case 0: + if(c == 0) + { + throw new FormatException(); + } + break; + case 1: case 2: case 3: case 4: case 5: case 6: case 7: + // 0xxxxxxx + break; + case 12: case 13: + // 110x xxxx 10xx xxxx + char2 = buf[pos + ++i]; + if((char2 & 0xc0) != 0x80) + { + throw new FormatException(); + } + c = (((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = buf[pos + ++i]; + char3 = buf[pos + ++i]; + if((char2 & 0xc0) != 0x80 || (char3 & 0xc0) != 0x80) + { + throw new FormatException(); + } + c = (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); + break; + default: + throw new FormatException(); + } + ch[l++] = (char)c; + } + pos += len; + return new String(ch, 0, l); + } + } + string s = System.Text.ASCIIEncoding.ASCII.GetString(buf, pos, len); + pos += len; + return s; + } + + internal ushort ReadUInt16() + { + ushort s = (ushort)((buf[pos] << 8) + buf[pos + 1]); + pos += 2; + return s; + } + + internal uint ReadUInt32() + { + uint i = (uint)((buf[pos] << 24) + (buf[pos + 1] << 16) + (buf[pos + 2] << 8) + buf[pos + 3]); + pos += 4; + return i; + } +} diff --git a/IK.VM.NET/ByteCode.cs b/IK.VM.NET/ByteCode.cs new file mode 100644 index 00000000..d8836158 --- /dev/null +++ b/IK.VM.NET/ByteCode.cs @@ -0,0 +1,676 @@ +/* + 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 + +*/ +using System; + +enum ByteCode +{ + __nop = 0, + __aconst_null = 1, + __iconst_m1 = 2, + __iconst_0 = 3, + __iconst_1 = 4, + __iconst_2 = 5, + __iconst_3 = 6, + __iconst_4 = 7, + __iconst_5 = 8, + __lconst_0 = 9, + __lconst_1 = 10, + __fconst_0 = 11, + __fconst_1 = 12, + __fconst_2 = 13, + __dconst_0 = 14, + __dconst_1 = 15, + __bipush = 16, + __sipush = 17, + __ldc = 18, + __ldc_w = 19, + __ldc2_w = 20, + __iload = 21, + __lload = 22, + __fload = 23, + __dload = 24, + __aload = 25, + __iload_0 = 26, + __iload_1 = 27, + __iload_2 = 28, + __iload_3 = 29, + __lload_0 = 30, + __lload_1 = 31, + __lload_2 = 32, + __lload_3 = 33, + __fload_0 = 34, + __fload_1 = 35, + __fload_2 = 36, + __fload_3 = 37, + __dload_0 = 38, + __dload_1 = 39, + __dload_2 = 40, + __dload_3 = 41, + __aload_0 = 42, + __aload_1 = 43, + __aload_2 = 44, + __aload_3 = 45, + __iaload = 46, + __laload = 47, + __faload = 48, + __daload = 49, + __aaload = 50, + __baload = 51, + __caload = 52, + __saload = 53, + __istore = 54, + __lstore = 55, + __fstore = 56, + __dstore = 57, + __astore = 58, + __istore_0 = 59, + __istore_1 = 60, + __istore_2 = 61, + __istore_3 = 62, + __lstore_0 = 63, + __lstore_1 = 64, + __lstore_2 = 65, + __lstore_3 = 66, + __fstore_0 = 67, + __fstore_1 = 68, + __fstore_2 = 69, + __fstore_3 = 70, + __dstore_0 = 71, + __dstore_1 = 72, + __dstore_2 = 73, + __dstore_3 = 74, + __astore_0 = 75, + __astore_1 = 76, + __astore_2 = 77, + __astore_3 = 78, + __iastore = 79, + __lastore = 80, + __fastore = 81, + __dastore = 82, + __aastore = 83, + __bastore = 84, + __castore = 85, + __sastore = 86, + __pop = 87, + __pop2 = 88, + __dup = 89, + __dup_x1 = 90, + __dup_x2 = 91, + __dup2 = 92, + __dup2_x1 = 93, + __dup2_x2 = 94, + __swap = 95, + __iadd = 96, + __ladd = 97, + __fadd = 98, + __dadd = 99, + __isub = 100, + __lsub = 101, + __fsub = 102, + __dsub = 103, + __imul = 104, + __lmul = 105, + __fmul = 106, + __dmul = 107, + __idiv = 108, + __ldiv = 109, + __fdiv = 110, + __ddiv = 111, + __irem = 112, + __lrem = 113, + __frem = 114, + __drem = 115, + __ineg = 116, + __lneg = 117, + __fneg = 118, + __dneg = 119, + __ishl = 120, + __lshl = 121, + __ishr = 122, + __lshr = 123, + __iushr = 124, + __lushr = 125, + __iand = 126, + __land = 127, + __ior = 128, + __lor = 129, + __ixor = 130, + __lxor = 131, + __iinc = 132, + __i2l = 133, + __i2f = 134, + __i2d = 135, + __l2i = 136, + __l2f = 137, + __l2d = 138, + __f2i = 139, + __f2l = 140, + __f2d = 141, + __d2i = 142, + __d2l = 143, + __d2f = 144, + __i2b = 145, + __i2c = 146, + __i2s = 147, + __lcmp = 148, + __fcmpl = 149, + __fcmpg = 150, + __dcmpl = 151, + __dcmpg = 152, + __ifeq = 153, + __ifne = 154, + __iflt = 155, + __ifge = 156, + __ifgt = 157, + __ifle = 158, + __if_icmpeq = 159, + __if_icmpne = 160, + __if_icmplt = 161, + __if_icmpge = 162, + __if_icmpgt = 163, + __if_icmple = 164, + __if_acmpeq = 165, + __if_acmpne = 166, + __goto = 167, + __jsr = 168, + __ret = 169, + __tableswitch = 170, + __lookupswitch = 171, + __ireturn = 172, + __lreturn = 173, + __freturn = 174, + __dreturn = 175, + __areturn = 176, + __return = 177, + __getstatic = 178, + __putstatic = 179, + __getfield = 180, + __putfield = 181, + __invokevirtual = 182, + __invokespecial = 183, + __invokestatic = 184, + __invokeinterface = 185, + __xxxunusedxxx = 186, + __new = 187, + __newarray = 188, + __anewarray = 189, + __arraylength = 190, + __athrow = 191, + __checkcast = 192, + __instanceof = 193, + __monitorenter = 194, + __monitorexit = 195, + __wide = 196, + __multianewarray = 197, + __ifnull = 198, + __ifnonnull = 199, + __goto_w = 200, + __jsr_w = 201 +} + +enum NormalizedByteCode +{ + __nop = 0, + __aconst_null = 1, + __iconst = -1, + __lconst_0 = 9, + __lconst_1 = 10, + __fconst_0 = 11, + __fconst_1 = 12, + __fconst_2 = 13, + __dconst_0 = 14, + __dconst_1 = 15, + __ldc = 18, + __iload = 21, + __lload = 22, + __fload = 23, + __dload = 24, + __aload = 25, + __iaload = 46, + __laload = 47, + __faload = 48, + __daload = 49, + __aaload = 50, + __baload = 51, + __caload = 52, + __saload = 53, + __istore = 54, + __lstore = 55, + __fstore = 56, + __dstore = 57, + __astore = 58, + __iastore = 79, + __lastore = 80, + __fastore = 81, + __dastore = 82, + __aastore = 83, + __bastore = 84, + __castore = 85, + __sastore = 86, + __pop = 87, + __pop2 = 88, + __dup = 89, + __dup_x1 = 90, + __dup_x2 = 91, + __dup2 = 92, + __dup2_x1 = 93, + __dup2_x2 = 94, + __swap = 95, + __iadd = 96, + __ladd = 97, + __fadd = 98, + __dadd = 99, + __isub = 100, + __lsub = 101, + __fsub = 102, + __dsub = 103, + __imul = 104, + __lmul = 105, + __fmul = 106, + __dmul = 107, + __idiv = 108, + __ldiv = 109, + __fdiv = 110, + __ddiv = 111, + __irem = 112, + __lrem = 113, + __frem = 114, + __drem = 115, + __ineg = 116, + __lneg = 117, + __fneg = 118, + __dneg = 119, + __ishl = 120, + __lshl = 121, + __ishr = 122, + __lshr = 123, + __iushr = 124, + __lushr = 125, + __iand = 126, + __land = 127, + __ior = 128, + __lor = 129, + __ixor = 130, + __lxor = 131, + __iinc = 132, + __i2l = 133, + __i2f = 134, + __i2d = 135, + __l2i = 136, + __l2f = 137, + __l2d = 138, + __f2i = 139, + __f2l = 140, + __f2d = 141, + __d2i = 142, + __d2l = 143, + __d2f = 144, + __i2b = 145, + __i2c = 146, + __i2s = 147, + __lcmp = 148, + __fcmpl = 149, + __fcmpg = 150, + __dcmpl = 151, + __dcmpg = 152, + __ifeq = 153, + __ifne = 154, + __iflt = 155, + __ifge = 156, + __ifgt = 157, + __ifle = 158, + __if_icmpeq = 159, + __if_icmpne = 160, + __if_icmplt = 161, + __if_icmpge = 162, + __if_icmpgt = 163, + __if_icmple = 164, + __if_acmpeq = 165, + __if_acmpne = 166, + __goto = 167, + __jsr = 168, + __ret = 169, + __lookupswitch = 171, + __ireturn = 172, + __lreturn = 173, + __freturn = 174, + __dreturn = 175, + __areturn = 176, + __return = 177, + __getstatic = 178, + __putstatic = 179, + __getfield = 180, + __putfield = 181, + __invokevirtual = 182, + __invokespecial = 183, + __invokestatic = 184, + __invokeinterface = 185, + __new = 187, + __newarray = 188, + __anewarray = 189, + __arraylength = 190, + __athrow = 191, + __checkcast = 192, + __instanceof = 193, + __monitorenter = 194, + __monitorexit = 195, + __multianewarray = 197, + __ifnull = 198, + __ifnonnull = 199, +} + +enum ByteCodeMode +{ + Unused, + Simple, + Constant_1, + Constant_2, + Branch_2, + Local_1, + Local_2, + Constant_2_1_1, + Immediate_1, + Immediate_2, + Local_1_Immediate_1, + Local_2_Immediate_2, + Tableswitch, + Lookupswitch, + Constant_2_Immediate_1, + Branch_4 +} + +struct ByteCodeMetaData +{ + private static ByteCodeMetaData[] data = new ByteCodeMetaData[202]; + private ByteCodeMode reg; + private ByteCodeMode wide; + private NormalizedByteCode normbc; + private int arg; + private bool hasFixedArg; + + private ByteCodeMetaData(ByteCode bc, ByteCodeMode reg, ByteCodeMode wide) + { + this.reg = reg; + this.wide = wide; + this.normbc = (NormalizedByteCode)Enum.Parse(typeof(NormalizedByteCode), bc.ToString()); + this.arg = 0; + this.hasFixedArg = false; + data[(int)bc] = this; + } + + private ByteCodeMetaData(ByteCode bc, NormalizedByteCode normbc, ByteCodeMode reg, ByteCodeMode wide) + { + this.reg = reg; + this.wide = wide; + this.normbc = normbc; + this.arg = 0; + this.hasFixedArg = false; + data[(int)bc] = this; + } + + private ByteCodeMetaData(ByteCode bc, NormalizedByteCode normbc, int arg, ByteCodeMode reg, ByteCodeMode wide) + { + this.reg = reg; + this.wide = wide; + this.normbc = normbc; + this.arg = arg; + this.hasFixedArg = true; + data[(int)bc] = this; + } + + internal static NormalizedByteCode GetNormalizedByteCode(ByteCode bc) + { + return data[(int)bc].normbc; + } + + internal static int GetArg(ByteCode bc, int arg) + { + if(data[(int)bc].hasFixedArg) + { + return data[(int)bc].arg; + } + return arg; + } + + internal static ByteCodeMode GetMode(ByteCode bc) + { + return data[(int)bc].reg; + } + + internal static ByteCodeMode GetWideMode(ByteCode bc) + { + ByteCodeMode m = data[(int)bc].wide; + if(m == ByteCodeMode.Unused) + { + throw new ArgumentException("Wide version of " + bc + " is not a valid instruction", "bc"); + } + return m; + } + + static ByteCodeMetaData() + { + new ByteCodeMetaData(ByteCode.__nop, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__aconst_null, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iconst_m1, NormalizedByteCode.__iconst, -1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iconst_0, NormalizedByteCode.__iconst, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iconst_1, NormalizedByteCode.__iconst, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iconst_2, NormalizedByteCode.__iconst, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iconst_3, NormalizedByteCode.__iconst, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iconst_4, NormalizedByteCode.__iconst, 4, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iconst_5, NormalizedByteCode.__iconst, 5, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lconst_0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lconst_1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fconst_0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fconst_1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fconst_2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dconst_0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dconst_1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__bipush, NormalizedByteCode.__iconst, ByteCodeMode.Immediate_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__sipush, NormalizedByteCode.__iconst, ByteCodeMode.Immediate_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ldc, ByteCodeMode.Constant_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ldc_w, NormalizedByteCode.__ldc, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ldc2_w, NormalizedByteCode.__ldc, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iload, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lload, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fload, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dload, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__aload, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iload_0, NormalizedByteCode.__iload, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iload_1, NormalizedByteCode.__iload, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iload_2, NormalizedByteCode.__iload, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iload_3, NormalizedByteCode.__iload, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lload_0, NormalizedByteCode.__lload, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lload_1, NormalizedByteCode.__lload, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lload_2, NormalizedByteCode.__lload, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lload_3, NormalizedByteCode.__lload, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fload_0, NormalizedByteCode.__fload, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fload_1, NormalizedByteCode.__fload, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fload_2, NormalizedByteCode.__fload, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fload_3, NormalizedByteCode.__fload, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dload_0, NormalizedByteCode.__dload, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dload_1, NormalizedByteCode.__dload, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dload_2, NormalizedByteCode.__dload, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dload_3, NormalizedByteCode.__dload, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__aload_0, NormalizedByteCode.__aload, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__aload_1, NormalizedByteCode.__aload, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__aload_2, NormalizedByteCode.__aload, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__aload_3, NormalizedByteCode.__aload, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iaload, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__laload, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__faload, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__daload, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__aaload, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__baload, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__caload, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__saload, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__istore, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lstore, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fstore, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dstore, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__astore, ByteCodeMode.Local_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__istore_0, NormalizedByteCode.__istore, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__istore_1, NormalizedByteCode.__istore, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__istore_2, NormalizedByteCode.__istore, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__istore_3, NormalizedByteCode.__istore, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lstore_0, NormalizedByteCode.__lstore, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lstore_1, NormalizedByteCode.__lstore, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lstore_2, NormalizedByteCode.__lstore, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lstore_3, NormalizedByteCode.__lstore, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fstore_0, NormalizedByteCode.__fstore, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fstore_1, NormalizedByteCode.__fstore, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fstore_2, NormalizedByteCode.__fstore, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fstore_3, NormalizedByteCode.__fstore, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dstore_0, NormalizedByteCode.__dstore, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dstore_1, NormalizedByteCode.__dstore, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dstore_2, NormalizedByteCode.__dstore, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dstore_3, NormalizedByteCode.__dstore, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__astore_0, NormalizedByteCode.__astore, 0, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__astore_1, NormalizedByteCode.__astore, 1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__astore_2, NormalizedByteCode.__astore, 2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__astore_3, NormalizedByteCode.__astore, 3, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iastore, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lastore, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fastore, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dastore, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__aastore, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__bastore, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__castore, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__sastore, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__pop, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__pop2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dup, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dup_x1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dup_x2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dup2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dup2_x1, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dup2_x2, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__swap, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iadd, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ladd, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fadd, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dadd, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__isub, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lsub, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fsub, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dsub, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__imul, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lmul, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fmul, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dmul, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__idiv, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ldiv, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fdiv, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ddiv, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__irem, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lrem, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__frem, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__drem, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ineg, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lneg, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fneg, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dneg, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ishl, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lshl, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ishr, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lshr, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iushr, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lushr, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iand, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__land, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ior, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lor, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ixor, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lxor, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iinc, ByteCodeMode.Local_1_Immediate_1, ByteCodeMode.Local_2_Immediate_2); + new ByteCodeMetaData(ByteCode.__i2l, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__i2f, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__i2d, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__l2i, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__l2f, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__l2d, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__f2i, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__f2l, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__f2d, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__d2i, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__d2l, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__d2f, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__i2b, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__i2c, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__i2s, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lcmp, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fcmpl, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__fcmpg, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dcmpl, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dcmpg, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ifeq, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ifne, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__iflt, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ifge, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ifgt, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ifle, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__if_icmpeq, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__if_icmpne, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__if_icmplt, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__if_icmpge, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__if_icmpgt, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__if_icmple, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__if_acmpeq, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__if_acmpne, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__goto, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__jsr, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ret, ByteCodeMode.Local_1, ByteCodeMode.Local_2); + new ByteCodeMetaData(ByteCode.__tableswitch, NormalizedByteCode.__lookupswitch, ByteCodeMode.Tableswitch, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lookupswitch, ByteCodeMode.Lookupswitch, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ireturn, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__lreturn, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__freturn, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__dreturn, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__areturn, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__return, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__getstatic, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__putstatic, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__getfield, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__putfield, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__invokevirtual, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__invokespecial, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__invokestatic, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__invokeinterface, ByteCodeMode.Constant_2_1_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__xxxunusedxxx, NormalizedByteCode.__nop, ByteCodeMode.Unused, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__new, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__newarray, ByteCodeMode.Immediate_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__anewarray, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__arraylength, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__athrow, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__checkcast, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__instanceof, ByteCodeMode.Constant_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__monitorenter, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__monitorexit, ByteCodeMode.Simple, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__wide, NormalizedByteCode.__nop, ByteCodeMode.Unused, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__multianewarray, ByteCodeMode.Constant_2_Immediate_1, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ifnull, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__ifnonnull, ByteCodeMode.Branch_2, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__goto_w, NormalizedByteCode.__goto, ByteCodeMode.Branch_4, ByteCodeMode.Unused); + new ByteCodeMetaData(ByteCode.__jsr_w, NormalizedByteCode.__jsr, ByteCodeMode.Branch_4, ByteCodeMode.Unused); + } +} diff --git a/IK.VM.NET/ByteCodeHelper.cs b/IK.VM.NET/ByteCodeHelper.cs new file mode 100644 index 00000000..01477844 --- /dev/null +++ b/IK.VM.NET/ByteCodeHelper.cs @@ -0,0 +1,72 @@ +/* + 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 + +*/ +using System; + +public class ByteCodeHelper +{ + public static object multianewarray(RuntimeTypeHandle typeHandle, int[] lengths) + { + for(int i = 0; i < lengths.Length; i++) + { + if(lengths[i] < 0) + { + throw JavaException.NegativeArraySizeException(); + } + } + return MultianewarrayHelper(Type.GetTypeFromHandle(typeHandle).GetElementType(), lengths, 0); + } + + private static object MultianewarrayHelper(Type elemType, int[] lengths, int index) + { + object o = Array.CreateInstance(elemType, lengths[index++]); + if(index < lengths.Length) + { + elemType = elemType.GetElementType(); + object[] a = (object[])o; + for(int i = 0; i < a.Length; i++) + { + a[i] = MultianewarrayHelper(elemType, lengths, index); + } + } + return o; + } + + public static void monitorenter(object o) + { + if(o == null) + { + throw new NullReferenceException(); + } + System.Threading.Monitor.Enter(o); + } + + public static void monitorexit(object o) + { + if(o == null) + { + throw new NullReferenceException(); + } + System.Threading.Monitor.Exit(o); + } +} diff --git a/IK.VM.NET/ClassFile.cs b/IK.VM.NET/ClassFile.cs new file mode 100644 index 00000000..c823feb8 --- /dev/null +++ b/IK.VM.NET/ClassFile.cs @@ -0,0 +1,1579 @@ +/* + 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 + +*/ +using System; +using System.IO; +using System.Collections; + +[Flags] +public enum Modifiers : short +{ + Public = 0x0001, + Private = 0x0002, + Protected = 0x0004, + Static = 0x0008, + Final = 0x0010, + Super = 0x0020, + Synchronized = 0x0020, + Volatile = 0x0040, + Transient = 0x0080, + Native = 0x0100, + Interface = 0x0200, + Abstract = 0x0400, + Strictfp = 0x0800, + Synthetic = -1 // we use this to record the fact that we created the method/field/property + // so the member should not be visible through reflection +} + +class ClassFile +{ + private ConstantPoolItem[] constantpool; + private Modifiers access_flags; + private string name; + private string supername; + private string[] interfaces; + private Field[] fields; + private Method[] methods; + private Attribute[] attributes; + private string sourceFile; + private bool sourceFileCached; + private static readonly char[] illegalcharacters = { '<', '>' }; + + internal ClassFile(byte[] buf, int offset, int length, string inputClassName) + { + try + { + BigEndianBinaryReader br = new BigEndianBinaryReader(buf, offset); + if(br.ReadUInt32() != 0xCAFEBABE) + { + throw JavaException.ClassFormatError("Bad magic number"); + } + int minor = br.ReadUInt16(); + int major = br.ReadUInt16(); + if(major < 45 || major > 48) + { + throw JavaException.UnsupportedClassVersionError(major + "." + minor); + } + int constantpoolcount = br.ReadUInt16(); + constantpool = new ConstantPoolItem[constantpoolcount]; + for(int i = 1; i < constantpoolcount; i++) + { + constantpool[i] = ConstantPoolItem.Read(inputClassName, br); + // LONG and DOUBLE items take up two slots... + if(constantpool[i].IsWide) + { + i++; + } + } + for(int i = 1; i < constantpoolcount; i++) + { + if(constantpool[i] != null) + { + try + { + constantpool[i].Resolve(this); + } + catch(IndexOutOfRangeException) + { + throw JavaException.ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); + } + catch(InvalidCastException) + { + throw JavaException.ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); + } + } + } + access_flags = (Modifiers)br.ReadUInt16(); + // NOTE although the vmspec says (in 4.1) that interfaces must be marked abstract, earlier versions of + // javac (JDK 1.1) didn't do this, so the VM doesn't enforce this rule + // NOTE although the vmspec implies (in 4.1) that ACC_SUPER is illegal on interfaces, it doesn't enforce this + if((IsInterface && IsFinal) || (IsAbstract && IsFinal)) + { + throw JavaException.ClassFormatError("{0} (Illegal class modifiers 0x{1:X})", inputClassName, access_flags); + } + int this_class = br.ReadUInt16(); + try + { + name = ((ConstantPoolItemClass)constantpool[this_class]).Name; + } + catch(Exception) + { + throw JavaException.ClassFormatError("{0} (Class name has bad constant pool index)", inputClassName); + } + int super_class = br.ReadUInt16(); + // NOTE for convenience we allow parsing java/lang/Object (which has no super class), so + // we check for super_class != 0 + if(super_class != 0) + { + try + { + supername = ((ConstantPoolItemClass)constantpool[super_class]).Name; + } + catch(Exception) + { + throw JavaException.ClassFormatError("{0} (Bad superclass constant pool index)", inputClassName); + } + } + else + { + if(this.Name != "java/lang/Object") + { + throw JavaException.ClassFormatError("{0} (Bad superclass index)", Name); + } + } + if(IsInterface && (super_class == 0 || supername != "java/lang/Object")) + { + throw JavaException.ClassFormatError("{0} (Interfaces must have java.lang.Object as superclass)", Name); + } + int interfaces_count = br.ReadUInt16(); + interfaces = new string[interfaces_count]; + Hashtable interfaceNames = new Hashtable(); + for(int i = 0; i < interfaces_count; i++) + { + int index = br.ReadUInt16(); + if(index == 0 || index >= constantpool.Length) + { + throw JavaException.ClassFormatError("{0} (Illegal constant pool index)", Name); + } + ConstantPoolItemClass cpi = constantpool[index] as ConstantPoolItemClass; + if(cpi == null) + { + throw JavaException.ClassFormatError("{0} (Interface name has bad constant type)", Name); + } + interfaces[i] = ((ConstantPoolItemClass)GetConstantPoolItem(index)).Name; + if(interfaceNames.ContainsKey(interfaces[i])) + { + throw JavaException.ClassFormatError("{0} (Repetitive interface name)", Name); + } + interfaceNames.Add(interfaces[i], interfaces[i]); + } + int fields_count = br.ReadUInt16(); + fields = new Field[fields_count]; + Hashtable fieldNameSigs = new Hashtable(); + for(int i = 0; i < fields_count; i++) + { + fields[i] = new Field(this, br); + string name = fields[i].Name; + // NOTE It's not in the vmspec (as far as I can tell), but Sun's VM doens't allow names that + // contain '<' or '>' + if(name.Length == 0 || name.IndexOfAny(illegalcharacters) != -1) + { + throw JavaException.ClassFormatError("{0} (Illegal field name \"{1}\")", Name, name); + } + string nameSig = name + fields[i].Signature; + if(fieldNameSigs.ContainsKey(nameSig)) + { + throw JavaException.ClassFormatError("{0} (Repetitive field name/signature)", Name); + } + fieldNameSigs.Add(nameSig, nameSig); + } + int methods_count = br.ReadUInt16(); + methods = new Method[methods_count]; + Hashtable methodNameSigs = new Hashtable(); + for(int i = 0; i < methods_count; i++) + { + methods[i] = new Method(this, br); + string name = methods[i].Name; + string sig = methods[i].Signature; + if(name.Length == 0 || (name.IndexOfAny(illegalcharacters) != -1 && name != "<init>" && name != "<clinit>")) + { + throw JavaException.ClassFormatError("{0} (Illegal method name \"{1}\")", Name, name); + } + if((name == "<init>" || name == "<clinit>") && !sig.EndsWith("V")) + { + throw JavaException.ClassFormatError("{0} (Method \"{1}\" has illegal signature \"{2}\")", Name, name, sig); + } + string nameSig = name + sig; + if(methodNameSigs.ContainsKey(nameSig)) + { + throw JavaException.ClassFormatError("{0} (Repetitive method name/signature)", Name); + } + methodNameSigs.Add(nameSig, nameSig); + } + int attributes_count = br.ReadUInt16(); + attributes = new Attribute[attributes_count]; + for(int i = 0; i < attributes_count; i++) + { + attributes[i] = Attribute.Read(this, br); + } + if(br.Position != offset + length) + { + if(br.Position > offset + length) + { + throw JavaException.ClassFormatError("Truncated class file"); + } + else + { + throw JavaException.ClassFormatError("Extra bytes at the end of the class file"); + } + } + } + catch(IndexOutOfRangeException) + { + throw JavaException.ClassFormatError("Truncated class file"); + } +// catch(Exception) +// { +// FileStream fs = File.Create(inputClassName + ".broken"); +// fs.Write(buf, offset, length); +// fs.Close(); +// throw; +// } + } + + internal Modifiers Modifiers + { + get + { + return access_flags; + } + } + + internal bool IsAbstract + { + get + { + return (access_flags & Modifiers.Abstract) != 0; + } + } + + internal bool IsFinal + { + get + { + return (access_flags & Modifiers.Final) != 0; + } + } + + internal bool IsPublic + { + get + { + return (access_flags & Modifiers.Public) != 0; + } + } + + internal bool IsInterface + { + get + { + return (access_flags & Modifiers.Interface) != 0; + } + } + + internal bool IsSuper + { + get + { + return (access_flags & Modifiers.Super) != 0; + } + } + + internal ConstantPoolItemFieldref GetFieldref(int index) + { + return (ConstantPoolItemFieldref)constantpool[index]; + } + + // NOTE this returns an FMI, because it used for both normal methods and interface methods + internal ConstantPoolItemFMI GetMethodref(int index) + { + return (ConstantPoolItemFMI)constantpool[index]; + } + + private ConstantPoolItem GetConstantPoolItem(int index) + { + return constantpool[index]; + } + + internal string GetConstantPoolClass(int index) + { + return ((ConstantPoolItemClass)constantpool[index]).Name; + } + + private string GetConstantPoolString(int index) + { + return ((ConstantPoolItemString)constantpool[index]).Value; + } + + private string GetConstantPoolUtf8(int index) + { + return ((ConstantPoolItemUtf8)constantpool[index]).Value; + } + + internal object GetConstantPoolConstant(int index) + { + ConstantPoolItem cpi = constantpool[index]; + if(cpi is ConstantPoolItemDouble) + { + return ((ConstantPoolItemDouble)cpi).Value; + } + else if(cpi is ConstantPoolItemFloat) + { + return ((ConstantPoolItemFloat)cpi).Value; + } + else if(cpi is ConstantPoolItemInteger) + { + return ((ConstantPoolItemInteger)cpi).Value; + } + else if(cpi is ConstantPoolItemLong) + { + return ((ConstantPoolItemLong)cpi).Value; + } + else if(cpi is ConstantPoolItemString) + { + return ((ConstantPoolItemString)cpi).Value; + } + else + { + return null; + } + } + + internal string Name + { + get + { + return name; + } + } + + internal string PackageName + { + get + { + int index = name.LastIndexOf('/'); + if(index == -1) + { + return ""; + } + return name.Substring(0, index); + } + } + + internal string SuperClass + { + get + { + return supername; + } + } + + internal Field[] Fields + { + get + { + return fields; + } + } + + internal Method[] Methods + { + get + { + return methods; + } + } + + internal string[] Interfaces + { + get + { + return interfaces; + } + } + + private Attribute GetAttribute(string name) + { + for(int i = 0; i < attributes.Length; i++) + { + if(attributes[i].Name == name) + { + return attributes[i]; + } + } + return null; + } + + internal string SourceFileAttribute + { + get + { + if(!sourceFileCached) + { + sourceFileCached = true; + Attribute attr = GetAttribute("SourceFile"); + if(attr != null) + { + sourceFile = ((ConstantPoolItemUtf8)GetConstantPoolItem(attr.Data.ReadUInt16())).Value; + } + } + return sourceFile; + } + } + + internal string NetExpTypeAttribute + { + get + { + Attribute attr = GetAttribute("IK.VM.NET.Type"); + if(attr != null) + { + return ((ConstantPoolItemUtf8)GetConstantPoolItem(attr.Data.ReadUInt16())).Value; + } + return null; + } + } + + internal abstract class ConstantPoolItem + { + internal virtual bool IsWide + { + get + { + return false; + } + } + + internal virtual void Resolve(ClassFile classFile) + { + } + + internal static ConstantPoolItem Read(string inputClassName, BigEndianBinaryReader br) + { + byte tag = br.ReadByte(); + switch((Constant)tag) + { + case Constant.Class: + return new ConstantPoolItemClass(br); + case Constant.Double: + return new ConstantPoolItemDouble(br); + case Constant.Fieldref: + return new ConstantPoolItemFieldref(br); + case Constant.Float: + return new ConstantPoolItemFloat(br); + case Constant.Integer: + return new ConstantPoolItemInteger(br); + case Constant.InterfaceMethodref: + return new ConstantPoolItemInterfaceMethodref(br); + case Constant.Long: + return new ConstantPoolItemLong(br); + case Constant.Methodref: + return new ConstantPoolItemMethodref(br); + case Constant.NameAndType: + return new ConstantPoolItemNameAndType(br); + case Constant.String: + return new ConstantPoolItemString(br); + case Constant.Utf8: + return new ConstantPoolItemUtf8(inputClassName, br); + default: + throw JavaException.ClassFormatError("{0} (Illegal constant pool type 0x{1:X})", inputClassName, tag); + } + } + } + + private class ConstantPoolItemClass : ConstantPoolItem + { + private ushort name_index; + private string name; + + internal ConstantPoolItemClass(BigEndianBinaryReader br) + { + name_index = br.ReadUInt16(); + } + + internal override void Resolve(ClassFile classFile) + { + name = ((ConstantPoolItemUtf8)classFile.GetConstantPoolItem(name_index)).Value;; + } + + internal string Name + { + get + { + return name; + } + } + } + + private class ConstantPoolItemDouble : ConstantPoolItem + { + private double d; + + internal ConstantPoolItemDouble(BigEndianBinaryReader br) + { + d = br.ReadDouble(); + } + + internal double Value + { + get + { + return d; + } + } + + internal override bool IsWide + { + get + { + return true; + } + } + } + + internal class ConstantPoolItemFMI : ConstantPoolItem + { + private ushort class_index; + private ushort name_and_type_index; + private string name; + private string signature; + private string clazz; + + internal ConstantPoolItemFMI(BigEndianBinaryReader br) + { + class_index = br.ReadUInt16(); + name_and_type_index = br.ReadUInt16(); + } + + internal override void Resolve(ClassFile classFile) + { + ConstantPoolItemNameAndType nat = (ConstantPoolItemNameAndType)classFile.GetConstantPoolItem(name_and_type_index); + nat.Resolve(classFile); + name = nat.Name; + signature = nat.Type; + ConstantPoolItemClass cls = (ConstantPoolItemClass)classFile.GetConstantPoolItem(class_index); + cls.Resolve(classFile); + clazz = cls.Name; + } + + internal string Name + { + get + { + return name; + } + } + + internal string Signature + { + get + { + return signature; + } + } + + internal string Class + { + get + { + return clazz; + } + } + } + + internal class ConstantPoolItemFieldref : ConstantPoolItemFMI + { + internal ConstantPoolItemFieldref(BigEndianBinaryReader br) : base(br) + { + } + } + + internal class ConstantPoolItemMethodref : ConstantPoolItemFMI + { + internal ConstantPoolItemMethodref(BigEndianBinaryReader br) : base(br) + { + } + } + + internal class ConstantPoolItemInterfaceMethodref : ConstantPoolItemFMI + { + internal ConstantPoolItemInterfaceMethodref(BigEndianBinaryReader br) : base(br) + { + } + } + + private class ConstantPoolItemFloat : ConstantPoolItem + { + private float v; + + internal ConstantPoolItemFloat(BigEndianBinaryReader br) + { + v = br.ReadSingle(); + } + + internal float Value + { + get + { + return v; + } + } + } + + private class ConstantPoolItemInteger : ConstantPoolItem + { + private int v; + + internal ConstantPoolItemInteger(BigEndianBinaryReader br) + { + v = br.ReadInt32(); + } + + internal int Value + { + get + { + return v; + } + } + } + + private class ConstantPoolItemLong : ConstantPoolItem + { + private long l; + + internal ConstantPoolItemLong(BigEndianBinaryReader br) + { + l = br.ReadInt64(); + } + + internal long Value + { + get + { + return l; + } + } + + internal override bool IsWide + { + get + { + return true; + } + } + } + + private class ConstantPoolItemNameAndType : ConstantPoolItem + { + private ushort name_index; + private ushort descriptor_index; + private string name; + private string descriptor; + + internal ConstantPoolItemNameAndType(BigEndianBinaryReader br) + { + name_index = br.ReadUInt16(); + descriptor_index = br.ReadUInt16(); + } + + internal override void Resolve(ClassFile classFile) + { + ConstantPoolItemUtf8 nameUtf8 = (ConstantPoolItemUtf8)classFile.GetConstantPoolItem(name_index); + nameUtf8.Resolve(classFile); + name = nameUtf8.Value; + ConstantPoolItemUtf8 descriptorUtf8 = (ConstantPoolItemUtf8)classFile.GetConstantPoolItem(descriptor_index); + descriptorUtf8.Resolve(classFile); + descriptor = descriptorUtf8.Value; + } + + internal string Name + { + get + { + return name; + } + } + + internal string Type + { + get + { + return descriptor; + } + } + } + + private class ConstantPoolItemString : ConstantPoolItem + { + private ushort string_index; + private string s; + + internal ConstantPoolItemString(BigEndianBinaryReader br) + { + string_index = br.ReadUInt16(); + } + + internal override void Resolve(ClassFile classFile) + { + ConstantPoolItemUtf8 utf8 = (ConstantPoolItemUtf8)classFile.GetConstantPoolItem(string_index); + utf8.Resolve(classFile); + s = utf8.Value; + } + + internal string Value + { + get + { + return s; + } + } + } + + private class ConstantPoolItemUtf8 : ConstantPoolItem + { + private string s; + + internal ConstantPoolItemUtf8(string inputClassName, BigEndianBinaryReader br) + { + try + { + s = br.ReadString(); + } + catch(FormatException) + { + throw JavaException.ClassFormatError("{0} (Illegal UTF8 string in constant pool)", inputClassName); + } + } + + internal string Value + { + get + { + return s; + } + } + } + + private enum Constant + { + Utf8 = 1, + Integer = 3, + Float = 4, + Long = 5, + Double = 6, + Class = 7, + String = 8, + Fieldref = 9, + Methodref = 10, + InterfaceMethodref = 11, + NameAndType = 12 + } + + internal class Attribute + { + private string name; + private BigEndianBinaryReader data; + + private Attribute() + { + } + + internal static Attribute Read(ClassFile classFile, BigEndianBinaryReader br) + { + try + { + int name_index = br.ReadUInt16(); + string name = classFile.GetConstantPoolUtf8(name_index); + int attribute_length = br.ReadInt32(); + Attribute a = new Attribute(); + a.name = name; + a.data = br.Duplicate(); + br.Skip(attribute_length); + return a; + } + catch(InvalidCastException) + { + } + catch(NullReferenceException) + { + } + catch(IndexOutOfRangeException) + { + } + throw JavaException.ClassFormatError("{0} (Attribute name invalid type)", classFile.Name); + } + + internal string Name + { + get + { + return name; + } + } + + internal BigEndianBinaryReader Data + { + get + { + return data.Duplicate(); + } + } + } + + internal class FieldOrMethod + { + private ClassFile classFile; + protected Modifiers access_flags; + private ushort name_index; + private ushort descriptor_index; + private Attribute[] attributes; + + internal FieldOrMethod(ClassFile classFile, BigEndianBinaryReader br) + { + this.classFile = classFile; + access_flags = (Modifiers)br.ReadUInt16(); + // TODO check that name is ConstantPoolItemUtf8 + name_index = br.ReadUInt16(); + // TODO check that descriptor is ConstantPoolItemUtf8 and validate the descriptor + descriptor_index = br.ReadUInt16(); + int attributes_count = br.ReadUInt16(); + attributes = new Attribute[attributes_count]; + for(int i = 0; i < attributes_count; i++) + { + attributes[i] = Attribute.Read(classFile, br); + } + } + + internal string Name + { + get + { + return classFile.GetConstantPoolUtf8(name_index); + } + } + + internal string Signature + { + get + { + return classFile.GetConstantPoolUtf8(descriptor_index); + } + } + + internal Modifiers Modifiers + { + get + { + return (Modifiers)access_flags; + } + } + + internal bool IsAbstract + { + get + { + return (access_flags & Modifiers.Abstract) != 0; + } + } + + internal bool IsFinal + { + get + { + return (access_flags & Modifiers.Final) != 0; + } + } + + internal bool IsPublic + { + get + { + return (access_flags & Modifiers.Public) != 0; + } + } + + internal bool IsPrivate + { + get + { + return (access_flags & Modifiers.Private) != 0; + } + } + + internal bool IsProtected + { + get + { + return (access_flags & Modifiers.Protected) != 0; + } + } + + internal bool IsStatic + { + get + { + return (access_flags & Modifiers.Static) != 0; + } + } + + internal bool IsSynchronized + { + get + { + return (access_flags & Modifiers.Synchronized) != 0; + } + } + + internal bool IsVolatile + { + get + { + return (access_flags & Modifiers.Volatile) != 0; + } + } + + internal bool IsTransient + { + get + { + return (access_flags & Modifiers.Transient) != 0; + } + } + + internal bool IsNative + { + get + { + return (access_flags & Modifiers.Native) != 0; + } + } + + protected Attribute GetAttribute(string name) + { + foreach(Attribute attr in attributes) + { + if(attr.Name == name) + { + return attr; + } + } + return null; + } + + internal ClassFile ClassFile + { + get + { + return classFile; + } + } + } + + internal class Field : FieldOrMethod + { + private object constantValue; + + internal Field(ClassFile classFile, BigEndianBinaryReader br) : base(classFile, br) + { + if((IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected) + || (IsFinal && IsVolatile) + || (classFile.IsInterface && (!IsPublic || !IsStatic || !IsFinal || IsTransient))) + { + throw JavaException.ClassFormatError("{0} (Illegal field modifiers: 0x{1:X})", classFile.Name, access_flags); + } + Attribute attr = GetAttribute("ConstantValue"); + if(attr != null) + { + ushort index = attr.Data.ReadUInt16(); + try + { + object o = classFile.GetConstantPoolConstant(index); + if(o == null) + { + throw JavaException.ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); + } + switch(Signature) + { + case "I": + constantValue = (int)o; + break; + case "S": + constantValue = (short)(int)o; + break; + case "B": + constantValue = (sbyte)(int)o; + break; + case "C": + constantValue = (char)(int)o; + break; + case "Z": + constantValue = ((int)o) != 0; + break; + case "J": + constantValue = (long)o; + break; + case "F": + constantValue = (float)o; + break; + case "D": + constantValue = (double)o; + break; + case "Ljava/lang/String;": + constantValue = (string)o; + break; + default: + throw JavaException.ClassFormatError("{0} (Invalid signature for constant)", classFile.Name); + } + } + catch(InvalidCastException) + { + throw JavaException.ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); + } + catch(IndexOutOfRangeException) + { + throw JavaException.ClassFormatError("{0} (Bad index into constant pool)", classFile.Name); + } + } + } + + internal object ConstantValue + { + get + { + return constantValue; + } + } + } + + internal class Method : FieldOrMethod + { + private Code code; + + internal Method(ClassFile classFile, BigEndianBinaryReader br) : base(classFile, br) + { + // vmspec 4.6 says that all flags, except ACC_STRICT are ignored on <clinit> + if(Name == "<clinit>") + { + access_flags &= Modifiers.Strictfp; + access_flags |= (Modifiers.Static | Modifiers.Private); + } + else + { + if((Name == "<init>" && (IsStatic || IsSynchronized || IsFinal || IsAbstract)) + || (IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected) + || (IsAbstract && (IsFinal || IsNative || IsPrivate || IsStatic || IsStrict || IsSynchronized)) + || (classFile.IsInterface && (!IsPublic || !IsAbstract))) + { + throw JavaException.ClassFormatError("{0} (Illegal method modifiers: 0x{1:X})", classFile.Name, access_flags); + } + } + // TODO if the method is abstract or native it may not have a Code attribute (right?) + // and if it is not abstract or native, it must have a Code attribute + } + + internal bool IsStrict + { + get + { + return (access_flags & Modifiers.Strictfp) != 0; + } + } + + internal string[] NetExpSigAttribute + { + get + { + Attribute attr = GetAttribute("IK.VM.NET.Sig"); + if(attr != null) + { + string s = ClassFile.GetConstantPoolUtf8(attr.Data.ReadUInt16()); + if(s.Length == 0) + { + return new string[0]; + } + return s.Split('|'); + } + return null; + } + } + + internal Code CodeAttribute + { + get + { + if(code == null) + { + Attribute attr = GetAttribute("Code"); + if(attr != null) + { + code = new Code(this, attr); + } + } + return code; + } + } + + internal class Code + { + private Method method; + private ushort max_stack; + private ushort max_locals; + private Instruction[] instructions; + private int[] pcIndexMap; + private ExceptionTableEntry[] exception_table; + private Attribute[] codeAttributes; + private int[] argmap; + private LineNumberTableEntry[] lineNumberTable; + private bool lineNumberTableCached; + private LocalVariableTableEntry[] localVariableTable; + private bool localVariableTableCached; + + internal Code(Method method, Attribute attr) + { + this.method = method; + BigEndianBinaryReader br = attr.Data; + max_stack = br.ReadUInt16(); + max_locals = br.ReadUInt16(); + uint code_length = br.ReadUInt32(); + ArrayList instructions = new ArrayList(); + int basePosition = br.Position; + while(br.Position - basePosition < code_length) + { + instructions.Add(Instruction.Read(this, br.Position - basePosition, br)); + } + // we add an additional nop instruction to make it easier for consumers of the code array + instructions.Add(new Instruction(this, br.Position - basePosition, ByteCode.__nop)); + this.instructions = (Instruction[])instructions.ToArray(typeof(Instruction)); + ushort exception_table_length = br.ReadUInt16(); + exception_table = new ExceptionTableEntry[exception_table_length]; + for(int i = 0; i < exception_table_length; i++) + { + exception_table[i] = new ExceptionTableEntry(); + exception_table[i].start_pc = br.ReadUInt16(); + exception_table[i].end_pc = br.ReadUInt16(); + exception_table[i].handler_pc = br.ReadUInt16(); + exception_table[i].catch_type = br.ReadUInt16(); + exception_table[i].ordinal = i; + } + ushort attributes_count = br.ReadUInt16(); + codeAttributes = new Attribute[attributes_count]; + for(int i = 0; i < attributes_count; i++) + { + codeAttributes[i] = Attribute.Read(method.ClassFile, br); + } + // build the pcIndexMap + pcIndexMap = new int[this.instructions[this.instructions.Length - 1].PC + 1]; + for(int i = 0; i < pcIndexMap.Length; i++) + { + pcIndexMap[i] = -1; + } + for(int i = 0; i < this.instructions.Length; i++) + { + pcIndexMap[this.instructions[i].PC] = i; + } + } + + // maps argument 'slot' (as encoded in the xload/xstore instructions) into the ordinal + internal int[] ArgMap + { + get + { + if(argmap == null) + { + string sig = method.Signature; + ArrayList args = new ArrayList(); + int pos = 0; + if(!method.IsStatic) + { + args.Add(pos++); + } + for(int i = 1; sig[i] != ')'; i++) + { + args.Add(pos++); + switch(sig[i]) + { + case 'L': + i = sig.IndexOf(';', i); + break; + case 'D': + case 'J': + args.Add(-1); + break; + case '[': + { + while(sig[i] == '[') + { + i++; + } + if(sig[i] == 'L') + { + i = sig.IndexOf(';', i); + } + break; + } + } + } + argmap = new int[args.Count]; + args.CopyTo(argmap); + } + return argmap; + } + } + + internal Method Method + { + get + { + return method; + } + } + + internal int MaxStack + { + get + { + return max_stack; + } + } + + internal int MaxLocals + { + get + { + return max_locals; + } + } + + internal Instruction[] Instructions + { + get + { + return instructions; + } + } + + // maps a PC to an index in the Instruction[], invalid PCs return -1 + internal int[] PcIndexMap + { + get + { + return pcIndexMap; + } + } + + internal ExceptionTableEntry[] ExceptionTable + { + get + { + return exception_table; + } + } + + private Attribute GetAttribute(string name) + { + foreach(Attribute attr in codeAttributes) + { + if(attr.Name == name) + { + return attr; + } + } + return null; + } + + internal LineNumberTableEntry[] LineNumberTableAttribute + { + get + { + if(!lineNumberTableCached) + { + lineNumberTableCached = true; + Attribute attr = GetAttribute("LineNumberTable"); + if(attr != null) + { + BigEndianBinaryReader rdr = attr.Data; + int count = rdr.ReadUInt16(); + lineNumberTable = new LineNumberTableEntry[count]; + for(int i = 0; i < count; i++) + { + lineNumberTable[i].start_pc = rdr.ReadUInt16(); + lineNumberTable[i].line_number = rdr.ReadUInt16(); + } + } + } + return lineNumberTable; + } + } + + internal LocalVariableTableEntry[] LocalVariableTableAttribute + { + get + { + if(!localVariableTableCached) + { + localVariableTableCached = true; + Attribute attr = GetAttribute("LocalVariableTable"); + if(attr != null) + { + BigEndianBinaryReader rdr = attr.Data; + int count = rdr.ReadUInt16(); + localVariableTable = new LocalVariableTableEntry[count]; + for(int i = 0; i < count; i++) + { + localVariableTable[i].start_pc = rdr.ReadUInt16(); + localVariableTable[i].length = rdr.ReadUInt16(); + localVariableTable[i].name = method.ClassFile.GetConstantPoolUtf8(rdr.ReadUInt16()); + localVariableTable[i].descriptor = method.ClassFile.GetConstantPoolUtf8(rdr.ReadUInt16()); + localVariableTable[i].index = rdr.ReadUInt16(); + } + } + } + return localVariableTable; + } + } + } + + internal class ExceptionTableEntry + { + internal ushort start_pc; + internal ushort end_pc; + internal ushort handler_pc; + internal ushort catch_type; + internal int ordinal; + } + + internal class Instruction + { + private Method.Code method; + private int pc; + private ByteCode opcode; + private int arg1; + private int arg2; + private int default_offset; + private int[] values; + private int[] target_offsets; + + internal Instruction(Method.Code method, int pc, ByteCode opcode) + : this(method, pc, opcode, 0) + { + } + + private Instruction(Method.Code method, int pc, ByteCode opcode, int arg1) + : this(method, pc, opcode, arg1, 0) + { + } + + private Instruction(Method.Code method, int pc, ByteCode opcode, int arg1, int arg2) + { + this.method = method; + this.pc = pc; + this.opcode = opcode; + this.arg1 = arg1; + this.arg2 = arg2; + } + + private Instruction(Method.Code method, int pc, ByteCode opcode, int default_offset, int[] values, int[] target_offsets) + : this(method, pc, opcode) + { + this.default_offset = default_offset; + this.values = values; + this.target_offsets = target_offsets; + } + + internal static Instruction Read(Method.Code method, int pc, BigEndianBinaryReader br) + { + ByteCode bc = (ByteCode)br.ReadByte(); + ByteCodeMode mode = ByteCodeMetaData.GetMode(bc); + if(bc == ByteCode.__wide) + { + bc = (ByteCode)br.ReadByte(); + // NOTE the PC of a wide instruction is actually the PC of the + // wide prefix, not the following instruction (vmspec 4.9.2) + mode = ByteCodeMetaData.GetWideMode(bc); + } + switch(mode) + { + case ByteCodeMode.Simple: + return new Instruction(method, pc, bc); + case ByteCodeMode.Constant_1: + case ByteCodeMode.Local_1: + return new Instruction(method, pc, bc, br.ReadByte()); + case ByteCodeMode.Constant_2: + case ByteCodeMode.Local_2: + return new Instruction(method, pc, bc, br.ReadUInt16()); + case ByteCodeMode.Branch_2: + return new Instruction(method, pc, bc, br.ReadInt16()); + case ByteCodeMode.Branch_4: + return new Instruction(method, pc, bc, br.ReadInt32()); + case ByteCodeMode.Constant_2_1_1: + { + Instruction instr = new Instruction(method, pc, bc, br.ReadUInt16()); + // TODO validate these + br.ReadByte(); // count + br.ReadByte(); // unused + return instr; + } + case ByteCodeMode.Immediate_1: + return new Instruction(method, pc, bc, br.ReadSByte()); + case ByteCodeMode.Immediate_2: + return new Instruction(method, pc, bc, br.ReadInt16()); + case ByteCodeMode.Local_1_Immediate_1: + return new Instruction(method, pc, bc, br.ReadByte(), br.ReadSByte()); + case ByteCodeMode.Local_2_Immediate_2: + return new Instruction(method, pc, bc, br.ReadUInt16(), br.ReadInt16()); + case ByteCodeMode.Constant_2_Immediate_1: + return new Instruction(method, pc, bc, br.ReadUInt16(), br.ReadSByte()); + case ByteCodeMode.Tableswitch: + { + // skip the padding + int p = pc + 1; + int align = ((p + 3) & 0x7ffffffc) - p; + for(int i = 0; i < align; i++) + { + br.ReadByte(); + } + int default_offset = br.ReadInt32(); + int low = br.ReadInt32(); + int high = br.ReadInt32(); + int[] values = new int[high - low + 1]; + int[] target_offset = new int[high - low + 1]; + for(int i = low; i <= high; i++) + { + values[i - low] = i; + target_offset[i - low] = br.ReadInt32(); + } + return new Instruction(method, pc, bc, default_offset, values, target_offset); + } + case ByteCodeMode.Lookupswitch: + { + // skip the padding + int p = pc + 1; + int align = ((p + 3) & 0x7ffffffc) - p; + for(int i = 0; i < align; i++) + { + br.ReadByte(); + } + int default_offset = br.ReadInt32(); + int count = br.ReadInt32(); + int[] values = new int[count]; + int[] target_offset = new int[count]; + for(int i = 0; i < count; i++) + { + values[i] = br.ReadInt32(); + target_offset[i] = br.ReadInt32(); + } + return new Instruction(method, pc, bc, default_offset, values, target_offset); + } + default: + throw JavaException.ClassFormatError("Invalid opcode: {0}", bc); + } + } + + internal int PC + { + get + { + return pc; + } + } + + internal ByteCode OpCode + { + get + { + return opcode; + } + } + + internal NormalizedByteCode NormalizedOpCode + { + get + { + return ByteCodeMetaData.GetNormalizedByteCode(opcode); + } + } + + internal int Arg1 + { + get + { + return arg1; + } + } + + internal int Arg2 + { + get + { + return arg2; + } + } + + internal int NormalizedArg1 + { + get + { + return ByteCodeMetaData.GetArg(opcode, arg1); + } + } + + internal int DefaultOffset + { + get + { + return default_offset; + } + } + + internal int[] Values + { + get + { + return values; + } + } + + internal int[] TargetOffsets + { + get + { + return target_offsets; + } + } + + internal Method.Code MethodCode + { + get + { + return method; + } + } + } + + internal struct LineNumberTableEntry + { + internal int start_pc; + internal int line_number; + } + + internal struct LocalVariableTableEntry + { + internal int start_pc; + internal int length; + internal string name; + internal string descriptor; + internal int index; + } + } +} diff --git a/IK.VM.NET/ClassLoaderWrapper.cs b/IK.VM.NET/ClassLoaderWrapper.cs new file mode 100644 index 00000000..b4c6ea77 --- /dev/null +++ b/IK.VM.NET/ClassLoaderWrapper.cs @@ -0,0 +1,900 @@ +#define DEBUG +/* + 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 + +*/ +using System; +using System.Reflection.Emit; +using System.Reflection; +using System.IO; +using System.Collections; +using System.Xml; +using System.Diagnostics; + +class ClassLoaderWrapper +{ + private static bool arrayConstructionHack; + private static ArrayList ikvmAssemblies = new ArrayList(); + private static Hashtable assemblyToClassLoaderWrapper = new Hashtable(); + private static Hashtable javaClassLoaderToClassLoaderWrapper = new Hashtable(); + private static ArrayList classLoaders = new ArrayList(); + private static Hashtable dynamicTypes = new Hashtable(); + // TODO typeToTypeWrapper should be an identity hashtable + private static Hashtable typeToTypeWrapper = new Hashtable(); + private static ClassLoaderWrapper bootstrapClassLoader; + private object javaClassLoader; + private MethodInfo loadClassMethod; + private Hashtable types = new Hashtable(); + private Hashtable nativeMethods; + // HACK moduleBuilder is static, because multiple dynamic assemblies is broken (TypeResolve doesn't fire) + // so for the time being, we share one dynamic assembly among all classloaders + private static ModuleBuilder moduleBuilder; + + // HACK this is used by the ahead-of-time compiler to overrule the bootstrap classloader + internal static void SetBootstrapClassLoader(ClassLoaderWrapper bootstrapClassLoader) + { + if(ClassLoaderWrapper.bootstrapClassLoader != null) + { + throw new InvalidOperationException(); + } + ClassLoaderWrapper.bootstrapClassLoader = bootstrapClassLoader; + } + + static ClassLoaderWrapper() + { + AppDomain.CurrentDomain.TypeResolve += new ResolveEventHandler(OnTypeResolve); + AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(OnAssemblyLoad); + foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies()) + { + if(a.IsDefined(typeof(IKVMAssemblyAttribute), false) && !(a is AssemblyBuilder)) + { + ikvmAssemblies.Add(a); + } + } + } + + private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs e) + { + if(e.LoadedAssembly.IsDefined(typeof(IKVMAssemblyAttribute), false) && !(e.LoadedAssembly is AssemblyBuilder)) + { + ikvmAssemblies.Add(e.LoadedAssembly); + } + } + + private static Assembly OnTypeResolve(object sender, ResolveEventArgs args) + { + //Console.WriteLine("OnTypeResolve: " + args.Name); + if(arrayConstructionHack) + { + return null; + } + try + { + TypeWrapper type = (TypeWrapper)dynamicTypes[args.Name]; + if(type == null) + { + return null; + } + type.Finish(); + return type.Type.Assembly; + } + catch(Exception x) + { + // TODO don't catch the exception here... But, the problem is that Type.GetType() swallows all exceptions + // that occur here, unless throwOnError is set, but in some (most?) cases you don't want the exception if it only + // means that the class cannot be found... + Console.WriteLine(x); + Console.WriteLine(new StackTrace(true)); + throw; + } + } + + internal ClassLoaderWrapper(object javaClassLoader) + { + this.javaClassLoader = javaClassLoader; + if(javaClassLoader != null) + { + loadClassMethod = javaClassLoader.GetType().GetMethod("loadClass", BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Standard, new Type[] { typeof(string) }, null); + if(loadClassMethod == null) + { + throw new InvalidOperationException(); + } + } + classLoaders.Add(this); + } + + internal void LoadRemappedTypes() + { + nativeMethods = new Hashtable(); + // TODO interfaces have java/lang/Object as the base type (do they really?) + types["java.lang.Cloneable"] = new RemappedTypeWrapper(ModifiersAttribute.GetModifiers(typeof(java.lang.Cloneable)), "java/lang/Cloneable", typeof(java.lang.Cloneable), new TypeWrapper[0], null); + typeToTypeWrapper.Add(typeof(java.lang.Cloneable), types["java.lang.Cloneable"]); + types["java.io.Serializable"] = new RemappedTypeWrapper(ModifiersAttribute.GetModifiers(typeof(java.io.Serializable)), "java/io/Serializable", typeof(java.io.Serializable), new TypeWrapper[0], null); + typeToTypeWrapper.Add(typeof(java.io.Serializable), types["java.io.Serializable"]); + MapXml.Root map = null; + using(Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("map.xml")) + { + // TODO the XmlSerializer generates a bunch of C# code and compiles that. This is very slow, we probably + // shouldn't use it. + System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(MapXml.Root)); + map = (MapXml.Root)ser.Deserialize(s); + } + foreach(MapXml.Class c in map.remappings) + { + TypeWrapper baseWrapper = null; + // HACK need to resolve the base type or put it in the XML + if(c.Type != "System.Object") + { + baseWrapper = (TypeWrapper)types["java.lang.Object"]; + } + string name = c.Name; + Modifiers modifiers = (Modifiers)c.Modifiers; + // TODO specify interfaces + TypeWrapper tw = new RemappedTypeWrapper(modifiers, name.Replace('.', '/'), Type.GetType(c.Type, true), new TypeWrapper[0], baseWrapper); + types.Add(name, tw); + typeToTypeWrapper.Add(tw.Type, tw); + } + foreach(MapXml.Class c in map.remappings) + { + ((RemappedTypeWrapper)types[c.Name]).LoadRemappings(c); + } + // native methods + foreach(MapXml.Class c in map.nativeMethods) + { + string className = c.Name; + foreach(MapXml.Method method in c.Methods) + { + string methodName = method.Name; + string methodSig = method.Sig; + nativeMethods[className + "." + methodName + methodSig] = method; + } + } + } + + internal TypeWrapper LoadClassBySlashedName(string name) + { + // OPTIMIZE + return LoadClassByDottedName(name.Replace('/', '.')); + } + + // TODO implement vmspec 5.3.4 Loading Constraints + internal TypeWrapper LoadClassByDottedName(string name) + { + TypeWrapper type = (TypeWrapper)types[name]; + if(type != null) + { + return type; + } + if(name.Length > 1 && name[0] == '[') + { + int dims = 1; + while(name[dims] == '[') + { + dims++; + } + switch(name[dims]) + { + case 'L': + { + type = LoadClassByDottedName(name.Substring(dims + 1, name.IndexOf(';', dims) - dims - 1)); + type = type.GetClassLoader().CreateArrayType(name, type.Type, dims); + return type; + } + case 'B': + return GetBootstrapClassLoader().CreateArrayType(name, typeof(sbyte), dims); + case 'C': + return GetBootstrapClassLoader().CreateArrayType(name, typeof(char), dims); + case 'D': + return GetBootstrapClassLoader().CreateArrayType(name, typeof(double), dims); + case 'F': + return GetBootstrapClassLoader().CreateArrayType(name, typeof(float), dims); + case 'I': + return GetBootstrapClassLoader().CreateArrayType(name, typeof(int), dims); + case 'J': + return GetBootstrapClassLoader().CreateArrayType(name, typeof(long), dims); + case 'S': + return GetBootstrapClassLoader().CreateArrayType(name, typeof(short), dims); + case 'Z': + return GetBootstrapClassLoader().CreateArrayType(name, typeof(bool), dims); + default: + // TODO I'm not sure this is the right exception here (instead we could throw a NoClassDefFoundError) + throw JavaException.ClassNotFoundException(name); + } + } + if(javaClassLoader == null) + { + // HACK if the name contains a comma, we assume it is an assembly qualified name + if(name.IndexOf(',') != -1) + { + Type t = Type.GetType(name); + if(t != null) + { + return GetCompiledTypeWrapper(t); + } + } + type = GetBootstrapType(name); + if(type != null) + { + return type; + } + throw JavaException.ClassNotFoundException(name); + } + else + { + // OPTIMIZE this should be optimized + try + { + object clazz; + // NOTE just like Java does (I think), we take the classloader lock before calling the loadClass method + lock(javaClassLoader) + { + clazz = loadClassMethod.Invoke(javaClassLoader, new object[] { name }); + } + type = (TypeWrapper)clazz.GetType().GetField("wrapper", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(clazz); + if(type == null) + { + Type t = (Type)clazz.GetType().GetField("type", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(clazz); + ClassLoaderWrapper loader = GetClassLoader(t); + type = (TypeWrapper)loader.types[name]; + if(type == null) + { + // this shouldn't be possible + throw new InvalidOperationException(name + ", this = " + javaClassLoader); + } + } + // NOTE we're caching types loaded by parent classloaders as well! + // TODO not sure if this is correct + if(type.GetClassLoader() != this) + { + if(types[name] != type) + { + types.Add(name, type); + } + } + return type; + } + catch(TargetInvocationException x) + { + ExceptionHelper.MapExceptionFast(x); + throw x.InnerException; + } + } + } + + private TypeWrapper GetCompiledTypeWrapper(Type type) + { + Debug.Assert(!(type is TypeBuilder)); + // only the bootstrap classloader can own compiled types + Debug.Assert(javaClassLoader == null); + string name = NativeCode.java.lang.Class.getName(type); + TypeWrapper wrapper = (TypeWrapper)types[name]; + if(wrapper == null) + { + TypeWrapper baseType; + if(type.BaseType == null) + { + baseType = LoadClassByDottedName("java.lang.Object"); + } + else + { + baseType = GetWrapperFromType(type.BaseType); + } + wrapper = new CompiledTypeWrapper(name.Replace('.', '/'), type, baseType); + types.Add(name, wrapper); + // TODO shouldn't we add the <type,wrapper> to the typeToTypeWrapper hashtable? + } + return wrapper; + } + + internal virtual Type GetBootstrapTypeRaw(string name) + { + // TODO consider the thread safety aspects of this (if another thread triggers a load of an IKVM assembly, + // the collection enumerator will throw a version exception) + foreach(Assembly a in ikvmAssemblies) + { + Type t = a.GetType(name); + if(t != null) + { + return t; + } + } + return null; + } + + internal virtual TypeWrapper GetBootstrapType(string name) + { + Type t = GetBootstrapTypeRaw(name); + if(t != null) + { + return GetCompiledTypeWrapper(t); + } + return null; + } + + private TypeWrapper CreateArrayType(string name, Type elementType, int dims) + { + // TODO array accessibility should be the same as the elementType's accessibility + // (and this should be enforced) + TypeWrapper wrapper = (TypeWrapper)types[name]; + if(wrapper == null) + { + String netname = "[]"; + for(int i = 1; i < dims; i++) + { + netname += "[]"; + } + Type array; + if(elementType.Module is ModuleBuilder) + { + // HACK ModuleBuilder.GetType() is broken (I think), it fires a TypeResolveEvent when + // you try to construct an array type from an unfinished type. I don't think it should + // do that. We have to work around that by setting a global flag (yuck) to prevent us + // from responding to the TypeResolveEvent. + arrayConstructionHack = true; + try + { + array = ((ModuleBuilder)elementType.Module).GetType(elementType.FullName + netname); + } + finally + { + arrayConstructionHack = false; + } + } + else + { + array = elementType.Assembly.GetType(elementType.FullName + netname, true); + } + TypeWrapper[] interfaces = new TypeWrapper[2]; + interfaces[0] = GetBootstrapClassLoader().LoadClassByDottedName("java.lang.Cloneable"); + interfaces[1] = GetBootstrapClassLoader().LoadClassByDottedName("java.io.Serializable"); + MethodDescriptor mdClone = new MethodDescriptor(GetBootstrapClassLoader(), "clone", "()Ljava/lang/Object;"); + Modifiers modifiers = Modifiers.Final | Modifiers.Public; + // TODO copy accessibility from element type + wrapper = new RemappedTypeWrapper(modifiers, name, array, interfaces, GetBootstrapClassLoader().LoadClassByDottedName("java.lang.Object")); + MethodInfo clone = typeof(Array).GetMethod("Clone"); + MethodWrapper mw = new MethodWrapper(wrapper, mdClone, clone, Modifiers.Public); + mw.EmitCall = CodeEmitter.Create(OpCodes.Callvirt, clone); + mw.EmitCallvirt = CodeEmitter.Create(OpCodes.Callvirt, clone); + wrapper.AddMethod(mw); + types.Add(name, wrapper); + } + return wrapper; + } + + // TODO make sure class isn't defined already + // TODO check for circularity + // TODO disallow anyone other than the bootstrap classloader defining classes in the "java." package + internal TypeWrapper DefineClass(ClassFile f) + { + // TODO shouldn't this check be in ClassFile.cs? + if(f.Name.Length == 0 || f.Name[0] == '[') + { + throw JavaException.ClassFormatError("Bad name"); + } + TypeWrapper type; + // TODO if the class doesn't exist, LoadClassBySlashedName throws a ClassNotFoundException, but + // we need to catch that and throw a NoClassDefFoundError (because that is unchecked) + // TODO also figure out what should happen if LoadClassBySlashedName throws another exception (custom class loaders + // can throw whatever exception they want) + TypeWrapper baseType = LoadClassBySlashedName(f.SuperClass); + // if the base type isn't public, it must be in the same package + if(!baseType.IsPublic) + { + if(baseType.GetClassLoader() != this || f.PackageName != baseType.PackageName) + { + throw JavaException.IllegalAccessError("Class {0} cannot access its superclass {1}", f.Name, baseType.Name); + } + } + if(baseType.IsFinal) + { + throw JavaException.VerifyError("Cannot inherit from final class"); + } + if(baseType.IsInterface) + { + throw JavaException.IncompatibleClassChangeError("Class {0} has interface {1} as superclass", f.Name, baseType.Name); + } + string dotnetType = f.NetExpTypeAttribute; + if(dotnetType != null) + { + type = new NetExpTypeWrapper(f, dotnetType, baseType); + } + else + { + type = new DynamicTypeWrapper(f.Name, f, baseType, this, nativeMethods); + dynamicTypes.Add(f.Name.Replace('/', '.'), type); + } + types.Add(f.Name.Replace('/', '.'), type); + return type; + } + + internal object GetJavaClassLoader() + { + return javaClassLoader; + } + + internal static void SaveDebugImage(object mainClass) + { + // HACK we iterate 3 times, in the hopes that that will be enough. We really should let FinishAll return a boolean whether + // anything was done, and continue iterating until all FinishAlls return false. + for(int i = 0; i < 3; i++) + { + foreach(ClassLoaderWrapper wrapper in classLoaders) + { + wrapper.FinishAll(); + } + } + // HACK use reflection to get the type from the class + Type mainType = NativeCode.java.lang.Class.getType(mainClass); + MethodInfo main = mainType.GetMethod("main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(string[]) }, null); + AssemblyBuilder asm = ((AssemblyBuilder)moduleBuilder.Assembly); + asm.SetEntryPoint(main, PEFileKinds.ConsoleApplication); + asm.Save(moduleBuilder.Name); + } + + // this version isn't used at the moment, because multi assembly type references are broken in the CLR + internal static void SaveDebugImage__MultiAssemblyVersion(object mainClass) + { + // HACK we iterate 3 times, in the hopes that that will be enough. We really should let FinishAll return a boolean whether + // anything was done, and continue iterating until all FinishAlls return false. + for(int i = 0; i < 3; i++) + { + foreach(DictionaryEntry entry in assemblyToClassLoaderWrapper) + { + AssemblyBuilder asm = (AssemblyBuilder)entry.Key; + ClassLoaderWrapper loader = (ClassLoaderWrapper)entry.Value; + loader.FinishAll(); + } + } + // HACK use reflection to get the type from the class + Type mainType = NativeCode.java.lang.Class.getType(mainClass); + MethodInfo main = mainType.GetMethod("main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(string[]) }, null); + foreach(DictionaryEntry entry in ClassLoaderWrapper.assemblyToClassLoaderWrapper) + { + AssemblyBuilder asm = (AssemblyBuilder)entry.Key; + ClassLoaderWrapper loader = (ClassLoaderWrapper)entry.Value; + if(mainType.Assembly.Equals(asm)) + { + asm.SetEntryPoint(main, PEFileKinds.ConsoleApplication); + } + asm.Save(asm.GetName().Name); + } + } + + internal void FinishAll() + { + int prevCount = -1; + while(prevCount != types.Count) + { + prevCount = types.Count; + ArrayList l = new ArrayList(); + foreach(TypeWrapper t in types.Values) + { + l.Add(t); + } + foreach(TypeWrapper t in l) + { + t.Finish(); + } + } + } + + internal ModuleBuilder ModuleBuilder + { + get + { + lock(this) + { + if(moduleBuilder == null) + { + moduleBuilder = CreateModuleBuilder(); + lock(assemblyToClassLoaderWrapper.SyncRoot) + { + assemblyToClassLoaderWrapper[moduleBuilder.Assembly] = this; + } + } + return moduleBuilder; + } + } + } + + protected virtual ModuleBuilder CreateModuleBuilder() + { + AssemblyName name = new AssemblyName(); + name.Name = "ikvm_dynamic_assembly__" + (javaClassLoader == null ? "bootstrap" : javaClassLoader); + AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave); + ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name.Name, JVM.Debug); + if(JVM.Debug) + { + CustomAttributeBuilder debugAttr = new CustomAttributeBuilder(typeof(DebuggableAttribute).GetConstructor(new Type[] { typeof(bool), typeof(bool) }), new object[] { true, true }); + moduleBuilder.SetCustomAttribute(debugAttr); + } + return moduleBuilder; + } + + internal Type ExpressionType(string type) + { + // HACK to ease the burden of the compiler, we support the Lret pseudo type here + if(type.StartsWith("Lret;")) + { + return typeof(int); + } + if(type == "Lnull") + { + throw new InvalidOperationException("ExpressionType for Lnull requested"); + } + int index = 0; + return SigDecoder(ref index, type); + } + + // NOTE: this will ignore anything following the sig marker (so that it can be used to decode method signatures) + private Type SigDecoder(ref int index, string sig) + { + switch(sig[index++]) + { + case 'B': + return typeof(sbyte); + case 'C': + return typeof(char); + case 'D': + return typeof(double); + case 'F': + return typeof(float); + case 'I': + return typeof(int); + case 'J': + return typeof(long); + case 'L': + { + int pos = index; + index = sig.IndexOf(';', index) + 1; + return LoadClassBySlashedName(sig.Substring(pos, index - pos - 1)).Type; + } + case 'S': + return typeof(short); + case 'Z': + return typeof(bool); + case 'V': + return typeof(void); + case '[': + { + // TODO this can be optimized + string array = "["; + while(sig[index] == '[') + { + index++; + array += "["; + } + switch(sig[index]) + { + case 'L': + { + int pos = index; + index = sig.IndexOf(';', index) + 1; + return LoadClassBySlashedName(array + sig.Substring(pos, index - pos)).Type; + } + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + return LoadClassBySlashedName(array + sig[index++]).Type; + default: + throw new InvalidOperationException(sig.Substring(index)); + } + } + default: + throw new InvalidOperationException("Invalid at " + index + " in " + sig); + } + } + + internal Type RetTypeFromSig(string sig) + { + int index = sig.IndexOf(')') + 1; + return SigDecoder(ref index, sig); + } + + internal Type[] ArgTypeListFromSig(string sig) + { + if(sig[1] == ')') + { + return Type.EmptyTypes; + } + ArrayList list = new ArrayList(); + for(int i = 1; sig[i] != ')';) + { + list.Add(SigDecoder(ref i, sig)); + } + Type[] types = new Type[list.Count]; + list.CopyTo(types); + return types; + } + + // NOTE: this will ignore anything following the sig marker (so that it can be used to decode method signatures) + private TypeWrapper SigDecoderWrapper(ref int index, string sig) + { + switch(sig[index++]) + { + case 'B': + return PrimitiveTypeWrapper.BYTE; + case 'C': + return PrimitiveTypeWrapper.CHAR; + case 'D': + return PrimitiveTypeWrapper.DOUBLE; + case 'F': + return PrimitiveTypeWrapper.FLOAT; + case 'I': + return PrimitiveTypeWrapper.INT; + case 'J': + return PrimitiveTypeWrapper.LONG; + case 'L': + { + int pos = index; + index = sig.IndexOf(';', index) + 1; + return LoadClassBySlashedName(sig.Substring(pos, index - pos - 1)); + } + case 'S': + return PrimitiveTypeWrapper.SHORT; + case 'Z': + return PrimitiveTypeWrapper.BOOLEAN; + case 'V': + return PrimitiveTypeWrapper.VOID; + case '[': + { + // TODO this can be optimized + string array = "["; + while(sig[index] == '[') + { + index++; + array += "["; + } + switch(sig[index]) + { + case 'L': + { + int pos = index; + index = sig.IndexOf(';', index) + 1; + return LoadClassBySlashedName(array + sig.Substring(pos, index - pos)); + } + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + return LoadClassBySlashedName(array + sig[index++]); + default: + throw new InvalidOperationException(sig.Substring(index)); + } + } + default: + throw new InvalidOperationException(sig.Substring(index)); + } + } + + internal TypeWrapper RetTypeWrapperFromSig(string sig) + { + int index = sig.IndexOf(')') + 1; + return SigDecoderWrapper(ref index, sig); + } + + internal TypeWrapper[] ArgTypeWrapperListFromSig(string sig) + { + if(sig[1] == ')') + { + return new TypeWrapper[0]; + } + ArrayList list = new ArrayList(); + for(int i = 1; sig[i] != ')';) + { + list.Add(SigDecoderWrapper(ref i, sig)); + } + TypeWrapper[] types = new TypeWrapper[list.Count]; + list.CopyTo(types); + return types; + } + + // subType and baseType are Java class name (e.g. java/lang/Object) + internal bool IsSubType(string subType, string baseType) + { + return LoadClassBySlashedName(subType).IsSubTypeOf(LoadClassBySlashedName(baseType)); + } + + internal string FindCommonBaseType(string type1, string type2) + { + TypeWrapper t1 = LoadClassBySlashedName(type1); + TypeWrapper t2 = LoadClassBySlashedName(type2); + if(t1 == t2) + { + return type1; + } + if(t1.IsInterface || t2.IsInterface) + { + // TODO I don't know how finding the common base for interfaces is defined, but + // for now I'm just doing the naive thing + // UPDATE according to a paper by Alessandro Coglio & Allen Goldberg titled + // "Type Safety in the JVM: Some Problems in Java 2 SDK 1.2 and Proposed Solutions" + // the common base of two interfaces is java/lang/Object, and there is special + // treatment for java/lang/Object types that allow it to be assigned to any interface + // type, the JVM's typesafety then depends on the invokeinterface instruction to make + // sure that the reference actually implements the interface. + // So strictly speaking, the code below isn't correct, but it works, so for now it stays in. + if(t1.ImplementsInterface(t2)) + { + return t2.Name; + } + if(t2.ImplementsInterface(t1)) + { + return t1.Name; + } + return "java/lang/Object"; + } + Stack st1 = new Stack(); + Stack st2 = new Stack(); + while(t1 != null) + { + st1.Push(t1); + t1 = t1.BaseTypeWrapper; + } + while(t2 != null) + { + st2.Push(t2); + t2 = t2.BaseTypeWrapper; + } + TypeWrapper type = null; + for(;;) + { + t1 = st1.Count > 0 ? (TypeWrapper)st1.Pop() : null; + t2 = st2.Count > 0 ? (TypeWrapper)st2.Pop() : null; + if(t1 != t2) + { + return type.Name; + } + type = t1; + } + } + + internal static ClassLoaderWrapper GetBootstrapClassLoader() + { + if(bootstrapClassLoader == null) + { + bootstrapClassLoader = new ClassLoaderWrapper(null); + bootstrapClassLoader.LoadRemappedTypes(); + } + return bootstrapClassLoader; + } + + internal static ClassLoaderWrapper GetClassLoaderWrapper(object javaClassLoader) + { + if(javaClassLoader == null) + { + return GetBootstrapClassLoader(); + } + ClassLoaderWrapper wrapper = (ClassLoaderWrapper)javaClassLoaderToClassLoaderWrapper[javaClassLoader]; + if(wrapper == null) + { + wrapper = new ClassLoaderWrapper(javaClassLoader); + javaClassLoaderToClassLoaderWrapper[javaClassLoader] = wrapper; + } + return wrapper; + } + + internal static ClassLoaderWrapper GetClassLoader(Type type) + { + Debug.Assert(!(type is TypeBuilder)); + TypeWrapper wrapper = GetWrapperFromTypeFast(type); + if(wrapper != null) + { + return wrapper.GetClassLoader(); + } + return GetBootstrapClassLoader(); +// ClassLoaderWrapper loader = (ClassLoaderWrapper)assemblyToClassLoaderWrapper[type.Assembly]; +// if(loader == null) +// { +// loader = GetBootstrapClassLoader(); +// } +// return loader; + } + + // This only returns the wrapper for a Type if that wrapper has already been created, otherwise + // it returns null + // If the wrapper doesn't exist, that means that the type is either a .NET type or a pre-compiled Java class + internal static TypeWrapper GetWrapperFromTypeFast(Type type) + { + Debug.Assert(!(type is TypeBuilder)); + return (TypeWrapper)typeToTypeWrapper[type]; + } + + internal static TypeWrapper GetWrapperFromType(Type type) + { + Debug.Assert(!(type is TypeBuilder)); + TypeWrapper wrapper = GetWrapperFromTypeFast(type); + if(wrapper == null) + { + // if the wrapper doesn't already exist, that must mean that the type + // is a .NET type (or a pre-compiled Java class), which means that it + // was "loaded" by the bootstrap classloader + // TODO think up a scheme to deal with .NET types that have the same name. Since all .NET types + // appear in the boostrap classloader, we need to devise a scheme to mangle the class name + if(type.IsPrimitive || type == typeof(void)) + { + if(type == typeof(void)) + { + return PrimitiveTypeWrapper.VOID; + } + else if(type == typeof(sbyte)) + { + return PrimitiveTypeWrapper.BYTE; + } + else if(type == typeof(char)) + { + return PrimitiveTypeWrapper.CHAR; + } + else if(type == typeof(double)) + { + return PrimitiveTypeWrapper.DOUBLE; + } + else if(type == typeof(float)) + { + return PrimitiveTypeWrapper.FLOAT; + } + else if(type == typeof(int)) + { + return PrimitiveTypeWrapper.INT; + } + else if(type == typeof(long)) + { + return PrimitiveTypeWrapper.LONG; + } + else if(type == typeof(short)) + { + return PrimitiveTypeWrapper.SHORT; + } + else if(type == typeof(bool)) + { + return PrimitiveTypeWrapper.BOOLEAN; + } + } + wrapper = GetBootstrapClassLoader().GetCompiledTypeWrapper(type); + } + return wrapper; + } + + internal static void SetWrapperForType(Type type, TypeWrapper wrapper) + { + Debug.Assert(!(type is TypeBuilder)); + typeToTypeWrapper.Add(type, wrapper); + } + + // name is dot separated (e.g. java.lang.Object) + internal static Type GetType(string name) + { + TypeWrapper wrapper = GetBootstrapClassLoader().LoadClassByDottedName(name); + // TODO think about this Finish here + wrapper.Finish(); + return wrapper.Type; + } +} diff --git a/IK.VM.NET/DoubleToString.cs b/IK.VM.NET/DoubleToString.cs new file mode 100644 index 00000000..1078c761 --- /dev/null +++ b/IK.VM.NET/DoubleToString.cs @@ -0,0 +1,539 @@ +// NOTE this code was adapted from source code accompanying the article +// http://www.onjava.com/pub/a/onjava/2000/12/15/formatting_doubles.html?page=2 +// by Jack Shirazi + +using System; +using System.Text; + +public class DoubleToString +{ + //Hardcode some arrays to make them quickly available + private static readonly string[] ZEROS = new string[] { + "", + "0", + "00", + "000", + "0000", + "00000", + "000000", + "0000000", + "00000000", + "000000000", + "0000000000", + "00000000000", + "000000000000", + "0000000000000", + "00000000000000", + "000000000000000", + "0000000000000000", + "00000000000000000", + "000000000000000000", + "0000000000000000000", + "00000000000000000000" + }; + + private static readonly char[] charForDigit = new char[] { + '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h', + 'i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' + }; + + //And required double related constants. + private const long DoubleSignMask = long.MinValue; + private const long DoubleExpMask = 0x7ff0000000000000L; + private const long DoubleFractMask= ~(DoubleSignMask|DoubleExpMask); + private const int DoubleExpShift = 52; + private const int DoubleExpBias = 1023; + + private static readonly double[] d_tenthPowers = new double[] { + 1e-323D, 1e-322D, 1e-321D, 1e-320D, 1e-319D, 1e-318D, 1e-317D, 1e-316D, 1e-315D, 1e-314D, + 1e-313D, 1e-312D, 1e-311D, 1e-310D, 1e-309D, 1e-308D, 1e-307D, 1e-306D, 1e-305D, 1e-304D, + 1e-303D, 1e-302D, 1e-301D, 1e-300D, 1e-299D, 1e-298D, 1e-297D, 1e-296D, 1e-295D, 1e-294D, + 1e-293D, 1e-292D, 1e-291D, 1e-290D, 1e-289D, 1e-288D, 1e-287D, 1e-286D, 1e-285D, 1e-284D, + 1e-283D, 1e-282D, 1e-281D, 1e-280D, 1e-279D, 1e-278D, 1e-277D, 1e-276D, 1e-275D, 1e-274D, + 1e-273D, 1e-272D, 1e-271D, 1e-270D, 1e-269D, 1e-268D, 1e-267D, 1e-266D, 1e-265D, 1e-264D, + 1e-263D, 1e-262D, 1e-261D, 1e-260D, 1e-259D, 1e-258D, 1e-257D, 1e-256D, 1e-255D, 1e-254D, + 1e-253D, 1e-252D, 1e-251D, 1e-250D, 1e-249D, 1e-248D, 1e-247D, 1e-246D, 1e-245D, 1e-244D, + 1e-243D, 1e-242D, 1e-241D, 1e-240D, 1e-239D, 1e-238D, 1e-237D, 1e-236D, 1e-235D, 1e-234D, + 1e-233D, 1e-232D, 1e-231D, 1e-230D, 1e-229D, 1e-228D, 1e-227D, 1e-226D, 1e-225D, 1e-224D, + 1e-223D, 1e-222D, 1e-221D, 1e-220D, 1e-219D, 1e-218D, 1e-217D, 1e-216D, 1e-215D, 1e-214D, + 1e-213D, 1e-212D, 1e-211D, 1e-210D, 1e-209D, 1e-208D, 1e-207D, 1e-206D, 1e-205D, 1e-204D, + 1e-203D, 1e-202D, 1e-201D, 1e-200D, 1e-199D, 1e-198D, 1e-197D, 1e-196D, 1e-195D, 1e-194D, + 1e-193D, 1e-192D, 1e-191D, 1e-190D, 1e-189D, 1e-188D, 1e-187D, 1e-186D, 1e-185D, 1e-184D, + 1e-183D, 1e-182D, 1e-181D, 1e-180D, 1e-179D, 1e-178D, 1e-177D, 1e-176D, 1e-175D, 1e-174D, + 1e-173D, 1e-172D, 1e-171D, 1e-170D, 1e-169D, 1e-168D, 1e-167D, 1e-166D, 1e-165D, 1e-164D, + 1e-163D, 1e-162D, 1e-161D, 1e-160D, 1e-159D, 1e-158D, 1e-157D, 1e-156D, 1e-155D, 1e-154D, + 1e-153D, 1e-152D, 1e-151D, 1e-150D, 1e-149D, 1e-148D, 1e-147D, 1e-146D, 1e-145D, 1e-144D, + 1e-143D, 1e-142D, 1e-141D, 1e-140D, 1e-139D, 1e-138D, 1e-137D, 1e-136D, 1e-135D, 1e-134D, + 1e-133D, 1e-132D, 1e-131D, 1e-130D, 1e-129D, 1e-128D, 1e-127D, 1e-126D, 1e-125D, 1e-124D, + 1e-123D, 1e-122D, 1e-121D, 1e-120D, 1e-119D, 1e-118D, 1e-117D, 1e-116D, 1e-115D, 1e-114D, + 1e-113D, 1e-112D, 1e-111D, 1e-110D, 1e-109D, 1e-108D, 1e-107D, 1e-106D, 1e-105D, 1e-104D, + 1e-103D, 1e-102D, 1e-101D, 1e-100D, 1e-99D, 1e-98D, 1e-97D, 1e-96D, 1e-95D, 1e-94D, + 1e-93D, 1e-92D, 1e-91D, 1e-90D, 1e-89D, 1e-88D, 1e-87D, 1e-86D, 1e-85D, 1e-84D, + 1e-83D, 1e-82D, 1e-81D, 1e-80D, 1e-79D, 1e-78D, 1e-77D, 1e-76D, 1e-75D, 1e-74D, + 1e-73D, 1e-72D, 1e-71D, 1e-70D, 1e-69D, 1e-68D, 1e-67D, 1e-66D, 1e-65D, 1e-64D, + 1e-63D, 1e-62D, 1e-61D, 1e-60D, 1e-59D, 1e-58D, 1e-57D, 1e-56D, 1e-55D, 1e-54D, + 1e-53D, 1e-52D, 1e-51D, 1e-50D, 1e-49D, 1e-48D, 1e-47D, 1e-46D, 1e-45D, 1e-44D, + 1e-43D, 1e-42D, 1e-41D, 1e-40D, 1e-39D, 1e-38D, 1e-37D, 1e-36D, 1e-35D, 1e-34D, + 1e-33D, 1e-32D, 1e-31D, 1e-30D, 1e-29D, 1e-28D, 1e-27D, 1e-26D, 1e-25D, 1e-24D, + 1e-23D, 1e-22D, 1e-21D, 1e-20D, 1e-19D, 1e-18D, 1e-17D, 1e-16D, 1e-15D, 1e-14D, + 1e-13D, 1e-12D, 1e-11D, 1e-10D, 1e-9D, 1e-8D, 1e-7D, 1e-6D, 1e-5D, 1e-4D, + 1e-3D, 1e-2D, 1e-1D, 1e0D, 1e1D, 1e2D, 1e3D, 1e4D, + 1e5D, 1e6D, 1e7D, 1e8D, 1e9D, 1e10D, 1e11D, 1e12D, 1e13D, 1e14D, + 1e15D, 1e16D, 1e17D, 1e18D, 1e19D, 1e20D, 1e21D, 1e22D, 1e23D, 1e24D, + 1e25D, 1e26D, 1e27D, 1e28D, 1e29D, 1e30D, 1e31D, 1e32D, 1e33D, 1e34D, + 1e35D, 1e36D, 1e37D, 1e38D, 1e39D, 1e40D, 1e41D, 1e42D, 1e43D, 1e44D, + 1e45D, 1e46D, 1e47D, 1e48D, 1e49D, 1e50D, 1e51D, 1e52D, 1e53D, 1e54D, + 1e55D, 1e56D, 1e57D, 1e58D, 1e59D, 1e60D, 1e61D, 1e62D, 1e63D, 1e64D, + 1e65D, 1e66D, 1e67D, 1e68D, 1e69D, 1e70D, 1e71D, 1e72D, 1e73D, 1e74D, + 1e75D, 1e76D, 1e77D, 1e78D, 1e79D, 1e80D, 1e81D, 1e82D, 1e83D, 1e84D, + 1e85D, 1e86D, 1e87D, 1e88D, 1e89D, 1e90D, 1e91D, 1e92D, 1e93D, 1e94D, + 1e95D, 1e96D, 1e97D, 1e98D, 1e99D, 1e100D, 1e101D, 1e102D, 1e103D, 1e104D, + 1e105D, 1e106D, 1e107D, 1e108D, 1e109D, 1e110D, 1e111D, 1e112D, 1e113D, 1e114D, + 1e115D, 1e116D, 1e117D, 1e118D, 1e119D, 1e120D, 1e121D, 1e122D, 1e123D, 1e124D, + 1e125D, 1e126D, 1e127D, 1e128D, 1e129D, 1e130D, 1e131D, 1e132D, 1e133D, 1e134D, + 1e135D, 1e136D, 1e137D, 1e138D, 1e139D, 1e140D, 1e141D, 1e142D, 1e143D, 1e144D, + 1e145D, 1e146D, 1e147D, 1e148D, 1e149D, 1e150D, 1e151D, 1e152D, 1e153D, 1e154D, + 1e155D, 1e156D, 1e157D, 1e158D, 1e159D, 1e160D, 1e161D, 1e162D, 1e163D, 1e164D, + 1e165D, 1e166D, 1e167D, 1e168D, 1e169D, 1e170D, 1e171D, 1e172D, 1e173D, 1e174D, + 1e175D, 1e176D, 1e177D, 1e178D, 1e179D, 1e180D, 1e181D, 1e182D, 1e183D, 1e184D, + 1e185D, 1e186D, 1e187D, 1e188D, 1e189D, 1e190D, 1e191D, 1e192D, 1e193D, 1e194D, + 1e195D, 1e196D, 1e197D, 1e198D, 1e199D, 1e200D, 1e201D, 1e202D, 1e203D, 1e204D, + 1e205D, 1e206D, 1e207D, 1e208D, 1e209D, 1e210D, 1e211D, 1e212D, 1e213D, 1e214D, + 1e215D, 1e216D, 1e217D, 1e218D, 1e219D, 1e220D, 1e221D, 1e222D, 1e223D, 1e224D, + 1e225D, 1e226D, 1e227D, 1e228D, 1e229D, 1e230D, 1e231D, 1e232D, 1e233D, 1e234D, + 1e235D, 1e236D, 1e237D, 1e238D, 1e239D, 1e240D, 1e241D, 1e242D, 1e243D, 1e244D, + 1e245D, 1e246D, 1e247D, 1e248D, 1e249D, 1e250D, 1e251D, 1e252D, 1e253D, 1e254D, + 1e255D, 1e256D, 1e257D, 1e258D, 1e259D, 1e260D, 1e261D, 1e262D, 1e263D, 1e264D, + 1e265D, 1e266D, 1e267D, 1e268D, 1e269D, 1e270D, 1e271D, 1e272D, 1e273D, 1e274D, + 1e275D, 1e276D, 1e277D, 1e278D, 1e279D, 1e280D, 1e281D, 1e282D, 1e283D, 1e284D, + 1e285D, 1e286D, 1e287D, 1e288D, 1e289D, 1e290D, 1e291D, 1e292D, 1e293D, 1e294D, + 1e295D, 1e296D, 1e297D, 1e298D, 1e299D, 1e300D, 1e301D, 1e302D, 1e303D, 1e304D, + 1e305D, 1e306D, 1e307D, 1e308D + }; + + + public void appendFormatted(StringBuilder s, double d, int numFractDigits, + char decimalPoint, char thousandsSeparator, int numDigitsSeparated, + char negativePrefix, char negativeSuffix) + { + //First check for the special cases, +/-infinity, Not-a-number and -0.0 + if (d == double.NegativeInfinity) + { + //d == -Infinity + if (negativePrefix != '\uFFFF') + s.Append(negativePrefix); + s.Append("Infinity"); + if (negativeSuffix != '\uFFFF') + s.Append(negativeSuffix); + } + else if (d == double.PositiveInfinity) + //d == Infinity + s.Append("Infinity"); + else if (d != d) + //d == NaN + s.Append("NaN"); + else if (d == 0.0) + { + if ( (BitConverter.DoubleToInt64Bits(d) & DoubleSignMask) != 0) + { + //d == -0.0 + if (negativePrefix != '\uFFFF') + s.Append(negativePrefix); + s.Append('0').Append(decimalPoint).Append(ZEROS[numFractDigits]); + if (negativeSuffix != '\uFFFF') + s.Append(negativeSuffix); + } + else + //d == 0.0 + s.Append('0').Append(decimalPoint).Append(ZEROS[numFractDigits]); + } + else + { + //convert to a positive format, and record whether we have a negative + //number so that we know later whether to add the negativeSuffix + bool negative = false; + if (d < 0) + { + //Even if the number is negative, we only need to set the + //negative flag if there is a printable negativeSuffix + if (negativeSuffix != '\uFFFF') + negative = true; + if (negativePrefix != '\uFFFF') + s.Append(negativePrefix); + d = -d; + } + + //Find the magnitude. This is basically the exponent in normal form. + int mag = magnitude(d); + + //First off, if the number is too small for the given format, we + //only print 0.0..., which makes this real quick + if ( (mag + numFractDigits) < 0) + { + appendNearlyZeroNumber(s, d, mag, numFractDigits, decimalPoint); + if (negative) + s.Append(negativeSuffix); + return; + } + + long l; + //Now scale the double to the biggest long value we need + //We need to handle the smallest magnitudes differently because of rounding errors + + //This test is unlikely to ever be true. It would require numFractDigits + //to be 305 or more, which is pretty unlikely. + if (mag < -305) + l = (long) ((d*1E18) / d_tenthPowers[mag + 324]); + else + l = (long) (d / d_tenthPowers[mag + 323 - 17]); + + //And round up if necessary. Add one to the numFractDigits digit if the + //numFractDigits+1 digit is 5 or greater. It is useful to know that + //given a long, l, the nth digit is obtained using the formula + // nthDigit = (l/(tenthPower(l)/l_tenthPowers[n-1]))%10; + + long l_tenthPower = tenthPower(l); + //The numFractDigits+1 digit of the double is the + //numFractDigits+1+magnitude digit of the long. + //We only need worry about digits within the long. Very large numbers are + //not rounded because all the digits after the decimal points are 0 anyway + if (numFractDigits+mag+1 < l_tenthPowers.Length) + { + long digit = (l/(l_tenthPower/l_tenthPowers[numFractDigits+mag+1]))%10; + if (digit >= 5) + { + l += l_tenthPower/l_tenthPowers[numFractDigits+mag]; + } + } + + //And now we just print out our long, with the decimal point character + //inserted in the right place, using as many places as we wanted. + appendAsDouble(s, l, l_tenthPower, mag, numFractDigits, decimalPoint, thousandsSeparator, + numDigitsSeparated, negativePrefix, negativeSuffix); + + //Finally, append the negativeSuffix if necessary + if (negative) + s.Append(negativeSuffix); + } + } + + public void appendAsDouble(StringBuilder s, long l, long l_mag, int d_magnitude, + int numFractDigits, char decimalPoint, char thousandsSeparator, + int numDigitsSeparated, char negativePrefix, char negativeSuffix) + { + //If the magnitude is negative, we have a 0.xxx number + if (d_magnitude < 0) + { + s.Append('0').Append(decimalPoint).Append(ZEROS[-d_magnitude-1]); + //And just print successive digits until we have reached numFractDigits + //First decrement numFractDigits by the number of digits already printed + numFractDigits += d_magnitude; + + //get the magnitude of l + long c; + while(numFractDigits-- >= 0) + { + //Get the leading character (e.g. '62345/10000 = 6' using integer-divide) + c = l/l_mag; + //Append the digit character for this digit (e.g. number is 6, so character is '6') + s.Append(charForDigit[(int) c]); + //Multiply by the leading digit by the magnitude so that we can eliminate the leading digit + //(e.g. 6 * 10000 = 60000) + c *= l_mag; + //and eliminate the leading digit (e.g. 62345-60000 = 2345) + if ( c <= l) + l -= c; + //Decrease the magnitude by 10, and repeat the loop. + l_mag = l_mag/10; + } + } + else + { + //Just keep printing until magnitude is 0 + long c; + while(d_magnitude-- >= 0) + { + if (l_mag == 0) {s.Append('0');continue;} + //Get the leading character (e.g. '62345/10000 = 6' using integer-divide) + c = l/l_mag; + //Append the digit character for this digit (e.g. number is 6, so character is '6') + s.Append(charForDigit[(int) c]); + + //Don't forget about the thousands separator + if (d_magnitude % numDigitsSeparated == (numDigitsSeparated-1)) + s.Append(thousandsSeparator); + + //Multiply by the leading digit by the magnitude so that we can eliminate the leading digit + //(e.g. 6 * 10000 = 60000) + c *= l_mag; + //and eliminate the leading digit (e.g. 62345-60000 = 2345) + if ( c <= l) + l -= c; + //Decrease the magnitude by 10, and repeat the loop. + l_mag = l_mag/10; + } + s.Append(decimalPoint); + if (l_mag == 0) + s.Append(ZEROS[numFractDigits]); + else + { + while(numFractDigits-- > 0) + { + if (l_mag == 0) {s.Append('0');continue;} + //Get the leading character (e.g. '62345/10000 = 6' using integer-divide) + c = l/l_mag; + //Append the digit character for this digit (e.g. number is 6, so character is '6') + s.Append(charForDigit[(int) c]); + //Multiply by the leading digit by the magnitude so that we can eliminate the leading digit + //(e.g. 6 * 10000 = 60000) + c *= l_mag; + //and eliminate the leading digit (e.g. 62345-60000 = 2345) + if ( c <= l) + l -= c; + //Decrease the magnitude by 10, and repeat the loop. + l_mag = l_mag/10; + } + } + } + } + + private void appendNearlyZeroNumber(StringBuilder s, double d, int d_magnitude, + int numFractDigits, char decimalPoint) + { + if (d_magnitude + numFractDigits == -1) + { + //Possibly too small, depends on whether the top digit is 5 or greater + //So we have to scale to get the leading digit + int i; + if (d_magnitude < -305) + //Probably not necessary. Who is going to print 305 places? + i = (int) ((d*1E19) / d_tenthPowers[d_magnitude + 324 + 18]); + else + i = (int) (d / d_tenthPowers[d_magnitude + 323]); + + if (i >= 5) + { + //Not too small, we get to round up + s.Append('0').Append(decimalPoint).Append(ZEROS[numFractDigits-1]); + s.Append('1'); + } + else + { + //Definitely too small. Just print zeros + s.Append('0').Append(decimalPoint).Append(ZEROS[numFractDigits]); + } + } + else + { + //Definitely too small + s.Append('0').Append(decimalPoint).Append(ZEROS[numFractDigits]); + } + } + + /** + * Assumes i is positive. Returns the magnitude of i in base 10. + */ + private static long tenthPower(long i) + { + if (i < 10L) return 1; + else if (i < 100L) return 10L; + else if (i < 1000L) return 100L; + else if (i < 10000L) return 1000L; + else if (i < 100000L) return 10000L; + else if (i < 1000000L) return 100000L; + else if (i < 10000000L) return 1000000L; + else if (i < 100000000L) return 10000000L; + else if (i < 1000000000L) return 100000000L; + else if (i < 10000000000L) return 1000000000L; + else if (i < 100000000000L) return 10000000000L; + else if (i < 1000000000000L) return 100000000000L; + else if (i < 10000000000000L) return 1000000000000L; + else if (i < 100000000000000L) return 10000000000000L; + else if (i < 1000000000000000L) return 100000000000000L; + else if (i < 10000000000000000L) return 1000000000000000L; + else if (i < 100000000000000000L) return 10000000000000000L; + else if (i < 1000000000000000000L) return 100000000000000000L; + else return 1000000000000000000L; + } + + private static int magnitude(double d) + { + //It works. What else can I say. + long doubleToLongBits = BitConverter.DoubleToInt64Bits(d); + int magnitude = + (int) ((((doubleToLongBits & DoubleExpMask) >> DoubleExpShift) - DoubleExpBias) * 0.301029995663981); + + if (magnitude < -323) + magnitude = -323; + else if (magnitude > 308) + magnitude = 308; + + if (d >= d_tenthPowers[magnitude+323]) + { + while(magnitude < 309 && d >= d_tenthPowers[magnitude+323]) + magnitude++; + magnitude--; + return magnitude; + } + else + { + while(magnitude > -324 && d < d_tenthPowers[magnitude+323]) + magnitude--; + return magnitude; + } + } + + private static long[] l_tenthPowers = { + 1, + 10L, + 100L, + 1000L, + 10000L, + 100000L, + 1000000L, + 10000000L, + 100000000L, + 1000000000L, + 10000000000L, + 100000000000L, + 1000000000000L, + 10000000000000L, + 100000000000000L, + 1000000000000000L, + 10000000000000000L, + 100000000000000000L, + 1000000000000000000L, + }; + + public static void append(StringBuilder s, double d) + { + if (d == double.NegativeInfinity) + s.Append("-Infinity"); + else if (d == double.PositiveInfinity) + s.Append("Infinity"); + else if (d != d) + s.Append("NaN"); + else if (d == 0.0) + { + if ( (BitConverter.DoubleToInt64Bits(d) & DoubleSignMask) != 0) + s.Append('-'); + s.Append("0.0"); + } + else + { + if (d < 0) + { + s.Append('-'); + d = -d; + } + + if (d >= 0.001 && d < 0.01) + { + long i = (long) (d * 1E20); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + s.Append("0.00"); + appendFractDigits(s, i,-1); + } + else if (d >= 0.01 && d < 0.1) + { + long i = (long) (d * 1E19); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + s.Append("0.0"); + appendFractDigits(s, i,-1); + } + else if (d >= 0.1 && d < 1) + { + long i = (long) (d * 1E18); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + s.Append("0."); + appendFractDigits(s, i,-1); + } + else if (d >= 1 && d < 10) + { + long i = (long) (d * 1E17); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + appendFractDigits(s, i,1); + } + else if (d >= 10 && d < 100) + { + long i = (long) (d * 1E16); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + appendFractDigits(s, i,2); + } + else if (d >= 100 && d < 1000) + { + long i = (long) (d * 1E15); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + appendFractDigits(s, i,3); + } + else if (d >= 1000 && d < 10000) + { + long i = (long) (d * 1E14); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + appendFractDigits(s, i,4); + } + else if (d >= 10000 && d < 100000) + { + long i = (long) (d * 1E13); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + appendFractDigits(s, i,5); + } + else if (d >= 100000 && d < 1000000) + { + long i = (long) (d * 1E12); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + appendFractDigits(s, i,6); + } + else if (d >= 1000000 && d < 10000000) + { + long i = (long) (d * 1E11); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + appendFractDigits(s, i,7); + } + else + { + int mag = magnitude(d); + long i; + if (mag < -305) + i = (long) (d*1E18 / d_tenthPowers[mag + 324]); + else + i = (long) (d / d_tenthPowers[mag + 323 - 17]); + i = i%100 >= 50 ? (i/100) + 1 : i/100; + appendFractDigits(s, i, 1); + s.Append('E'); + append(s,mag); + } + } + } + + private static void appendFractDigits(StringBuilder s, long i, int decimalOffset) + { + long mag = tenthPower(i); + long c; + while ( i > 0 ) + { + c = i/mag; + s.Append(charForDigit[(int) c]); + decimalOffset--; + if (decimalOffset == 0) + s.Append('.'); + c *= mag; + if ( c <= i) + i -= c; + mag = mag/10; + } + if (i != 0) + s.Append(charForDigit[(int) i]); + else if (decimalOffset > 0) + { + s.Append(ZEROS[decimalOffset]); + decimalOffset = 1; + } + + decimalOffset--; + if (decimalOffset == 0) + s.Append(".0"); + else if (decimalOffset == -1) + s.Append('0'); + } +} diff --git a/IK.VM.NET/ExceptionHelper.cs b/IK.VM.NET/ExceptionHelper.cs new file mode 100644 index 00000000..125ead50 --- /dev/null +++ b/IK.VM.NET/ExceptionHelper.cs @@ -0,0 +1,659 @@ +/* + 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 + +*/ +using System; +using System.Reflection; +using System.Diagnostics; +using System.Text; +using System.Collections; +using java.lang; +using ClassHelper = NativeCode.java.lang.Class; + +public class ExceptionHelper +{ + // the contents of the NULL_STRING should be empty (because when the exception propagates to other .NET code + // it will return that text as the Message property), but it *must* be a copy, because we need to be + // able to distinguish it from a user specified blank string + private static readonly string NULL_STRING = string.Copy(""); + + private class ExceptionInfoHelper + { + private static readonly Exception CAUSE_NOT_SET = new Exception(); + private ArrayList stackTrace = new ArrayList(); + private Exception cause; + + [StackTraceInfo(Hidden = true)] + internal ExceptionInfoHelper(Exception x) + { + Append(new StackTrace(x, true)); + bool chopFirst = stackTrace.Count != 0; + Append(new StackTrace(true)); + if(chopFirst && stackTrace.Count > 0 && JVM.CleanStackTraces) + { + stackTrace.RemoveAt(0); + } + cause = x.InnerException; + if(cause == null) + { + cause = CAUSE_NOT_SET; + } + } + + internal Exception Cause + { + get + { + return cause == CAUSE_NOT_SET ? null : cause; + } + set + { + if(cause == CAUSE_NOT_SET) + { + cause = value; + } + else + { + throw JavaException.IllegalStateException("Throwable cause already initialized"); + } + } + } + + internal void ResetStackTrace() + { + stackTrace.Clear(); + Append(new StackTrace(true)); + } + + private static bool IsPrivateScope(MethodBase mb) + { + // HACK shouldn't there be a better way to determine whether a method is privatescope? + return !mb.IsPrivate && !mb.IsFamily && !mb.IsFamilyAndAssembly && !mb.IsFamilyOrAssembly && !mb.IsPublic; + } + + private void Append(StackTrace st) + { + if(st.FrameCount > 0) + { + int baseSize = stackTrace.Count; + for(int i = 0; i < st.FrameCount; i++) + { + StackFrame frame = st.GetFrame(i); + MethodBase m = frame.GetMethod(); + // TODO I may need more safety checks like these + if(m.DeclaringType == null || m.ReflectedType == null) + { + continue; + } + if(m.DeclaringType == typeof(System.Runtime.CompilerServices.RuntimeHelpers) + || m.DeclaringType == typeof(JNI) // HACK we exclude the JNI class from the stack trace + || m.DeclaringType.IsSubclassOf(typeof(System.Reflection.MethodInfo)) + || IsPrivateScope(m)) // NOTE we assume that privatescope methods are always stubs that we should exclude + { + if(JVM.CleanStackTraces) + { + continue; + } + } + string methodName = frame.GetMethod().Name; + if(methodName == ".ctor") + { + methodName = "<init>"; + } + else if(methodName == ".cctor") + { + methodName = "<clinit>"; + } + int lineNumber = frame.GetFileLineNumber(); + if(lineNumber == 0) + { + lineNumber = -1; + } + string fileName = frame.GetFileName(); + if(fileName != null) + { + fileName = new System.IO.FileInfo(fileName).Name; + } + string className = ClassHelper.getName(frame.GetMethod().ReflectedType); + bool native = false; + if(m.IsDefined(typeof(ModifiersAttribute), false)) + { + object[] methodFlagAttribs = m.GetCustomAttributes(typeof(ModifiersAttribute), false); + if(methodFlagAttribs.Length == 1) + { + ModifiersAttribute modifiersAttrib = (ModifiersAttribute)methodFlagAttribs[0]; + if(modifiersAttrib.IsSynthetic) + { + continue; + } + if((modifiersAttrib.Modifiers & Modifiers.Native) != 0) + { + native = true; + } + } + } + if(JVM.CleanStackTraces) + { + object[] attribs = m.DeclaringType.GetCustomAttributes(typeof(StackTraceInfoAttribute), false); + if(attribs.Length == 1) + { + StackTraceInfoAttribute sta = (StackTraceInfoAttribute)attribs[0]; + if(sta.EatFrames > 0) + { + stackTrace.RemoveRange(stackTrace.Count - sta.EatFrames, sta.EatFrames); + } + if(sta.Hidden) + { + continue; + } + if(sta.Truncate) + { + stackTrace.RemoveRange(baseSize, stackTrace.Count - baseSize); + continue; + } + if(sta.Class != null) + { + className = sta.Class; + } + } + attribs = m.GetCustomAttributes(typeof(StackTraceInfoAttribute), false); + if(attribs.Length == 1) + { + StackTraceInfoAttribute sta = (StackTraceInfoAttribute)attribs[0]; + if(sta.EatFrames > 0) + { + int eat = Math.Min(stackTrace.Count, sta.EatFrames); + stackTrace.RemoveRange(stackTrace.Count - eat, eat); + } + if(sta.Hidden) + { + continue; + } + if(sta.Truncate) + { + stackTrace.RemoveRange(baseSize, stackTrace.Count - baseSize); + continue; + } + if(sta.Class != null) + { + className = sta.Class; + } + } + } + stackTrace.Add(new StackTraceElement(fileName, lineNumber, className, methodName, native)); + } + if(JVM.CleanStackTraces) + { + int chop = 0; + for(int i = stackTrace.Count - 1; i >= 0; i--) + { + StackTraceElement ste = (StackTraceElement)stackTrace[i]; + if(ste.getClassName() == "System.Reflection.RuntimeMethodInfo") + { + // skip method invocation by reflection, if it is at the top of the stack + chop++; + } + else + { + break; + } + } + stackTrace.RemoveRange(stackTrace.Count - chop, chop); + } + } + } + + internal StackTraceElement[] StackTrace + { + get + { + return (StackTraceElement[])stackTrace.ToArray(typeof(StackTraceElement)); + } + set + { + stackTrace = new ArrayList(value); + } + } + } + + // TODO this should be an "identity" hashtable instead of "equality" + private static WeakHashtable exceptions = new WeakHashtable(); + + public static void printStackTrace(Exception x) + { + if(x == null) + { + throw new NullReferenceException(); + } + Type type = ClassLoaderWrapper.GetType("java.lang.System"); + object err = type.GetProperty("err").GetValue(null, null); + printStackTrace(x, err); + } + + public static void printStackTrace(Exception x, object printStreamOrWriter) + { + if(x == null) + { + throw new NullReferenceException(); + } + StringBuilder sb = new StringBuilder(); + sb.Append(toString_Virtual(x)).Append(Environment.NewLine); + StackTraceElement[] stack = getStackTrace_Virtual(x); + for(int i = 0; i < stack .Length; i++) + { + sb.Append("\tat ").Append(stack[i]).Append(Environment.NewLine); + } + Exception cause = getCause_Virtual(x); + while(cause != null) + { + sb.Append("Caused by: ").Append(toString_Virtual(cause)).Append(Environment.NewLine); + + // Cause stacktrace + StackTraceElement[] parentStack = stack; + stack = getStackTrace_Virtual(cause); + bool equal = false; // Is rest of stack equal to parent frame? + for(int i = 0; i < stack.Length && !equal; i++) + { + // Check if we already printed the rest of the stack + // since it was the tail of the parent stack + int remaining = stack.Length - i; + int element = i; + int parentElement = parentStack.Length - remaining; + equal = parentElement >= 0 + && parentElement < parentStack.Length; // be optimistic + while(equal && element < stack.Length) + { + if(stack[element].Equals(parentStack[parentElement])) + { + element++; + parentElement++; + } + else + { + equal = false; + } + } + // Print stacktrace element or indicate the rest is equal + if(!equal) + { + sb.Append("\tat ").Append(stack[i]).Append(Environment.NewLine); + } + else + { + sb.Append("\t... ").Append(remaining).Append(" more").Append(Environment.NewLine); + break; // from stack printing for loop + } + } + cause = getCause_Virtual(cause); + } + // NOTE since we use reflection to lookup the print method each time, we can use this one method for both + // the printStackTrace(..., PrintStream) & printStackTrace(..., PrintWriter) versions + MethodInfo write = printStreamOrWriter.GetType().GetMethod("print", BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Standard, new Type[] { typeof(string) }, null); + write.Invoke(printStreamOrWriter, new object[] { sb.ToString() }); + } + + public static Exception initCause(Exception x, Exception cause) + { + if(x == null) + { + throw new NullReferenceException(); + } + if(cause == x) + { + throw JavaException.IllegalArgumentException("Cause cannot be self"); + } + ExceptionInfoHelper eih = (ExceptionInfoHelper)exceptions[x]; + if(eih == null) + { + eih = new ExceptionInfoHelper(x); + exceptions[x] = eih; + } + eih.Cause = cause; + return x; + } + + public static Exception getCause(Exception x) + { + if(x == null) + { + throw new NullReferenceException(); + } + ExceptionInfoHelper eih = (ExceptionInfoHelper)exceptions[x]; + if(eih == null) + { + return x.InnerException; + } + return eih.Cause; + } + + public static StackTraceElement[] getStackTrace(Exception x) + { + if(x == null) + { + throw new NullReferenceException(); + } + ExceptionInfoHelper ei = (ExceptionInfoHelper)exceptions[x]; + if(ei == null) + { + return new StackTraceElement[0]; + } + return ei.StackTrace; + } + + public static void setStackTrace(Exception x, StackTraceElement[] stackTrace) + { + if(x == null) + { + throw new NullReferenceException(); + } + for(int i = 0; i < stackTrace.Length; i++) + { + if(stackTrace[i] == null) + { + throw new NullReferenceException(); + } + } + ExceptionInfoHelper ei = (ExceptionInfoHelper)exceptions[x]; + if(ei == null) + { + ei = new ExceptionInfoHelper(x); + exceptions[x] = ei; + } + ei.StackTrace = stackTrace; + } + + public static string NullString + { + get + { + return NULL_STRING; + } + } + + public static string FilterMessage(string message) + { + if(message == null) + { + message = NULL_STRING; + } + return message; + } + + public static string GetMessageFromCause(Exception cause) + { + if(cause == null) + { + return NULL_STRING; + } + return toString_Virtual(cause); + } + + public static string getMessage(Exception x) + { + if(x == null) + { + throw new NullReferenceException(); + } + string message = x.Message; + if(message == NULL_STRING) + { + message = null; + } + return message; + } + + public static string getLocalizedMessage(Exception x) + { + if(x == null) + { + throw new NullReferenceException(); + } + return getMessage_Virtual(x); + } + + [StackTraceInfo(Hidden = true)] + public static Exception fillInStackTrace(Exception x) + { + if(x == null) + { + throw new NullReferenceException(); + } + ExceptionInfoHelper eih = (ExceptionInfoHelper)exceptions[x]; + if(eih == null) + { + eih = new ExceptionInfoHelper(x); + exceptions[x] = eih; + } + else + { + eih.ResetStackTrace(); + } + return x; + } + + [StackTraceInfo(Hidden = true)] + public static Exception MapExceptionFast(Exception t) + { + if(exceptions.ContainsKey(t)) + { + return t; + } + return MapException(t, typeof(Exception)); + } + + [StackTraceInfo(Truncate = true)] + public static Exception MapException(Exception t, Type handler) + { + //Console.WriteLine("MapException: {0}, {1}", t, handler); + //Console.WriteLine(new StackTrace(t)); + Exception org = t; + Type type = t.GetType(); + // TODO don't remap if the exception already has associated ExceptionInfoHelper object (this means + // that the .NET exception was thrown from Java code, explicitly). + if(type == typeof(NullReferenceException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.NullPointerException")); + } + else if(type == typeof(IndexOutOfRangeException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.ArrayIndexOutOfBoundsException")); + } + else if(type == typeof(InvalidCastException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.ClassCastException")); + } + else if(type == typeof(TypeInitializationException)) + { + t = (Exception)MapExceptionFast(t.InnerException); + if(!ClassLoaderWrapper.GetType("java.lang.Error").IsInstanceOfType(t)) + { + ConstructorInfo constructor = ClassLoaderWrapper.GetType("java.lang.ExceptionInInitializerError").GetConstructor(new Type[] { typeof(Exception) }); + t = (Exception)constructor.Invoke(new object[] { t }); + } + } + else if(type == typeof(System.Threading.SynchronizationLockException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.IllegalMonitorStateException")); + } + else if(type == typeof(System.Threading.ThreadInterruptedException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.InterruptedException")); + } + else if(type == typeof(OutOfMemoryException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.OutOfMemoryError")); + } + else if(type == typeof(DivideByZeroException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.ArithmeticException"), new object[] { "/ by zero" }); + } + else if(type == typeof(ArrayTypeMismatchException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.ArrayStoreException")); + } + else if(type == typeof(StackOverflowException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.StackOverflowError")); + } + else if(type == typeof(System.Security.VerificationException)) + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.VerifyError")); + } + else if(type == typeof(System.Threading.ThreadAbortException)) + { + System.Threading.ThreadAbortException abort = (System.Threading.ThreadAbortException)t; + if(abort.ExceptionState is Exception) + { + t = (Exception)abort.ExceptionState; + } + else + { + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.ThreadDeath")); + } + System.Threading.Thread.ResetAbort(); + } + else if(type == typeof(OverflowException)) + { + // TODO make sure the originating method was from an IK.VM.NET generated assembly, because if it was + // generated by non-Java code, this remapping is obviously bogus. + t = (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.NegativeArraySizeException")); + } + else if(type.FullName.StartsWith("System.") && type != typeof(TargetInvocationException)) + { + // TODO this is just for debugging + Console.WriteLine("caught: {0}, handler: {1}", t.GetType().FullName, handler.FullName); + Console.WriteLine(t); + } + if(!exceptions.ContainsKey(t)) + { + exceptions.Add(t, new ExceptionInfoHelper(org)); + Exception inner = org.InnerException; + if(inner != null && !exceptions.ContainsKey(inner)) + { + exceptions.Add(inner, new ExceptionInfoHelper(inner)); + } + } + return handler.IsInstanceOfType(t) ? t : null; + } + + public static string toString(Exception x) + { + if(x == null) + { + throw new NullReferenceException(); + } + string message = getLocalizedMessage_Virtual(x); + if(message == null) + { + return ClassHelper.getName(x.GetType()); + } + return ClassHelper.getName(x.GetType()) + ": " + message; + } + + // below are some helper properties to support calling virtual methods on Throwable + + private delegate string toString_Delegate(Exception x); + private static toString_Delegate toString_Virtual_; + + private static toString_Delegate toString_Virtual + { + get + { + if(toString_Virtual_ == null) + { + MethodInfo method = ClassLoaderWrapper.GetType("java.lang.Throwable$VirtualMethodsHelper").GetMethod("toString"); + toString_Virtual_ = (toString_Delegate)Delegate.CreateDelegate(typeof(toString_Delegate), method); + } + return toString_Virtual_; + } + } + + private delegate string getMessage_Delegate(Exception x); + private static getMessage_Delegate getMessage_Virtual_; + + private static getMessage_Delegate getMessage_Virtual + { + get + { + if(getMessage_Virtual_ == null) + { + MethodInfo method = ClassLoaderWrapper.GetType("java.lang.Throwable$VirtualMethodsHelper").GetMethod("getMessage"); + getMessage_Virtual_ = (getMessage_Delegate)Delegate.CreateDelegate(typeof(getMessage_Delegate), method); + } + return getMessage_Virtual_; + } + } + + private delegate StackTraceElement[] getStackTrace_Delegate(Exception x); + private static getStackTrace_Delegate getStackTrace_Virtual_; + + private static getStackTrace_Delegate getStackTrace_Virtual + { + get + { + if(getStackTrace_Virtual_ == null) + { + MethodInfo method = ClassLoaderWrapper.GetType("java.lang.Throwable$VirtualMethodsHelper").GetMethod("getStackTrace"); + getStackTrace_Virtual_ = (getStackTrace_Delegate)Delegate.CreateDelegate(typeof(getStackTrace_Delegate), method); + } + return getStackTrace_Virtual_; + } + } + + private delegate Exception getCause_Delegate(Exception x); + private static getCause_Delegate getCause_Virtual_; + + private static getCause_Delegate getCause_Virtual + { + get + { + if(getCause_Virtual_ == null) + { + MethodInfo method = ClassLoaderWrapper.GetType("java.lang.Throwable$VirtualMethodsHelper").GetMethod("getCause"); + getCause_Virtual_ = (getCause_Delegate)Delegate.CreateDelegate(typeof(getCause_Delegate), method); + } + return getCause_Virtual_; + } + } + + private delegate string getLocalizedMessage_Delegate(Exception x); + private static getLocalizedMessage_Delegate getLocalizedMessage_Virtual_; + + private static getLocalizedMessage_Delegate getLocalizedMessage_Virtual + { + get + { + if(getLocalizedMessage_Virtual_ == null) + { + MethodInfo method = ClassLoaderWrapper.GetType("java.lang.Throwable$VirtualMethodsHelper").GetMethod("getLocalizedMessage"); + getLocalizedMessage_Virtual_ = (getLocalizedMessage_Delegate)Delegate.CreateDelegate(typeof(getLocalizedMessage_Delegate), method); + } + return getLocalizedMessage_Virtual_; + } + } + + [StackTraceInfo(Hidden = true)] + public static void ThrowHack(Exception x) + { + throw x; + } +} diff --git a/IK.VM.NET/IK.VM.NET.csproj b/IK.VM.NET/IK.VM.NET.csproj new file mode 100644 index 00000000..a39ee5a0 --- /dev/null +++ b/IK.VM.NET/IK.VM.NET.csproj @@ -0,0 +1,192 @@ +<VisualStudioProject> + <CSHARP + ProjectType = "Local" + ProductVersion = "7.0.9466" + SchemaVersion = "1.0" + ProjectGuid = "{F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}" + > + <Build> + <Settings + ApplicationIcon = "" + AssemblyKeyContainerName = "" + AssemblyName = "IK.VM.NET" + AssemblyOriginatorKeyFile = "" + DefaultClientScript = "JScript" + DefaultHTMLPageLayout = "Grid" + DefaultTargetSchema = "IE50" + DelaySign = "false" + OutputType = "Library" + RootNamespace = "" + StartupObject = "" + > + <Config + Name = "Debug" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "DEBUG;TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "true" + Optimize = "false" + OutputPath = "bin\Debug\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + <Config + Name = "Release" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "false" + Optimize = "true" + OutputPath = "bin\Release\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + </Settings> + <References> + <Reference + Name = "System" + AssemblyName = "System" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.dll" + /> + <Reference + Name = "System.Data" + AssemblyName = "System.Data" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Data.dll" + /> + <Reference + Name = "System.XML" + AssemblyName = "System.Xml" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll" + /> + <Reference + Name = "IK.VM.JNI" + Project = "{4D400F9D-68A1-4066-95F6-85AF0E58B710}" + Package = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}" + /> + </References> + </Build> + <Files> + <Include> + <File + RelPath = "AssemblyInfo.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "attributes.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "BigEndianBinaryReader.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ByteCode.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ByteCodeHelper.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ClassFile.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ClassLoaderWrapper.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "classpath.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "compiler.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "DoubleToString.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ExceptionHelper.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "JavaException.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "map.xml" + BuildAction = "EmbeddedResource" + /> + <File + RelPath = "ObjectHelper.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "profiler.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "remapper.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "StringHelper.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "TypeWrapper.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "verifier.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "vm.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "WeakHashtable.cs" + SubType = "Code" + BuildAction = "Compile" + /> + </Include> + </Files> + </CSHARP> +</VisualStudioProject> + diff --git a/IK.VM.NET/JavaException.cs b/IK.VM.NET/JavaException.cs new file mode 100644 index 00000000..7e7cdb26 --- /dev/null +++ b/IK.VM.NET/JavaException.cs @@ -0,0 +1,148 @@ +/* + 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 + +*/ +using System; +using System.Reflection; + +sealed class JavaException +{ + private JavaException() {} + + internal static Exception ClassFormatError(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.ClassFormatError").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception UnsupportedClassVersionError(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.UnsupportedClassVersionError").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception IllegalAccessError(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.IllegalAccessError").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception VerifyError(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.VerifyError").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception IncompatibleClassChangeError(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.IncompatibleClassChangeError").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + [ThreadStatic] + private static bool classNotFound; + + private class BootstrapClassMissing : Exception {} + + internal static Exception ClassNotFoundException(string s, params object[] args) + { + // HACK if java.lang.ClassNotFoundException is not found, this method would recurse until the + // stack overflows, so in order to prevent that, we use this hack + if(JVM.IsStaticCompiler && classNotFound) + { + throw new BootstrapClassMissing(); + } + try + { + classNotFound = true; + //Console.WriteLine("ClassNotFoundException: " + s); + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.ClassNotFoundException").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + catch(BootstrapClassMissing) + { + throw new TypeLoadException("ClassNotFoundException: " + s); + } + finally + { + classNotFound = false; + } + } + + internal static Exception NoClassDefFoundError(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.NoClassDefFoundError").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception UnsatisfiedLinkError(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.UnsatisfiedLinkError").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception IllegalStateException(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.IllegalStateException").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception IllegalArgumentException(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.lang.IllegalArgumentException").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception NegativeArraySizeException() + { + return (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.NegativeArraySizeException")); + } + + internal static Exception InvocationTargetException(Exception x) + { + return (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.reflect.InvocationTargetException"), new object[] { x }); + } + + internal static Exception IOException(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.io.IOException").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } + + internal static Exception UnknownHostException(string s, params object[] args) + { + s = String.Format(s, args); + ConstructorInfo ci = ClassLoaderWrapper.GetType("java.net.UnknownHostException").GetConstructor(new Type[] { typeof(string) }); + return (Exception)ci.Invoke(new object[] { s }); + } +} diff --git a/IK.VM.NET/ObjectHelper.cs b/IK.VM.NET/ObjectHelper.cs new file mode 100644 index 00000000..adf9da1e --- /dev/null +++ b/IK.VM.NET/ObjectHelper.cs @@ -0,0 +1,133 @@ +/* + 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 + +*/ +using System; + +public class ObjectHelper +{ + public static void notify(object o) + { + if(o == null) + { + throw new NullReferenceException(); + } + System.Threading.Monitor.Pulse(o); + } + + public static void notifyAll(object o) + { + if(o == null) + { + throw new NullReferenceException(); + } + System.Threading.Monitor.PulseAll(o); + } + + public static void wait(object o) + { + if(o == null) + { + throw new NullReferenceException(); + } + System.Threading.Monitor.Wait(o); + } + + public static void wait(object o, long timeout) + { + wait(o, timeout, 0); + } + + public static void wait(object o, long timeout, int nanos) + { + if(o == null) + { + throw new NullReferenceException(); + } + if(timeout == 0 && nanos == 0) + { + System.Threading.Monitor.Wait(o); + } + else + { + System.Threading.Monitor.Wait(o, new TimeSpan(timeout * 10000 + (nanos + 99) / 100)); + } + } + + public static void clonecheck(object o) + { + if(!(o is java.lang.Cloneable)) + { + throw (Exception)Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.CloneNotSupportedException")); + } + } + + public static object virtualclone(object o) + { + // TODO because Object.clone() is protected it is accessible from other classes in the java.lang package, + // so when they clone an array, we end up here (instead of being redirected to System.Array.Clone(), which + // the compiler normally does because Object.clone() is inaccessible) + if(o is Array) + { + return ((Array)o).Clone(); + } + clonecheck(o); + // TODO this doesn't happen very often, the only sensible pattern that I can think of that produces code + // that ends up here is as follows: + // class Base { + // public Base CloneMe() { return (Base)clone(); } + // } + // case Derived extends Base { + // protected object clone() { ... } + // } + // One way of implementing this is by calling the clone method thru reflection, not very fast, but + // since this is an uncommon scenario, we might be able to get away with it + throw new NotImplementedException("virtual clone invocation not implemented"); + } + + public static string toStringVirtual(object o) + { + if(o is Array) + { + return toStringSpecial(o); + } + try + { + return o.ToString(); + } + catch(NullReferenceException) + { + return o.GetType().FullName; + } + } + + public static string toStringSpecial(object o) + { + // TODO hex string should be formatted differently + return NativeCode.java.lang.Class.getName(o.GetType()) + "@" + o.GetHashCode().ToString("X"); + } + + public static object getClass(object o) + { + return NativeCode.java.lang.Class.getClassFromType(o.GetType()); + } +} diff --git a/IK.VM.NET/StringHelper.cs b/IK.VM.NET/StringHelper.cs new file mode 100644 index 00000000..102c665a --- /dev/null +++ b/IK.VM.NET/StringHelper.cs @@ -0,0 +1,369 @@ +/* + 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 + +*/ +using System; +using System.Text; +using System.Reflection; + +public class StringHelper +{ + public static string NewString(char[] data, int offset, int count, bool dont_copy) + { + return new String(data, offset, count); + } + + public static string NewString(sbyte[] sdata) + { + return NewString(sdata, 0, sdata.Length); + } + + public static string NewString(sbyte[] sdata, int hibyte) + { + return NewString(sdata, hibyte, 0, sdata.Length); + } + + public static string NewString(sbyte[] sdata, int offset, int count) + { + // TODO what encoding should this use? + // TODO could use the unsafe constructor that takes sbyte*, but I don't know if that is worthwhile to be unsafe for + byte[] data = new byte[sdata.Length]; + for(int i = 0; i < data.Length; i++) + { + data[i] = (byte)sdata[i]; + } + return System.Text.Encoding.ASCII.GetString(data, offset, count); + } + + public static string NewString(sbyte[] sdata, int hibyte, int offset, int count) + { + // TODO benchmark this versus using a stringbuilder instead of a char[] + hibyte <<= 8; + char[] data = new char[count]; + for(int i = 0; i < count; i++) + { + // TODO what happens for negative bytes? + data[i] = (char)(((byte)sdata[i + offset]) | hibyte); + } + return new String(data); + } + + public static string NewString(sbyte[] sdata, string charsetName) + { + return NewString(sdata, 0, sdata.Length, charsetName); + } + + public static string NewString(sbyte[] sdata, int offset, int count, string charsetName) + { + // HACK special case for UTF8, I really need to implement this by + // redirecting to the classpath character encoding support + if(charsetName == "UTF8") + { + char[] ch = new Char[count]; + int l = 0; + for(int i = 0; i < count; i++) + { + int c = (byte)sdata[offset + i]; + int char2, char3; + switch (c >> 4) + { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + // 0xxxxxxx + break; + case 12: case 13: + // 110x xxxx 10xx xxxx + char2 = (byte)sdata[offset + ++i]; + c = (((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = (byte)sdata[offset + ++i]; + char3 = (byte)sdata[offset + ++i]; + c = (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); + break; + } + ch[l++] = (char)c; + } + return new String(ch, 0, l); + } + // TODO don't use reflection, but write a Java helper class and redirect this method there + Type t = ClassLoaderWrapper.GetType("gnu.java.io.EncodingManager"); + object decoder = t.InvokeMember("getDecoder", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new object[] { charsetName }); + return new String((char[])decoder.GetType().InvokeMember("convertToChars", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, decoder, new object[] { sdata, offset, count })); + } + + public static string valueOf(bool b) + { + return b ? "true" : "false"; + } + + public static string valueOf(int i) + { + return i.ToString(); + } + + public static string valueOf(long l) + { + return l.ToString(); + } + + public static string valueOf(char c) + { + return c.ToString(); + } + + public static string valueOf(float f) + { + StringBuilder sb = new StringBuilder(); + return StringBufferHelper.append(sb, f).ToString(); + } + + public static string valueOf(double d) + { + StringBuilder sb = new StringBuilder(); + return StringBufferHelper.append(sb, d).ToString(); + } + + public static string valueOf(object o) + { + if(o == null) + { + return "null"; + } + return ObjectHelper.toStringVirtual(o); + } + + public static string substring(string s, int off, int end) + { + return s.Substring(off, end - off); + } + + public static bool startsWith(string s, string prefix, int toffset) + { + // TODO + throw new NotImplementedException(); + } + + public static void getChars(string s, int srcBegin, int srcEnd, char[] dst, int dstBegin) + { + s.CopyTo(srcBegin, dst, dstBegin, srcEnd - srcBegin); + } + + public static int GetCountField(string s) + { + return s.Length; + } + + public static char[] GetValueField(string s) + { + return s.ToCharArray(); + } + + public static int GetOffsetField(string s) + { + return 0; + } + + public static bool equalsIgnoreCase(string s1, string s2) + { + return String.Compare(s1, s2, true) == 0; + } + + public static int compareToIgnoreCase(string s1, string s2) + { + return String.Compare(s1, s2, true); + } + + public static sbyte[] getBytes(string s) + { + byte[] data = System.Text.Encoding.ASCII.GetBytes(s); + sbyte[] sdata = new sbyte[data.Length]; + for(int i = 0; i < data.Length; i++) + { + sdata[i] = (sbyte)data[i]; + } + return sdata; + } + + public static sbyte[] getBytes(string s, string charsetName) + { + // TODO don't use reflection, but write a Java helper class and redirect this method there + char[] ch = s.ToCharArray(); + Type t = ClassLoaderWrapper.GetType("gnu.java.io.EncodingManager"); + object encoder = t.InvokeMember("getEncoder", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new object[] { charsetName }); + return (sbyte[])encoder.GetType().InvokeMember("convertToBytes", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, encoder, new object[] { ch, 0, ch.Length }); + } + + public static void getBytes(string s, int srcBegin, int srcEnd, sbyte[] dst, int dstBegin) + { + for(int i = 0; i < (srcEnd - srcBegin); i++) + { + dst[i + dstBegin] = (sbyte)s[i + srcBegin]; + } + } + + public static object subSequence(string s, int offset, int count) + { + // TODO + throw new NotImplementedException(); + } + + public static bool regionMatches(string s, int toffset, string other, int ooffset, int len) + { + return regionMatches(s, false, toffset, other, ooffset, len); + } + + public static bool regionMatches(string s, bool ignoreCase, int toffset, string other, int ooffset, int len) + { + if(toffset < 0 || ooffset < 0 || toffset + len > s.Length || ooffset + len > other.Length) + { + return false; + } + while(--len >= 0) + { + char c1 = s[toffset++]; + char c2 = other[ooffset++]; + if(c1 != c2 && (!ignoreCase || (Char.ToLower(c1) != Char.ToLower(c2) && (Char.ToUpper(c1) != Char.ToUpper(c2))))) + { + return false; + } + } + return true; + } + + // NOTE argument is of type object, because otherwise the code that calls this function + // has to be much more complex + public static int hashCode(object s) + { + int h = 0; + foreach(char c in (string)s) + { + h = h * 31 + c; + } + return h; + } + + public static string toUpperCase(string s, object locale) + { + // TODO + return s.ToUpper(); + } +} + +public class StringBufferHelper +{ + public static StringBuilder append(StringBuilder thiz, object o) + { + if(o == null) + { + o = "null"; + } + return thiz.Append(ObjectHelper.toStringVirtual(o)); + } + + public static StringBuilder append(StringBuilder thiz, string s) + { + if(s == null) + { + s = "null"; + } + return thiz.Append(s); + } + + public static StringBuilder append(StringBuilder thiz, bool b) + { + if(b) + { + return thiz.Append("true"); + } + else + { + return thiz.Append("false"); + } + } + + public static StringBuilder append(StringBuilder thiz, float f) + { + // TODO this is not correct, we need to use the Java algorithm of converting a float to string + if(float.IsNaN(f)) + { + thiz.Append("NaN"); + return thiz; + } + if(float.IsNegativeInfinity(f)) + { + thiz.Append("-Infinity"); + return thiz; + } + if(float.IsPositiveInfinity(f)) + { + thiz.Append("Infinity"); + return thiz; + } + // HACK really lame hack to apprioximate the Java behavior a little bit + string s = f.ToString(System.Globalization.CultureInfo.InvariantCulture); + thiz.Append(s); + if(s.IndexOf('.') == -1) + { + thiz.Append(".0"); + } + return thiz; + } + + public static StringBuilder append(StringBuilder thiz, double d) + { + DoubleToString.append(thiz, d); + return thiz; + } + + public static StringBuilder insert(StringBuilder thiz, int index, string s) + { + if(s == null) + { + s = "null"; + } + return thiz.Insert(index, s); + } + + public static string substring(StringBuilder thiz, int start, int end) + { + return thiz.ToString(start, end - start); + } + + public static string substring(StringBuilder thiz, int start) + { + return thiz.ToString(start, thiz.Length - start); + } + + public static StringBuilder replace(StringBuilder thiz, int start, int end, string str) + { + // OPTIMIZE this could be done a little more efficient + thiz.Remove(start, end - start); + thiz.Insert(start, str); + return thiz; + } + + public static StringBuilder delete(StringBuilder thiz, int start, int end) + { + return thiz.Remove(start, end - start); + } +} diff --git a/IK.VM.NET/TypeWrapper.cs b/IK.VM.NET/TypeWrapper.cs new file mode 100644 index 00000000..396e59f1 --- /dev/null +++ b/IK.VM.NET/TypeWrapper.cs @@ -0,0 +1,3253 @@ +/* + 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 + +*/ +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics; + +sealed class MethodDescriptor +{ + private ClassLoaderWrapper classLoader; + private string name; + private string sig; + private Type[] args; + private Type ret; + + internal MethodDescriptor(ClassLoaderWrapper classLoader, string name, string sig) + { + if(classLoader == null) + { + throw new ArgumentNullException(); + } + this.classLoader = classLoader; + this.name = name; + this.sig = sig; + // class name in the sig should be slashed instead of dotted + Debug.Assert(sig.IndexOf('.') < 0); + } + + internal string Name + { + get + { + return name; + } + } + + internal string Signature + { + get + { + return sig; + } + } + + internal Type[] ArgTypes + { + get + { + if(args == null) + { + args = classLoader.ArgTypeListFromSig(sig); + } + return args; + } + } + + internal Type RetType + { + get + { + if(ret == null) + { + ret = classLoader.RetTypeFromSig(sig); + } + return ret; + } + } + + public override bool Equals(object o) + { + // TODO instead of comparing the signature strings, we should compare the actual types + // (because, in the face of multiple class loaders, there can be multiple classes with the same name) + MethodDescriptor md = o as MethodDescriptor; + return md != null && md.name == name && md.sig == sig; + } + + public override int GetHashCode() + { + return name.GetHashCode() ^ sig.GetHashCode(); + } + + internal static string getSigName(Type type) + { + if(type.IsValueType) + { + if(type == typeof(void)) + { + return "V"; + } + else if(type == typeof(bool)) + { + return "Z"; + } + else if(type == typeof(sbyte)) + { + return "B"; + } + else if(type == typeof(char)) + { + return "C"; + } + else if(type == typeof(short)) + { + return "S"; + } + else if(type == typeof(int)) + { + return "I"; + } + else if(type == typeof(long)) + { + return "J"; + } + else if(type == typeof(float)) + { + return "F"; + } + else if(type == typeof(double)) + { + return "D"; + } + else + { + return "L" + type.FullName.Replace('.', '/') + ";"; + } + } + else + { + string s = NativeCode.java.lang.Class.getName(type).Replace('.', '/'); + if(s[0] != '[') + { + s = "L" + s + ";"; + } + return s; + } + } + + internal static MethodDescriptor FromMethodBase(MethodBase mb) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + sb.Append('('); + foreach(ParameterInfo param in mb.GetParameters()) + { + sb.Append(getSigName(param.ParameterType)); + } + sb.Append(')'); + if(mb is ConstructorInfo) + { + sb.Append('V'); + return new MethodDescriptor(ClassLoaderWrapper.GetClassLoader(mb.DeclaringType), mb.IsStatic ? "<clinit>" : "<init>", sb.ToString()); + } + else + { + sb.Append(getSigName(((MethodInfo)mb).ReturnType)); + return new MethodDescriptor(ClassLoaderWrapper.GetClassLoader(mb.DeclaringType), mb.Name, sb.ToString()); + } + } +} + +abstract class TypeWrapper +{ + private ClassLoaderWrapper classLoader; + private string name; // java name (e.g. java/lang/Object) + private Modifiers modifiers; + protected Hashtable methods = new Hashtable(); + private Hashtable fields = new Hashtable(); + private TypeWrapper baseWrapper; + + public TypeWrapper(Modifiers modifiers, string name, TypeWrapper baseWrapper, ClassLoaderWrapper classLoader) + { + this.modifiers = modifiers; + this.name = name; + this.baseWrapper = baseWrapper; + this.classLoader = classLoader; + } + + internal Modifiers Modifiers + { + get + { + return modifiers; + } + } + + internal bool IsPublic + { + get + { + return (modifiers & Modifiers.Public) != 0; + } + } + + internal bool IsAbstract + { + get + { + return (modifiers & Modifiers.Abstract) != 0; + } + } + + internal bool IsFinal + { + get + { + return (modifiers & Modifiers.Final) != 0; + } + } + + internal ClassLoaderWrapper GetClassLoader() + { + return classLoader; + } + + protected abstract FieldWrapper GetFieldImpl(string fieldName); + + public FieldWrapper GetFieldWrapper(string fieldName) + { + FieldWrapper fae = (FieldWrapper)fields[fieldName]; + if(fae == null) + { + fae = GetFieldImpl(fieldName); + if(fae == null) + { + if(baseWrapper != null) + { + return baseWrapper.GetFieldWrapper(fieldName); + } + return null; + } + fields[fieldName] = fae; + } + return fae; + } + + // TODO figure out when it is safe to call this + // HACK for now we assume that the method hashtable has always been filled when this method is called (by java.lang.Class) + internal virtual MethodWrapper[] GetMethods() + { + MethodWrapper[] wrappers = new MethodWrapper[methods.Count]; + methods.Values.CopyTo(wrappers, 0); + return wrappers; + } + + // TODO figure out when it is safe to call this + // HACK for now we assume that the fields hashtable has always been filled when this method is called (by java.lang.Class) + internal virtual FieldWrapper[] GetFields() + { + FieldWrapper[] wrappers = new FieldWrapper[fields.Count]; + fields.Values.CopyTo(wrappers, 0); + return wrappers; + } + + protected abstract MethodWrapper GetMethodImpl(MethodDescriptor md); + + public MethodWrapper GetMethodWrapper(MethodDescriptor md, bool inherit) + { + MethodWrapper mce = (MethodWrapper)methods[md]; + if(mce == null) + { + mce = GetMethodImpl(md); + if(mce == null) + { + if(inherit && baseWrapper != null) + { + return baseWrapper.GetMethodWrapper(md, inherit); + } + return null; + } + methods[md] = mce; + } + return mce; + } + + public void AddMethod(MethodWrapper method) + { + if(method == null) + { + throw new ArgumentNullException(); + } + methods[method.Descriptor] = method; + } + + public void AddField(FieldWrapper field) + { + if(field == null) + { + throw new ArgumentNullException(); + } + fields[field.Name] = field; + } + + public string Name + { + get + { + return name; + } + } + + internal string PackageName + { + get + { + int index = name.LastIndexOf('/'); + if(index == -1) + { + return ""; + } + return name.Substring(0, index); + } + } + + // returns true iff wrapper is allowed to access us + internal bool IsAccessibleFrom(TypeWrapper wrapper) + { + return IsPublic || IsInSamePackageAs(wrapper); + } + + public bool IsInSamePackageAs(TypeWrapper wrapper) + { + if(GetClassLoader() == wrapper.GetClassLoader()) + { + int index1 = name.LastIndexOf('/'); + int index2 = wrapper.name.LastIndexOf('/'); + if(index1 == -1 && index2 == -1) + { + return true; + } + if(index1 == -1 || index2 == -1) + { + return false; + } + string package1 = name.Substring(0, index1); + string package2 = wrapper.name.Substring(0, index2); + return package1 == package2; + } + return false; + } + + public abstract Type Type + { + get; + } + + public TypeWrapper BaseTypeWrapper + { + get + { + return baseWrapper; + } + } + + public bool ImplementsInterface(TypeWrapper interfaceWrapper) + { + TypeWrapper typeWrapper = this; + while(typeWrapper != null) + { + for(int i = 0; i < typeWrapper.Interfaces.Length; i++) + { + if(typeWrapper.Interfaces[i] == interfaceWrapper) + { + return true; + } + if(typeWrapper.Interfaces[i].ImplementsInterface(interfaceWrapper)) + { + return true; + } + } + typeWrapper = typeWrapper.BaseTypeWrapper; + } + return false; + } + + public bool IsSubTypeOf(TypeWrapper baseType) + { + if(baseType.IsInterface) + { + if(baseType == this) + { + return true; + } + return ImplementsInterface(baseType); + } + TypeWrapper subType = this; + while(subType != baseType) + { + subType = subType.BaseTypeWrapper; + if(subType == null) + { + return false; + } + } + return true; + } + + public abstract bool IsInterface + { + get; + } + + public abstract TypeWrapper[] Interfaces + { + get; + } + + public abstract void Finish(); + + private void ImplementInterfaceMethodStubImpl(MethodDescriptor md, MethodBase ifmethod, TypeBuilder typeBuilder, TypeWrapper wrapper) + { + CustomAttributeBuilder methodFlags = new CustomAttributeBuilder(typeof(ModifiersAttribute).GetConstructor(new Type[] { typeof(Modifiers) }), new object[] { Modifiers.Synthetic }); + // HACK we're mangling the name to prevent subclasses from overriding this method (I think it is a bug + // the CLR that it tries to override privatescope methods, by name) + string mangledName = ifmethod.Name + "$" + wrapper.Name; + MethodWrapper mce = wrapper.GetMethodWrapper(md, true); + if(mce != null) + { + if(!mce.IsPublic) + { + // NOTE according to the ECMA spec it isn't legal for a privatescope method to be virtual, but this works and + // it makes sense, so I hope the spec is wrong + // UPDATE unfortunately, according to Serge Lidin the spec is correct, and it is not allowed to have virtual privatescope + // methods. Sigh! So I have to use private methods and mangle the name + MethodBuilder mb = typeBuilder.DefineMethod(mangledName, MethodAttributes.NewSlot | MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.Final, md.RetType, md.ArgTypes); + mb.SetCustomAttribute(methodFlags); + ILGenerator ilGenerator = mb.GetILGenerator(); + TypeWrapper exception = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/IllegalAccessError"); + ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name + "." + md.Name + md.Signature); + exception.GetMethodWrapper(new MethodDescriptor(ClassLoaderWrapper.GetBootstrapClassLoader(), "<init>", "(Ljava/lang/String;)V"), false).EmitNewobj.Emit(ilGenerator); + ilGenerator.Emit(OpCodes.Throw); + typeBuilder.DefineMethodOverride(mb, (MethodInfo)ifmethod); + } + else if(mce.GetMethod().Name != ifmethod.Name) + { + MethodBuilder mb = typeBuilder.DefineMethod(mangledName, MethodAttributes.NewSlot | MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.Final, md.RetType, md.ArgTypes); + mb.SetCustomAttribute(methodFlags); + ILGenerator ilGenerator = mb.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldarg_0); + int argc = md.ArgTypes.Length; + for(int n = 0; n < argc; n++) + { + ilGenerator.Emit(OpCodes.Ldarg_S, (byte)(n + 1)); + } + mce.EmitCallvirt.Emit(ilGenerator); + ilGenerator.Emit(OpCodes.Ret); + typeBuilder.DefineMethodOverride(mb, (MethodInfo)ifmethod); + } + } + else + { + if(!wrapper.IsAbstract) + { + // the type doesn't implement the interface method and isn't abstract either. The JVM allows this, but the CLR doesn't, + // so we have to create a stub method that throws an AbstractMethodError + MethodBuilder mb = typeBuilder.DefineMethod(mangledName, MethodAttributes.NewSlot | MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.Final, md.RetType, md.ArgTypes); + mb.SetCustomAttribute(methodFlags); + ILGenerator ilGenerator = mb.GetILGenerator(); + TypeWrapper exception = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/AbstractMethodError"); + ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name + "." + md.Name + md.Signature); + exception.GetMethodWrapper(new MethodDescriptor(ClassLoaderWrapper.GetBootstrapClassLoader(), "<init>", "(Ljava/lang/String;)V"), false).EmitNewobj.Emit(ilGenerator); + ilGenerator.Emit(OpCodes.Throw); + typeBuilder.DefineMethodOverride(mb, (MethodInfo)ifmethod); + } + else + { + // because of a bug in the .NET 1.0 CLR, we have emit an abstract Miranda method, otherwise + // the class will not be loadable under some circumstances + // Example (compile with Jikes 1.18): + //interface __Shape + //{ + // public abstract __Rectangle getBounds(); + // public abstract __Rectangle2D getBounds2D(); + //} + // + //abstract class __RectangularShape implements __Shape + //{ + // public __Rectangle getBounds() + // { + // return null; + // } + //} + // + //abstract class __Rectangle2D extends __RectangularShape + //{ + // public __Rectangle2D getBounds2D() + // { + // return null; + // } + //} + // + //class __Rectangle extends __Rectangle2D implements __Shape + //{ + // public __Rectangle getBounds() + // { + // return null; + // } + // + // public __Rectangle2D getBounds2D() + // { + // return null; + // } + //} + MethodBuilder mb = typeBuilder.DefineMethod(md.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract, md.RetType, md.ArgTypes); + mb.SetCustomAttribute(methodFlags); + } + } + } + + internal void ImplementInterfaceMethodStubs(TypeBuilder typeBuilder, TypeWrapper wrapper, Hashtable doneSet) + { + // TODO interfaces that implement other interfaces need to be handled as well... + if(!IsInterface) + { + throw new InvalidOperationException(); + } + // make sure we don't do the same method twice + if(doneSet.ContainsKey(this)) + { + return; + } + doneSet.Add(this, this); + Finish(); + // NOTE for dynamic types it isn't legal to call Type.GetMethods() (because + // that might trigger finishing of types that are already in the process of + // being finished) and for RemappedTypeWrappers it makes no sense, so both + // of these (ab)use the methods hashtable to obtain a list of methods + // NOTE since the types have been finished, we know for sure that all methods + // are in fact in the methods cache + if(Type.Assembly is AssemblyBuilder || this is RemappedTypeWrapper) + { + foreach(MethodWrapper method in methods.Values) + { + MethodBase ifmethod = method.GetMethod(); + if(!ifmethod.IsStatic) + { + ImplementInterfaceMethodStubImpl(method.Descriptor, ifmethod, typeBuilder, wrapper); + } + } + } + else + { + MethodInfo[] methods = Type.GetMethods(); + for(int i = 0; i < methods.Length; i++) + { + MethodInfo ifmethod = methods[i]; + if(!ifmethod.IsStatic) + { + ImplementInterfaceMethodStubImpl(MethodDescriptor.FromMethodBase(ifmethod), ifmethod, typeBuilder, wrapper); + } + } + } + TypeWrapper[] interfaces = Interfaces; + for(int i = 0; i < interfaces.Length; i++) + { + interfaces[i].ImplementInterfaceMethodStubs(typeBuilder, wrapper, doneSet); + } + } + + internal virtual void ImplementOverrideStubsAndVirtuals(TypeBuilder typeBuilder, TypeWrapper wrapper, Hashtable methodLookup) + { + } +} + +class PrimitiveTypeWrapper : TypeWrapper +{ + internal static readonly PrimitiveTypeWrapper BYTE = new PrimitiveTypeWrapper(typeof(sbyte)); + internal static readonly PrimitiveTypeWrapper CHAR = new PrimitiveTypeWrapper(typeof(char)); + internal static readonly PrimitiveTypeWrapper DOUBLE = new PrimitiveTypeWrapper(typeof(double)); + internal static readonly PrimitiveTypeWrapper FLOAT = new PrimitiveTypeWrapper(typeof(float)); + internal static readonly PrimitiveTypeWrapper INT = new PrimitiveTypeWrapper(typeof(int)); + internal static readonly PrimitiveTypeWrapper LONG = new PrimitiveTypeWrapper(typeof(long)); + internal static readonly PrimitiveTypeWrapper SHORT = new PrimitiveTypeWrapper(typeof(short)); + internal static readonly PrimitiveTypeWrapper BOOLEAN = new PrimitiveTypeWrapper(typeof(bool)); + internal static readonly PrimitiveTypeWrapper VOID = new PrimitiveTypeWrapper(typeof(void)); + + private Type type; + + private PrimitiveTypeWrapper(Type type) + : base(Modifiers.Public | Modifiers.Abstract | Modifiers.Final, null, null, ClassLoaderWrapper.GetBootstrapClassLoader()) + { + this.type = type; + } + + public override Type Type + { + get + { + return type; + } + } + + protected override FieldWrapper GetFieldImpl(string fieldName) + { + return null; + } + + protected override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + return null; + } + + public override bool IsInterface + { + get + { + return false; + } + } + + public override TypeWrapper[] Interfaces + { + get + { + // TODO does a primitive implement any interfaces? + return new TypeWrapper[0]; + } + } + + public override void Finish() + { + } +} + +class DynamicTypeWrapper : TypeWrapper +{ + private DynamicImpl impl; + private TypeWrapper[] interfaces; + + internal DynamicTypeWrapper(string name, ClassFile f, TypeWrapper baseType, ClassLoaderWrapper classLoader, Hashtable nativeMethods) + : base(f.Modifiers, name, baseType, classLoader) + { + JavaTypeImpl impl = new JavaTypeImpl(f, this, baseType, nativeMethods); + this.impl = impl; + interfaces = impl.GetInterfaces(); + } + + protected override FieldWrapper GetFieldImpl(string fieldName) + { + return impl.GetFieldImpl(fieldName); + } + + protected override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + return impl.GetMethodImpl(md); + } + + public override bool IsInterface + { + get + { + return impl.IsInterface; + } + } + + public override TypeWrapper[] Interfaces + { + get + { + return interfaces; + } + } + + public override Type Type + { + get + { + return impl.Type; + } + } + + public override void Finish() + { + impl = impl.Finish(); + } + + private abstract class DynamicImpl + { + public abstract FieldWrapper GetFieldImpl(string fieldName); + public abstract MethodWrapper GetMethodImpl(MethodDescriptor md); + public abstract Type Type { get; } + public abstract bool IsInterface { get; } + public abstract DynamicImpl Finish(); + } + + private class JavaTypeImpl : DynamicImpl + { + private ClassFile classFile; + private DynamicTypeWrapper wrapper; + private TypeWrapper baseWrapper; + private TypeBuilder typeBuilder; + private TypeWrapper[] interfaces; + private MethodWrapper[] methods; + private FieldWrapper[] fields; + private Hashtable methodLookup; + private Hashtable fieldLookup; + private bool finishing; + private Hashtable nativeMethods; + + internal JavaTypeImpl(ClassFile f, DynamicTypeWrapper wrapper, TypeWrapper baseWrapper, Hashtable nativeMethods) + { + // Console.WriteLine("constructing JavaTypeImpl for " + f.Name); + this.classFile = f; + this.wrapper = wrapper; + this.baseWrapper = baseWrapper; + this.nativeMethods = nativeMethods; + + TypeAttributes typeAttribs = 0; + if(f.IsAbstract) + { + typeAttribs |= TypeAttributes.Abstract; + } + if(f.IsFinal) + { + typeAttribs |= TypeAttributes.Sealed; + } + if(f.IsPublic) + { + typeAttribs |= TypeAttributes.Public; + } + if(f.IsInterface) + { + typeAttribs |= TypeAttributes.Interface | TypeAttributes.Abstract; + typeBuilder = wrapper.GetClassLoader().ModuleBuilder.DefineType(f.Name.Replace('/', '.'), typeAttribs); + } + else + { + typeAttribs |= TypeAttributes.Class; + typeBuilder = wrapper.GetClassLoader().ModuleBuilder.DefineType(f.Name.Replace('/', '.'), typeAttribs, baseWrapper.Type); + } + interfaces = new TypeWrapper[f.Interfaces.Length]; + for(int i = 0; i < f.Interfaces.Length; i++) + { + interfaces[i] = wrapper.GetClassLoader().LoadClassBySlashedName(f.Interfaces[i]); + if(!interfaces[i].IsInterface) + { + throw JavaException.IncompatibleClassChangeError("Implementing class"); + } + if(!interfaces[i].IsAccessibleFrom(wrapper)) + { + throw JavaException.IllegalAccessError("Class {0} cannot access its superinterface {1}", wrapper.Name, interfaces[i].Name); + } + typeBuilder.AddInterfaceImplementation(interfaces[i].Type); + } + } + + internal TypeWrapper[] GetInterfaces() + { + return interfaces; + } + + public override DynamicImpl Finish() + { + if(finishing) + { + throw new InvalidOperationException("Finishing already in progress, for type " + classFile.Name); + } + finishing = true; + // Console.WriteLine("finishing TypeFactory for " + classFile.Name); + if(fieldLookup == null) + { + fields = new FieldWrapper[classFile.Fields.Length]; + fieldLookup = new Hashtable(); + for(int i = 0; i < classFile.Fields.Length; i++) + { + fieldLookup[classFile.Fields[i].Name] = i; + } + } + for(int i = 0; i < fields.Length; i++) + { + if(fields[i] == null) + { + GenerateField(i); + wrapper.AddField(fields[i]); + } + } + MethodDescriptor[] methodDescriptors = new MethodDescriptor[classFile.Methods.Length]; + for(int i = 0; i < classFile.Methods.Length; i++) + { + methodDescriptors[i] = new MethodDescriptor(wrapper.GetClassLoader(), classFile.Methods[i].Name, classFile.Methods[i].Signature); + } + if(methodLookup == null) + { + methods = new MethodWrapper[classFile.Methods.Length]; + methodLookup = new Hashtable(); + for(int i = 0; i < classFile.Methods.Length; i++) + { + methodLookup[methodDescriptors[i]] = i; + } + } + for(int i = 0; i < methods.Length; i++) + { + if(methods[i] == null) + { + GenerateMethod(i); + wrapper.AddMethod(methods[i]); + } + } + wrapper.BaseTypeWrapper.Finish(); + bool basehasclinit = wrapper.BaseTypeWrapper.Type.GetConstructor(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, null, CallingConventions.Any, Type.EmptyTypes, null) != null; + bool hasclinit = false; + for(int i = 0; i < methods.Length; i++) + { + ILGenerator ilGenerator; + MethodBase mb = methods[i].GetMethod(); + if(mb is ConstructorBuilder) + { + ilGenerator = ((ConstructorBuilder)mb).GetILGenerator(); + if(basehasclinit && classFile.Methods[i].Name == "<clinit>" && classFile.Methods[i].Signature == "()V" && !classFile.IsInterface) + { + hasclinit = true; + ilGenerator.Emit(OpCodes.Ldtoken, Type.BaseType); + ilGenerator.Emit(OpCodes.Call, typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("RunClassConstructor")); + } + } + else + { + ilGenerator = ((MethodBuilder)mb).GetILGenerator(); + } + ClassFile.Method m = classFile.Methods[i]; + if(m.IsAbstract) + { + // NOTE in the JVM it is apparently legal for a non-abstract class to have abstract methods, but + // the CLR doens't allow this, so we have to emit a method that throws an AbstractMethodError + if(!m.ClassFile.IsAbstract && !m.ClassFile.IsInterface) + { + TypeWrapper exceptionType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/AbstractMethodError"); + MethodWrapper method = exceptionType.GetMethodWrapper(new MethodDescriptor(ClassLoaderWrapper.GetBootstrapClassLoader(), "<init>", "(Ljava/lang/String;)V"), false); + ilGenerator.Emit(OpCodes.Ldstr, m.ClassFile.Name + "." + m.Name + m.Signature); + method.EmitNewobj.Emit(ilGenerator); + ilGenerator.Emit(OpCodes.Throw); + } + } + else if(m.IsNative) + { + CustomAttributeBuilder methodFlags = new CustomAttributeBuilder(typeof(ModifiersAttribute).GetConstructor(new Type[] { typeof(Modifiers) }), new object[] { m.Modifiers }); + if(mb is ConstructorBuilder) + { + ((ConstructorBuilder)mb).SetCustomAttribute(methodFlags); + } + else + { + ((MethodBuilder)mb).SetCustomAttribute(methodFlags); + } + // do we have a native implementation in map.xml? + if(nativeMethods != null) + { + string key = classFile.Name.Replace('/', '.') + "." + m.Name + m.Signature; + CodeEmitter opcodes = (CodeEmitter)nativeMethods[key]; + if(opcodes != null) + { + opcodes.Emit(ilGenerator); + continue; + } + } + // see if there exists a NativeCode class for this type + Type nativeCodeType = Type.GetType("NativeCode." + classFile.Name.Replace('/', '.')); + MethodInfo nativeMethod = null; + if(nativeCodeType != null) + { + // TODO use better resolution + nativeMethod = nativeCodeType.GetMethod(m.Name); + } + Type[] args = wrapper.GetClassLoader().ArgTypeListFromSig(m.Signature); + if(nativeMethod != null) + { + int add = 0; + if(!m.IsStatic) + { + ilGenerator.Emit(OpCodes.Ldarg_0); + add = 1; + } + for(int j = 0; j < args.Length; j++) + { + ilGenerator.Emit(OpCodes.Ldarg, j + add); + } + ilGenerator.Emit(OpCodes.Call, nativeMethod); + Type retType = wrapper.GetClassLoader().RetTypeFromSig(m.Signature); + if(!retType.Equals(nativeMethod.ReturnType)) + { + ilGenerator.Emit(OpCodes.Castclass, retType); + } + } + else + { + if(JVM.NoJniStubs) + { + // TODO consider throwing a java.lang.UnsatisfiedLinkError here + //Console.WriteLine("Native method not implemented: " + classFile.Name + "." + m.Name + m.Signature); + ilGenerator.Emit(OpCodes.Ldstr, "Native method not implemented: " + classFile.Name + "." + m.Name + m.Signature); + ilGenerator.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[] { typeof(string) })); + ilGenerator.Emit(OpCodes.Throw); + continue; + } + //ilGenerator.EmitWriteLine(m.Name); + FieldBuilder methodPtr = typeBuilder.DefineField(m.Name + "$Ptr", typeof(IntPtr), FieldAttributes.Static | FieldAttributes.PrivateScope); + LocalBuilder localsref = ilGenerator.DeclareLocal(typeof(LocalRefStruct)); + ilGenerator.Emit(OpCodes.Ldsfld, methodPtr); + Label oklabel = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Brtrue, oklabel); + ilGenerator.Emit(OpCodes.Ldstr, m.Name); + ilGenerator.Emit(OpCodes.Ldstr, m.Signature); + ilGenerator.Emit(OpCodes.Ldstr, classFile.Name); + ilGenerator.Emit(OpCodes.Call, typeof(JNI).GetMethod("GetJniFuncPtr")); + ilGenerator.Emit(OpCodes.Stsfld, methodPtr); + ilGenerator.MarkLabel(oklabel); + Type retType = wrapper.GetClassLoader().RetTypeFromSig(m.Signature); + if(!retType.IsValueType) + { + // this one is for use after we return from "calli" + ilGenerator.Emit(OpCodes.Ldloca, localsref); + } + ilGenerator.Emit(OpCodes.Ldloca, localsref); + ilGenerator.Emit(OpCodes.Call, typeof(LocalRefStruct).GetMethod("Enter")); + Type[] modargs = new Type[args.Length + 2]; + modargs[0] = typeof(IntPtr); + modargs[1] = typeof(IntPtr); + args.CopyTo(modargs, 2); + int add = 0; + if(!m.IsStatic) + { + ilGenerator.Emit(OpCodes.Ldloca, localsref); + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Call, typeof(LocalRefStruct).GetMethod("MakeLocalRef")); + add = 1; + } + else + { + ilGenerator.Emit(OpCodes.Ldloca, localsref); + ilGenerator.Emit(OpCodes.Ldtoken, this.Type); + ilGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); + ilGenerator.Emit(OpCodes.Call, typeof(NativeCode.java.lang.Class).GetMethod("getClassFromType")); + ilGenerator.Emit(OpCodes.Call, typeof(LocalRefStruct).GetMethod("MakeLocalRef")); + } + for(int j = 0; j < args.Length; j++) + { + if(!args[j].IsValueType) + { + ilGenerator.Emit(OpCodes.Ldloca, localsref); + ilGenerator.Emit(OpCodes.Ldarg, j + add); + ilGenerator.Emit(OpCodes.Call, typeof(LocalRefStruct).GetMethod("MakeLocalRef")); + modargs[j + 2] = typeof(IntPtr); + } + else + { + ilGenerator.Emit(OpCodes.Ldarg, j + add); + } + } + ilGenerator.Emit(OpCodes.Ldsfld, methodPtr); + ilGenerator.EmitCalli(OpCodes.Calli, System.Runtime.InteropServices.CallingConvention.StdCall, retType.IsValueType ? retType : typeof(IntPtr), modargs); + ilGenerator.Emit(OpCodes.Ldloca, localsref); + // TODO we should use a try {} finally {} to make sure Leave is called, even if the native code throws an exception + ilGenerator.Emit(OpCodes.Call, typeof(LocalRefStruct).GetMethod("Leave")); + if(!retType.IsValueType) + { + ilGenerator.Emit(OpCodes.Call, typeof(LocalRefStruct).GetMethod("UnwrapLocalRef")); + ilGenerator.Emit(OpCodes.Castclass, retType); + } + } + ilGenerator.Emit(OpCodes.Ret); + } + else + { + Compiler.Compile(wrapper, m, ilGenerator, wrapper.GetClassLoader()); + } + } + // if we don't have a <clinit> we need to inject one to call the base class <clinit> + if(basehasclinit && !hasclinit && !classFile.IsInterface) + { + ILGenerator ilGenerator = typeBuilder.DefineConstructor(MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes).GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldtoken, Type.BaseType); + ilGenerator.Emit(OpCodes.Call, typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("RunClassConstructor")); + ilGenerator.Emit(OpCodes.Ret); + } + + if(!classFile.IsInterface) + { + // here we loop thru all the interfaces to explicitly implement any methods that we inherit from + // base types that may have a different name from the name in the interface + // (e.g. interface that has an equals() method that should override System.Object.Equals()) + // also deals with interface methods that aren't implemented (generate a stub that throws AbstractMethodError) + // and with methods that aren't public (generate a stub that throws IllegalAccessError) + Hashtable doneSet = new Hashtable(); + for(int i = 0; i < interfaces.Length; i++) + { + interfaces[i].ImplementInterfaceMethodStubs(typeBuilder, wrapper, doneSet); + } + wrapper.BaseTypeWrapper.ImplementOverrideStubsAndVirtuals(typeBuilder, wrapper, methodLookup); + } + + try + { + Type type = typeBuilder.CreateType(); + ClassLoaderWrapper.SetWrapperForType(type, wrapper); + return new FinishedTypeImpl(type); + } + catch + { + Console.WriteLine("Exception caused by : " + wrapper.Name); + throw; + } + } + + public override bool IsInterface + { + get + { + return typeBuilder.IsInterface; + } + } + + public override FieldWrapper GetFieldImpl(string fieldName) + { + if(fieldLookup == null) + { + fields = new FieldWrapper[classFile.Fields.Length]; + fieldLookup = new Hashtable(); + for(int i = 0; i < classFile.Fields.Length; i++) + { + fieldLookup[classFile.Fields[i].Name] = i; + } + } + object index = fieldLookup[fieldName]; + if(index != null) + { + int i = (int)index; + if(fields[i] == null) + { + GenerateField(i); + } + return fields[i]; + } + return null; + } + + private void GenerateField(int i) + { + FieldBuilder field; + ClassFile.Field fld = classFile.Fields[i]; + Type type = wrapper.GetClassLoader().ExpressionType(fld.Signature); + FieldAttributes attribs = 0; + MethodAttributes methodAttribs = 0; + bool setModifiers = false; + if(fld.IsPrivate) + { + attribs |= FieldAttributes.Private; + } + else if(fld.IsProtected) + { + attribs |= FieldAttributes.FamORAssem; + methodAttribs |= MethodAttributes.FamORAssem; + } + else if(fld.IsPublic) + { + attribs |= FieldAttributes.Public; + methodAttribs |= MethodAttributes.Public; + } + else + { + attribs |= FieldAttributes.Assembly; + methodAttribs |= MethodAttributes.Assembly; + } + if(fld.IsStatic) + { + attribs |= FieldAttributes.Static; + methodAttribs |= MethodAttributes.Static; + } + // NOTE "constant" static finals are converted into literals + // TODO it would be possible for Java code to change the value of a non-blank static final, but I don't + // know if we want to support this (since the Java JITs don't really support it either) + object constantValue = fld.ConstantValue; + if(fld.IsStatic && fld.IsFinal && constantValue != null) + { + attribs |= FieldAttributes.Literal; + field = typeBuilder.DefineField(fld.Name, type, attribs); + field.SetConstant(constantValue); + fields[i] = FieldWrapper.Create(wrapper, field, fld.Signature, fld.Modifiers); + // NOTE even though you're not supposed to access a constant static final (the compiler is supposed + // to inline them), we have to support it (because it does happen, e.g. if the field becomes final + // after the referencing class was compiled) + if(constantValue is int || constantValue is short || constantValue is sbyte || constantValue is char || constantValue is bool) + { + fields[i].EmitGet = CodeEmitter.Create(OpCodes.Ldc_I4, ((IConvertible)constantValue).ToInt32(null)); + } + else if(constantValue is string) + { + fields[i].EmitGet = CodeEmitter.Create(OpCodes.Ldstr, (string)constantValue); + } + else if(constantValue is float) + { + fields[i].EmitGet = CodeEmitter.Create(OpCodes.Ldc_R4, (float)constantValue); + } + else if(constantValue is double) + { + fields[i].EmitGet = CodeEmitter.Create(OpCodes.Ldc_R8, (double)constantValue); + } + else if(constantValue is long) + { + fields[i].EmitGet = CodeEmitter.Create(OpCodes.Ldc_I8, (long)constantValue); + } + else + { + throw new NotImplementedException(constantValue.GetType().FullName); + } + // TODO this should probably emit a IllegalAccessError (or whatever) + // TODO also check other field[x].EmitSet's for final fields + // UPDATE for other final fields this shouldn't happend, because the compiler will check access before + // calling EmitSet + // UPDATE when non-blank final fields are updated, the JIT normally doesn't see that (because the + // constant value is inlined), so we emulate that behavior by emitting a Nop + fields[i].EmitSet = CodeEmitter.Create(OpCodes.Nop); + } + else + { + if(fld.IsFinal) + { + // final doesn't make sense for private fields, so if the field is private we ignore final + if(!fld.IsPrivate && !wrapper.IsInterface) + { + // NOTE blank final fields get converted into a read-only property with a private field backing store + // we used to make the field privatescope, but that really serves no purpose (and it hinders serialization, + // which uses .NET reflection to get at the field) + attribs &= ~FieldAttributes.FieldAccessMask; + attribs |= FieldAttributes.Private; + setModifiers = true; + } + } + field = typeBuilder.DefineField(fld.Name, type, attribs); + if(fld.IsTransient) + { + CustomAttributeBuilder transientAttrib = new CustomAttributeBuilder(typeof(NonSerializedAttribute).GetConstructor(Type.EmptyTypes), new object[0]); + field.SetCustomAttribute(transientAttrib); + } + if(fld.IsVolatile) + { + // TODO the field should be marked as modreq(IsVolatile), but Reflection.Emit doesn't have a way of doing this + setModifiers = true; + } + if(fld.IsFinal && !fld.IsPrivate && !wrapper.IsInterface) + { + methodAttribs |= MethodAttributes.SpecialName; + // TODO we should ensure that the getter method name doesn't clash with an existing method + MethodBuilder getter = typeBuilder.DefineMethod("get_" + fld.Name, methodAttribs, CallingConventions.Standard, type, Type.EmptyTypes); + ModifiersAttribute.SetModifiers(getter, Modifiers.Synthetic); + ILGenerator ilgen = getter.GetILGenerator(); + if(fld.IsVolatile) + { + ilgen.Emit(OpCodes.Volatile); + } + if(fld.IsStatic) + { + ilgen.Emit(OpCodes.Ldsfld, field); + } + else + { + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Ldfld, field); + } + ilgen.Emit(OpCodes.Ret); + PropertyBuilder pb = typeBuilder.DefineProperty(fld.Name, PropertyAttributes.None, type, Type.EmptyTypes); + pb.SetGetMethod(getter); + fields[i] = FieldWrapper.Create(wrapper, field, fld.Signature, fld.Modifiers); + fields[i].EmitGet = CodeEmitter.Create(OpCodes.Call, getter); + } + else + { + fields[i] = FieldWrapper.Create(wrapper, field, fld.Signature, fld.Modifiers); + } + } + // if the Java modifiers cannot be expressed in .NET, we emit the Modifiers attribute to store + // the Java modifiers + if(setModifiers) + { + ModifiersAttribute.SetModifiers(field, fld.Modifiers); + } + } + + public override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + if(methodLookup == null) + { + methods = new MethodWrapper[classFile.Methods.Length]; + methodLookup = new Hashtable(); + for(int i = 0; i < classFile.Methods.Length; i++) + { + methodLookup[new MethodDescriptor(wrapper.GetClassLoader(), classFile.Methods[i].Name, classFile.Methods[i].Signature)] = i; + } + } + object index = methodLookup[md]; + if(index != null) + { + int i = (int)index; + if(methods[i] == null) + { + GenerateMethod(i); + } + return methods[i]; + } + return null; + } + + private void GenerateMethod(int index) + { + if(methods[index] != null) + { + throw new InvalidOperationException(); + } + MethodBase method; + ClassFile.Method m = classFile.Methods[index]; + Type[] args = wrapper.GetClassLoader().ArgTypeListFromSig(m.Signature); + MethodAttributes attribs = 0; + if(m.IsAbstract) + { + // only if the classfile is abstract, we make the CLR method abstract, otherwise, + // we have to generate a method that throws an AbstractMethodError (because the JVM + // allows abstract methods in non-abstract classes) + if(m.ClassFile.IsAbstract || m.ClassFile.IsInterface) + { + attribs |= MethodAttributes.Abstract; + } + } + if(m.IsFinal) + { + if(!m.IsStatic && !m.IsPrivate) + { + attribs |= MethodAttributes.Final; + } + } + if(m.IsPrivate) + { + attribs |= MethodAttributes.Private; + } + else if(m.IsProtected) + { + attribs |= MethodAttributes.FamORAssem; + } + else if(m.IsPublic) + { + attribs |= MethodAttributes.Public; + } + else + { + attribs |= MethodAttributes.Assembly; + } + if(m.IsStatic) + { + attribs |= MethodAttributes.Static; + } + if(m.Name == "<init>") + { + method = typeBuilder.DefineConstructor(attribs, CallingConventions.Standard, args); + } + else if(m.Name == "<clinit>" && m.Signature == "()V") + { + // I think this is a CLR bug, the verifier requires static inititializes of interfaces to be public + // TODO report this bug + if(classFile.IsInterface) + { + attribs &= ~MethodAttributes.MemberAccessMask; + attribs |= MethodAttributes.Public; + } + method = typeBuilder.DefineConstructor(attribs, CallingConventions.Standard, args); + } + else + { + if(!m.IsPrivate && !m.IsStatic) + { + attribs |= MethodAttributes.Virtual; + } + string name = m.Name; + MethodDescriptor md = new MethodDescriptor(wrapper.GetClassLoader(), name, m.Signature); + // if a method is virtual, we need to find the method it overrides (if any), for several reasons: + // - if we're overriding a method that has a different name (e.g. some of the virtual methods + // in System.Object [Equals <-> equals]) we need to add an explicit MethodOverride + // - if one of the base classes has a similar method that is private (or package) that we aren't + // overriding, we need to specify an explicit MethodOverride + MethodBase baseMethod = null; + bool explicitOverride = false; + if((attribs & MethodAttributes.Virtual) != 0 && !classFile.IsInterface) + { + TypeWrapper tw = baseWrapper; + while(tw != null) + { + MethodWrapper baseMce = tw.GetMethodWrapper(md, true); + if(baseMce == null) + { + break; + } + // here are the complex rules for determining wether this method overrides the method we found + // RULE 1: final methods may not be overriden + if(baseMce.IsFinal) + { + // NOTE we don't need to test for our method being private, because if it is + // we'll never get here (because private methods aren't virtual) + // TODO make sure the VerifyError is translated into a java.lang.VerifyError + throw new VerifyError(); + } + // RULE 2: public & protected methods can be overridden (package methods are handled by RULE 4) + // (by public, protected & *package* methods [even if they are in a different package]) + if(baseMce.IsPublic || baseMce.IsProtected) + { + // if we already encountered a package method, we cannot override the base method of + // that package method + if(explicitOverride) + { + explicitOverride = false; + break; + } + // if our method's accessibility is less than the method it overrides, we + // need to make our method more accessible, because the CLR doesn't allow reducing access + if((attribs & MethodAttributes.Public) == 0) + { + attribs &= ~MethodAttributes.MemberAccessMask; + if(baseMce.IsPublic) + { + attribs |= MethodAttributes.Public; + } + else + { + attribs |= MethodAttributes.FamORAssem; + } + } + baseMethod = baseMce.GetMethod(); + break; + } + // RULE 3: private methods are ignored + if(!baseMce.IsPrivate) + { + // RULE 4: package methods can only be overridden in the same package + if(baseMce.DeclaringType.IsInSamePackageAs(wrapper)) + { + baseMethod = baseMce.GetMethod(); + break; + } + // since we encountered a method with the same name/signature that we aren't overriding, + // we need to specify an explicit override + // NOTE we only do this if baseMce isn't private, because if it is, Reflection.Emit + // will complain about the explicit MethodOverride (possibly a bug) + explicitOverride = true; + } + tw = baseMce.DeclaringType.BaseTypeWrapper; + } + if(baseMethod == null) + { + attribs |= MethodAttributes.NewSlot; + } + } + MethodBuilder mb = typeBuilder.DefineMethod(name, attribs, wrapper.GetClassLoader().RetTypeFromSig(m.Signature), args); + method = mb; + // since Java constructors (and static intializers?) aren't allowed to be synchronized, we only check this here + if(m.IsSynchronized) + { + mb.SetImplementationFlags(method.GetMethodImplementationFlags() | MethodImplAttributes.Synchronized); + } + if(baseMethod != null && (explicitOverride || baseMethod.Name != name)) + { + // assert that the method we're overriding is in fact virtual and not final! + Debug.Assert(baseMethod.IsVirtual && !baseMethod.IsFinal); + typeBuilder.DefineMethodOverride(mb, (MethodInfo)baseMethod); + } + if(!wrapper.IsAbstract && m.IsPublic && !m.IsStatic) + { + // TODO this must be sped up... + // TODO process interfaces implemented by base classes (and interfaces implemented by interfaces) + foreach(TypeWrapper ifw in wrapper.BaseTypeWrapper.Interfaces) + { + MethodWrapper mw = ifw.GetMethodWrapper(md, false); + if(mw != null) + { + // TODO don't add explicit method override, unless it is required + typeBuilder.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod()); + } + } + } + } + methods[index] = MethodWrapper.Create(wrapper, new MethodDescriptor(wrapper.GetClassLoader(), m.Name, m.Signature), method, method, m.Modifiers); + } + + public override Type Type + { + get + { + return typeBuilder; + } + } + } + + private class FinishedTypeImpl : DynamicImpl + { + private Type type; + + public FinishedTypeImpl(Type type) + { + this.type = type; + } + + public override bool IsInterface + { + get + { + return type.IsInterface; + } + } + + public override FieldWrapper GetFieldImpl(string fieldName) + { + return null; + } + + public override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + return null; + } + + public override Type Type + { + get + { + return type; + } + } + + public override DynamicImpl Finish() + { + return this; + } + } +} + +class RemappedTypeWrapper : TypeWrapper +{ + private Type type; + private TypeWrapper[] interfaces; + private bool virtualsInterfaceBuilt; + private Type virtualsInterface; + private Type virtualsHelperHack; + + public RemappedTypeWrapper(Modifiers modifiers, string name, Type type, TypeWrapper[] interfaces, TypeWrapper baseType) + : base(modifiers, name, baseType, ClassLoaderWrapper.GetBootstrapClassLoader()) + { + this.type = type; + this.interfaces = interfaces; + } + + public void LoadRemappings(MapXml.Class classMap) + { + bool hasOverrides = false; + ArrayList methods = new ArrayList(); + if(classMap.Methods != null) + { + foreach(MapXml.Method method in classMap.Methods) + { + string name = method.Name; + string sig = method.Sig; + Modifiers modifiers = (Modifiers)method.Modifiers; + bool isStatic = (modifiers & Modifiers.Static) != 0; + MethodDescriptor md = new MethodDescriptor(GetClassLoader(), name, sig); + if(method.invokevirtual == null && + method.invokespecial == null && + method.invokestatic == null && + method.redirect == null && + method.@override == null) + { + // TODO use a better way to get the method + BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic; + if(isStatic) + { + binding |= BindingFlags.Static; + } + else + { + binding |= BindingFlags.Instance; + } + MethodBase mb = type.GetMethod(name, binding, null, CallingConventions.Standard, md.ArgTypes, null); + if(mb == null) + { + throw new InvalidOperationException("declared method: " + Name + "." + name + sig + " not found"); + } + MethodWrapper mw = MethodWrapper.Create(this, md, mb, mb, modifiers); + AddMethod(mw); + methods.Add(mw); + } + else + { + CodeEmitter newopc = null; + CodeEmitter invokespecial = null; + CodeEmitter invokevirtual = null; + CodeEmitter retcast = null; + MethodBase redirect = null; + MethodBase overrideMethod = null; + if(method.redirect != null) + { + if(method.invokevirtual != null || + method.invokespecial != null || + method.invokestatic != null || + method.@override != null) + { + throw new InvalidOperationException(); + } + if(method.redirect.Name != null) + { + name = method.redirect.Name; + } + if(method.redirect.Sig != null) + { + sig = method.redirect.Sig; + } + string stype = isStatic ? "static" : "instance"; + if(method.redirect.Type != null) + { + stype = method.redirect.Type; + } + MethodDescriptor redir = new MethodDescriptor(GetClassLoader(), name, sig); + BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic; + if(stype == "static") + { + binding |= BindingFlags.Static; + } + else + { + binding |= BindingFlags.Instance; + } + Type t = this.type; + if(method.redirect.Class != null) + { + t = Type.GetType(method.redirect.Class, true); + } + redirect = t.GetMethod(name, binding, null, CallingConventions.Standard, redir.ArgTypes, null); + if(redirect == null) + { + throw new InvalidOperationException("remapping method: " + name + sig + " not found"); + } + string ret = md.Signature.Substring(md.Signature.IndexOf(')') + 1); + // when constructors are remapped, we have to assume that the type is correct because the original + // return type (of the constructor) is void. + if(ret[0] != 'V' && ret != redir.Signature.Substring(redir.Signature.IndexOf(')') + 1)) + { + retcast = new CastEmitter(md.Signature); + } + if(BaseTypeWrapper != null && !Type.IsSealed) + { + MethodWrapper mce1 = BaseTypeWrapper.GetMethodWrapper(md, true); + if(mce1 != null) + { + MethodBase org = mce1.GetMethod(); + if(org != null) + { + ParameterInfo[] paramInfo = org.GetParameters(); + Type[] argTypes = new Type[paramInfo.Length]; + for(int i = 0; i < argTypes.Length; i++) + { + argTypes[i] = paramInfo[i].ParameterType; + } + BindingFlags binding1 = BindingFlags.Public | BindingFlags.NonPublic; + if(isStatic) + { + // TODO this looks like total nonsense, a static method cannot override a method, + // so we shouldn't ever get here + binding1 |= BindingFlags.Static; + } + else + { + binding1 |= BindingFlags.Instance; + } + overrideMethod = type.GetMethod(org.Name, binding1, null, CallingConventions.Standard, argTypes, null); + } + } + } + // NOTE we abuse MethodWrapper.Create here to construct the emitters for us + MethodWrapper temp = MethodWrapper.Create(this, md, overrideMethod, redirect, modifiers); + newopc = temp.EmitNewobj; + invokespecial = temp.EmitCall; + invokevirtual = temp.EmitCallvirt; + } + else + { + if(method.@override != null) + { + MethodDescriptor redir = new MethodDescriptor(GetClassLoader(), method.@override.Name, sig); + BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + overrideMethod = type.GetMethod(redir.Name, binding, null, CallingConventions.Standard, redir.ArgTypes, null); + if(overrideMethod == null) + { + throw new InvalidOperationException("Override method not found: " + Name + "." + name + sig); + } + } + invokespecial = method.invokespecial; + invokevirtual = method.invokevirtual; + if(method.invokestatic != null) + { + invokespecial = method.invokestatic; + } + } + // if invokespecial isn't redefined, it means that the base class' implementation is correct, + // so we don't need to generate an override stub for this method + bool trivialOverride = (invokespecial == null); + if(overrideMethod != null) + { + if(invokespecial == null) + { + invokespecial = CodeEmitter.Create(OpCodes.Call, (MethodInfo)overrideMethod); + } + if(invokevirtual == null) + { + invokevirtual = CodeEmitter.Create(OpCodes.Callvirt, (MethodInfo)overrideMethod); + } + } + MethodWrapper mw = new MethodWrapper(this, md, overrideMethod, modifiers); + mw.EmitNewobj = newopc; + mw.EmitCall = invokespecial; + mw.EmitCallvirt = invokevirtual; + if(retcast != null) + { + mw.EmitNewobj += retcast; + mw.EmitCall += retcast; + mw.EmitCallvirt += retcast; + } + // don't generate override stubs for trivial methods (i.e. methods that are only renamed) + if(overrideMethod != null && !trivialOverride) + { + hasOverrides = true; + mw.IsRemappedOverride = true; + } + if(method.Type == "virtual") + { + // TODO we're overwriting the retcast (if there is any). We shouldn't do this. + mw.EmitCallvirt = new VirtualEmitter(md, this); + mw.IsRemappedVirtual = true; + } + AddMethod(mw); + methods.Add(mw); + } + } + } + if(classMap.Constructors != null) + { + foreach(MapXml.Constructor constructor in classMap.Constructors) + { + Modifiers modifiers = (Modifiers)constructor.Modifiers; + MethodDescriptor md = new MethodDescriptor(GetClassLoader(), "<init>", constructor.Sig); + if(constructor.invokespecial == null && constructor.newobj == null && constructor.redirect == null) + { + // TODO use a better way to get the method + BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + MethodBase method = type.GetConstructor(binding, null, CallingConventions.Standard, md.ArgTypes, null); + if(method == null) + { + throw new InvalidOperationException("declared constructor: " + classMap.Name + constructor.Sig + " not found"); + } + MethodWrapper mw = MethodWrapper.Create(this, md, method, method, modifiers); + AddMethod(mw); + methods.Add(mw); + } + else + { + CodeEmitter newopc = null; + CodeEmitter invokespecial = null; + CodeEmitter retcast = null; + if(constructor.redirect != null) + { + if(constructor.invokespecial != null || constructor.newobj != null) + { + throw new InvalidOperationException(); + } + string sig = constructor.Sig; + if(constructor.redirect.Sig != null) + { + sig = constructor.redirect.Sig; + } + MethodDescriptor redir = new MethodDescriptor(GetClassLoader(), "<init>", sig); + BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic; + if(constructor.redirect.Type == "static") + { + binding |= BindingFlags.Static; + if((classMap.Modifiers & MapXml.MapModifiers.Final) == 0) + { + // NOTE only final classes can have constructors redirected to static methods, + // because we don't have support for making the distinction between new and invokespecial + throw new InvalidOperationException(); + } + } + else + { + binding |= BindingFlags.Instance; + } + Type t = this.type; + if(constructor.redirect.Class != null) + { + t = Type.GetType(constructor.redirect.Class, true); + } + MethodBase redirect = null; + if(constructor.redirect.Name != null) + { + redirect = t.GetMethod(constructor.redirect.Name, binding, null, CallingConventions.Standard, redir.ArgTypes, null); + } + else + { + redirect = t.GetConstructor(binding, null, CallingConventions.Standard, redir.ArgTypes, null); + } + if(redirect == null) + { + throw new InvalidOperationException("remapping constructor: " + classMap.Name + constructor.Sig + " not found"); + } + string ret = md.Signature.Substring(md.Signature.IndexOf(')') + 1); + // when constructors are remapped, we have to assume that the type is correct because the original + // return type (of the constructor) is void. + // TODO we could look at return type of the redirected method and see if that matches the type of the + // object we're supposed to be constructing + if(ret[0] != 'V' && ret != redir.Signature.Substring(redir.Signature.IndexOf(')') + 1)) + { + retcast = new CastEmitter(md.Signature); + } + // NOTE we abuse MethodWrapper.Create here to construct the emitters for us + MethodWrapper temp = MethodWrapper.Create(this, md, null, redirect, modifiers); + newopc = temp.EmitNewobj; + } + else + { + newopc = constructor.newobj; + invokespecial = constructor.invokespecial; + } + MethodWrapper mw = new MethodWrapper(this, md, null, modifiers); + mw.EmitNewobj = newopc; + mw.EmitCall = invokespecial; + if(retcast != null) + { + mw.EmitNewobj += retcast; + mw.EmitCall += retcast; + } + AddMethod(mw); + methods.Add(mw); + } + } + } + if(classMap.Fields != null) + { + foreach(MapXml.Field field in classMap.Fields) + { + string name = field.Name; + string sig = field.Sig; + string fieldName = name; + string fieldSig = sig; + Modifiers modifiers = (Modifiers)field.Modifiers; + bool isStatic = (modifiers & Modifiers.Static) != 0; + if(field.redirect == null) + { + throw new InvalidOperationException(); + } + // NOTE when fields are redirected it's always to a method! + // NOTE only reading a field can be redirected! + if(field.redirect.Name != null) + { + name = field.redirect.Name; + } + if(field.redirect.Sig != null) + { + sig = field.redirect.Sig; + } + string stype = isStatic ? "static" : "instance"; + if(field.redirect.Type != null) + { + stype = field.redirect.Type; + } + MethodDescriptor redir = new MethodDescriptor(GetClassLoader(), name, sig); + BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic; + if(stype == "static") + { + binding |= BindingFlags.Static; + } + else + { + binding |= BindingFlags.Instance; + } + Type t = this.type; + if(field.redirect.Class != null) + { + t = Type.GetType(field.redirect.Class, true); + } + MethodInfo method = t.GetMethod(name, binding, null, CallingConventions.Standard, redir.ArgTypes, null); + if(method == null) + { + throw new InvalidOperationException("remapping method: " + name + sig + " not found"); + } + // TODO ensure that return type for redirected method matches with field type, or emit a castclass + FieldWrapper fw = new FieldWrapper(this, fieldName, fieldSig, modifiers); + fw.EmitGet = CodeEmitter.Create(OpCodes.Call, method); + fw.EmitSet = null; + AddField(fw); + } + } + // if the type has "overrides" we need to construct a stub class that actually overrides the methods + // (for when the type itself is instantiated, instead of a subtype [e.g. java.lang.Throwable]) + if(hasOverrides) + { + //Console.WriteLine("constructing override stub for " + Name); + // HACK because we don't want to end up with System.Exception (which is the type that corresponds to the + // TypeWrapper that corresponds to the type of Throwable$OverrideStub) we have to use GetBootstrapTypeRaw, + // which was introduced specifically to deal with this problem + Type stubType = ClassLoaderWrapper.GetBootstrapClassLoader().GetBootstrapTypeRaw(Name.Replace('/', '.') + "$OverrideStub"); + if(stubType != null) + { + foreach(MethodWrapper mw in methods) + { + MethodDescriptor md = mw.Descriptor; + if(md.Name == "<init>") + { + //Console.WriteLine("replacing newobj " + stubType.FullName + " to " + stubType.GetConstructor(md.ArgTypes)); + // NOTE we only support public constructors here, as that correct? + mw.EmitNewobj = CodeEmitter.Create(OpCodes.Newobj, stubType.GetConstructor(md.ArgTypes)); + } + } + } + else + { + // TODO we can ignore the normal ClassNotFoundException, what should we do with other exceptions? + TypeBuilder stub = GetClassLoader().ModuleBuilder.DefineType(Name.Replace('/', '.') + "$OverrideStub", type.Attributes, type); + CustomAttributeBuilder overrideStubAttrib = new CustomAttributeBuilder(typeof(OverrideStubTypeAttribute).GetConstructor(Type.EmptyTypes), new object[0]); + stub.SetCustomAttribute(overrideStubAttrib); + foreach(MethodWrapper mw in methods) + { + MethodDescriptor md = mw.Descriptor; + if(mw.IsRemappedOverride) + { + MethodBuilder mb = stub.DefineMethod(md.Name, mw.GetMethodAttributes(), CallingConventions.Standard, md.RetType, md.ArgTypes); + ILGenerator ilgen = mb.GetILGenerator(); + ilgen.Emit(OpCodes.Ldarg_0); + int argc = md.ArgTypes.Length; + for(int n = 0; n < argc; n++) + { + ilgen.Emit(OpCodes.Ldarg, n + 1); + } + mw.EmitCall.Emit(ilgen); + ilgen.Emit(OpCodes.Ret); + // TODO only explicitly override if it is needed + stub.DefineMethodOverride(mb, (MethodInfo)mw.GetMethod()); + } + else if(md.Name == "<init>") + { + ConstructorBuilder cb = stub.DefineConstructor(mw.GetMethodAttributes(), CallingConventions.Standard, md.ArgTypes); + ILGenerator ilgen = cb.GetILGenerator(); + ilgen.Emit(OpCodes.Ldarg_0); + int argc = md.ArgTypes.Length; + for(int n = 0; n < argc; n++) + { + ilgen.Emit(OpCodes.Ldarg, n + 1); + } + mw.EmitCall.Emit(ilgen); + ilgen.Emit(OpCodes.Ret); + mw.EmitNewobj = CodeEmitter.Create(OpCodes.Newobj, cb); + } + } + // TODO consider post-poning this until the type is really needed + stub.CreateType(); + } + } + } + + public override bool IsInterface + { + get + { + return type.IsInterface; + } + } + + public override TypeWrapper[] Interfaces + { + get + { + return interfaces; + } + } + + public override Type Type + { + get + { + return type; + } + } + + public override void Finish() + { + } + + internal override void ImplementOverrideStubsAndVirtuals(TypeBuilder typeBuilder, TypeWrapper wrapper, Hashtable methodLookup) + { + MethodWrapper[] methods = new MethodWrapper[this.methods.Count]; + this.methods.Values.CopyTo(methods, 0); + Type virtualsInterface = VirtualsInterface; + if(virtualsInterface != null) + { + typeBuilder.AddInterfaceImplementation(virtualsInterface); + } + for(int i = 0; i < methods.Length; i++) + { + MethodWrapper mce = methods[i]; + if(mce.IsRemappedOverride) + { + MethodDescriptor md = mce.Descriptor; + if(!methodLookup.ContainsKey(md)) + { + MethodBuilder mb = typeBuilder.DefineMethod(md.Name, mce.GetMethodAttributes(), CallingConventions.Standard, md.RetType, md.ArgTypes); + ILGenerator ilgen = mb.GetILGenerator(); + ilgen.Emit(OpCodes.Ldarg_0); + int argc = md.ArgTypes.Length; + for(int n = 0; n < argc; n++) + { + ilgen.Emit(OpCodes.Ldarg, n + 1); + } + mce.EmitCall.Emit(ilgen); + ilgen.Emit(OpCodes.Ret); + // TODO only explicitly override if it is needed + typeBuilder.DefineMethodOverride(mb, (MethodInfo)mce.GetMethod()); + // now add the method to methodLookup, to prevent the virtuals loop below from adding it again + methodLookup[md] = md; + } + } + if(mce.IsRemappedVirtual) + { + MethodDescriptor md = mce.Descriptor; + if(!methodLookup.ContainsKey(md)) + { + // TODO the attributes aren't correct, but we cannot make the method non-public, because + // that would violate the interface contract. In other words, we need to find a different + // mechanism for implementing non-public virtuals. + MethodBuilder mb = typeBuilder.DefineMethod(md.Name, MethodAttributes.Virtual | MethodAttributes.Public, CallingConventions.Standard, md.RetType, md.ArgTypes); + ILGenerator ilgen = mb.GetILGenerator(); + ilgen.Emit(OpCodes.Ldarg_0); + int argc = md.ArgTypes.Length; + for(int n = 0; n < argc; n++) + { + ilgen.Emit(OpCodes.Ldarg, n + 1); + } + mce.EmitCall.Emit(ilgen); + ilgen.Emit(OpCodes.Ret); + } + } + } + } + + private MethodWrapper[] GetVirtuals() + { + ArrayList array = new ArrayList(); + foreach(MethodWrapper mw in methods.Values) + { + if(mw.IsRemappedVirtual) + { + array.Add(mw); + } + } + return (MethodWrapper[])array.ToArray(typeof(MethodWrapper)); + } + + private Type VirtualsInterface + { + get + { + if(!virtualsInterfaceBuilt) + { + virtualsInterfaceBuilt = true; + MethodWrapper[] virtuals = GetVirtuals(); + if(virtuals.Length > 0) + { + // if the virtualsinterface already exists in one of the bootstrap DLLs, we need to reference that one + // instead of creating another one, because creating a new type breaks compatibility with pre-compiled code + try + { + virtualsInterface = ClassLoaderWrapper.GetType(Name.Replace('/', '.') + "$VirtualMethods"); + virtualsHelperHack = ClassLoaderWrapper.GetType(Name.Replace('/', '.') + "$VirtualMethodsHelper"); + } + catch(Exception) + { + } + if(virtualsInterface != null && virtualsHelperHack != null) + { + return virtualsInterface; + } + // TODO since this construct makes all virtual methods public, we need to find another way of doing this, + // or split the methods in two interfaces, one public and one friendly (but how about protected?). + TypeBuilder typeBuilder = GetClassLoader().ModuleBuilder.DefineType(Name.Replace('/', '.') + "$VirtualMethods", TypeAttributes.Abstract | TypeAttributes.Interface | TypeAttributes.Public); + TypeBuilder tbStaticHack = GetClassLoader().ModuleBuilder.DefineType(Name.Replace('/', '.') + "$VirtualMethodsHelper", TypeAttributes.Class | TypeAttributes.Public); + foreach(MethodWrapper mw in virtuals) + { + MethodDescriptor md = mw.Descriptor; + MethodBuilder ifmethod = typeBuilder.DefineMethod(md.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract, md.RetType, md.ArgTypes); + Type[] args = new Type[md.ArgTypes.Length + 1]; + md.ArgTypes.CopyTo(args, 1); + args[0] = this.Type; + MethodBuilder mb = tbStaticHack.DefineMethod(md.Name, MethodAttributes.Public | MethodAttributes.Static, md.RetType, args); + ILGenerator ilgen = mb.GetILGenerator(); + ilgen.Emit(OpCodes.Ldarg_0); + ilgen.Emit(OpCodes.Isinst, typeBuilder); + ilgen.Emit(OpCodes.Dup); + Label label1 = ilgen.DefineLabel(); + ilgen.Emit(OpCodes.Brtrue_S, label1); + ilgen.Emit(OpCodes.Pop); + for(int i = 0; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldarg, i); + } + GetMethodWrapper(md, true).EmitCall.Emit(ilgen); + Label label2 = ilgen.DefineLabel(); + ilgen.Emit(OpCodes.Br_S, label2); + ilgen.MarkLabel(label1); + for(int i = 1; i < args.Length; i++) + { + ilgen.Emit(OpCodes.Ldarg, i); + } + ilgen.Emit(OpCodes.Callvirt, ifmethod); + ilgen.MarkLabel(label2); + ilgen.Emit(OpCodes.Ret); + } + virtualsInterface = typeBuilder.CreateType(); + virtualsHelperHack = tbStaticHack.CreateType(); + } + } + return virtualsInterface; + } + } + + // HACK since Reflection.Emit won't allow static methods on an interface (which is a bug), we create + // a separate type to contain the static helper methods + public Type VirtualsHelperHack + { + get + { + // make sure that the type has been created + object o = this.VirtualsInterface; + return virtualsHelperHack; + } + } + + protected override FieldWrapper GetFieldImpl(string fieldName) + { + return null; + } + + protected override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + return null; + } +} + +class NetExpTypeWrapper : TypeWrapper +{ + private ClassFile classFile; + private Type type; + + // TODO consider constructing modifiers from .NET type instead of the netexp class + public NetExpTypeWrapper(ClassFile f, string dotnetType, TypeWrapper baseType) + : base(f.Modifiers, f.Name, baseType, ClassLoaderWrapper.GetBootstrapClassLoader()) + { + this.classFile = f; + // TODO if the type isn't found, it should be handled differently + type = Type.GetType(dotnetType, true); + } + + public override bool IsInterface + { + get + { + return type.IsInterface; + } + } + + public override TypeWrapper[] Interfaces + { + get + { + // TODO resolve the interfaces! + return new TypeWrapper[0]; + } + } + + protected override FieldWrapper GetFieldImpl(string fieldName) + { + // HACK this is a totally broken quick & dirty implementation + // TODO clean this up, add error checking and whatnot + FieldInfo field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + Modifiers modifiers = ModifiersAttribute.GetModifiers(field); + if(modifiers != Modifiers.Synthetic) + { + return FieldWrapper.Create(this, field, MethodDescriptor.getSigName(field.FieldType), modifiers); + } + return null; + } + + private class DelegateConstructorEmitter : CodeEmitter + { + private ConstructorInfo delegateConstructor; + private MethodInfo method; + + internal DelegateConstructorEmitter(ConstructorInfo delegateConstructor, MethodInfo method) + { + this.delegateConstructor = delegateConstructor; + this.method = method; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(OpCodes.Dup); + ilgen.Emit(OpCodes.Ldvirtftn, method); + ilgen.Emit(OpCodes.Newobj, delegateConstructor); + } + } + + protected override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + // special case for delegate constructors! + if(md.Name == "<init>" && type.IsSubclassOf(typeof(MulticastDelegate))) + { + // TODO set method flags + MethodWrapper method = new MethodWrapper(this, md, null, Modifiers.Public); + // TODO what class loader should we use? + TypeWrapper iface = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName(classFile.Name + "$Method"); + iface.Finish(); + method.EmitNewobj = new DelegateConstructorEmitter(type.GetConstructor(new Type[] { typeof(object), typeof(IntPtr) }), iface.Type.GetMethod("Invoke")); + return method; + } + // HACK this is a totally broken quick & dirty implementation + // TODO clean this up, add error checking and whatnot + ClassFile.Method[] methods = classFile.Methods; + for(int i = 0; i < methods.Length; i++) + { + if(methods[i].Name == md.Name && methods[i].Signature == md.Signature) + { + Type[] args; + string[] sig = methods[i].NetExpSigAttribute; + if(sig == null) + { + args = md.ArgTypes; + } + else + { + args = new Type[sig.Length]; + for(int j = 0; j < sig.Length; j++) + { + args[j] = Type.GetType(sig[j], true); + } + } + MethodBase method; + if(md.Name == "<init>") + { + method = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard, args, null); + } + else + { + method = type.GetMethod(md.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, null, CallingConventions.Standard, args, null); + } + if(method != null) + { + // TODO we can decode the actual method attributes, or we can use them from the NetExp class, what is + // preferred? + return MethodWrapper.Create(this, md, method, method, ModifiersAttribute.GetModifiers(method)); + } + } + } + return null; + } + + public override Type Type + { + get + { + return type; + } + } + + public override void Finish() + { + } +} + +class CompiledTypeWrapper : TypeWrapper +{ + private Type type; + private TypeWrapper[] interfaces; + + // TODO consider resolving the baseType lazily + internal CompiledTypeWrapper(string name, Type type, TypeWrapper baseType) + : base(ModifiersAttribute.GetModifiers(type), name, baseType, ClassLoaderWrapper.GetBootstrapClassLoader()) + { + this.type = type; + } + + public override bool IsInterface + { + get + { + return type.IsInterface; + } + } + + public override TypeWrapper[] Interfaces + { + get + { + if(interfaces == null) + { + // TODO instead of getting the interfaces list from Type, we should use a custom + // attribute to list the implemented interfaces (alternatively, we could check the assembly + // of each interface to make sure it is from an IKVM compiled assembly, but even if the + // interfaces doesn't come from an IKVM assembly we still must call GetWrapperFromTypeFast + // to handle remapped interfaces. + Type[] ifaces = type.GetInterfaces(); + ArrayList wrappers = new ArrayList(); + for(int i = 0; i < ifaces.Length; i++) + { + // HACK if the interface wrapper isn't found, we'll just ignore it (this happens for + // example for Throwable derived classes, which seem to implement ISerializable [because System.Exception does], + // but we cannot find a wrapper for this type) + try + { + wrappers.Add(ClassLoaderWrapper.GetWrapperFromType(ifaces[i])); + } + catch(Exception) + { + } + } + interfaces = (TypeWrapper[])wrappers.ToArray(typeof(TypeWrapper)); + } + return interfaces; + } + } + + // TODO there is an inconsistency here, this method returns regular FieldWrappers for final fields, while + // GetFieldImpl returns a FieldWrapper that is aware of the getter method. Currently this isn't a problem, + // because this method is used for reflection (which doesn't care about accessibility) and GetFieldImpl is used for + // compiled code (which does care about accessibility). + internal override FieldWrapper[] GetFields() + { + ArrayList list = new ArrayList(); + FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + for(int i = 0; i < fields.Length; i++) + { + Modifiers modifiers = ModifiersAttribute.GetModifiers(fields[i]); + if(modifiers != Modifiers.Synthetic) + { + list.Add(CreateFieldWrapper(modifiers, fields[i].Name, fields[i].FieldType, fields[i], null)); + } + } + return (FieldWrapper[])list.ToArray(typeof(FieldWrapper)); + } + + private FieldWrapper CreateFieldWrapper(Modifiers modifiers, string name, Type fieldType, FieldInfo field, MethodInfo getter) + { + FieldWrapper fieldWrapper = new FieldWrapper(this, name, MethodDescriptor.getSigName(fieldType), modifiers); + if((modifiers & Modifiers.Static) != 0) + { + if(getter != null) + { + fieldWrapper.EmitGet = CodeEmitter.Create(OpCodes.Call, getter); + } + else + { + // TODO if field is a literal, we should emit an ldc instead of a ldsfld + fieldWrapper.EmitGet = CodeEmitter.Create(OpCodes.Ldsfld, field); + } + if(field != null) + { + fieldWrapper.EmitSet = CodeEmitter.Create(OpCodes.Stsfld, field); + } + else + { + // TODO what happens when you try to set a final field? + // through reflection: java.lang.IllegalAccessException: Field is final + // through code: java.lang.IllegalAccessError: Field <class>.<field> is final + fieldWrapper.EmitSet = CodeEmitter.Create(OpCodes.Nop); + } + } + else + { + if(getter != null) + { + fieldWrapper.EmitGet = CodeEmitter.Create(OpCodes.Callvirt, getter); + } + else + { + // TODO is it possible to have literal instance fields? + fieldWrapper.EmitGet = CodeEmitter.Create(OpCodes.Ldfld, field); + } + if(field != null) + { + fieldWrapper.EmitSet = CodeEmitter.Create(OpCodes.Stfld, field); + } + else + { + // TODO what happens when you try to set a final field through reflection? + // see above + fieldWrapper.EmitSet = CodeEmitter.Create(OpCodes.Nop); + } + } + return fieldWrapper; + } + + protected override FieldWrapper GetFieldImpl(string fieldName) + { + // TODO this is a crappy implementation, just to get going, but it needs to be revisited + MemberInfo[] members = type.GetMember(fieldName, MemberTypes.Field | MemberTypes.Property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + if(members.Length > 2) + { + throw new NotImplementedException(); + } + if(members.Length == 0) + { + return null; + } + if(members.Length == 2) + { + PropertyInfo prop; + FieldInfo field; + if(members[0] is PropertyInfo && !(members[1] is PropertyInfo)) + { + prop = (PropertyInfo)members[0]; + field = (FieldInfo)members[1]; + } + else if(members[1] is PropertyInfo && !(members[0] is PropertyInfo)) + { + prop = (PropertyInfo)members[1]; + field = (FieldInfo)members[0]; + } + else + { + throw new InvalidOperationException(); + } + Modifiers modifiers = ModifiersAttribute.GetModifiers(field); + MethodInfo getter = prop.GetGetMethod(true); + MethodInfo setter = prop.GetSetMethod(true); + if(getter == null || setter != null) + { + throw new InvalidOperationException(); + } + return CreateFieldWrapper(modifiers, field.Name, field.FieldType, field, getter); + } + else + { + FieldInfo fi = (FieldInfo)members[0]; + return CreateFieldWrapper(ModifiersAttribute.GetModifiers(fi), fi.Name, fi.FieldType, fi, null); + } + } + + // TODO this is broken + internal override MethodWrapper[] GetMethods() + { + ArrayList list = new ArrayList(); + MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + for(int i = 0; i < methods.Length; i++) + { + MethodWrapper mw = CreateMethodWrapper(MethodDescriptor.FromMethodBase(methods[i]), methods[i]); + if(mw != null) + { + list.Add(mw); + } + } + ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + for(int i = 0; i < constructors.Length; i++) + { + MethodWrapper mw = CreateMethodWrapper(MethodDescriptor.FromMethodBase(constructors[i]), constructors[i]); + if(mw != null) + { + list.Add(mw); + } + } + return (MethodWrapper[])list.ToArray(typeof(MethodWrapper)); + } + + private MethodWrapper CreateMethodWrapper(MethodDescriptor md, MethodBase mb) + { + Modifiers modifiers = ModifiersAttribute.GetModifiers(mb); + if(modifiers == Modifiers.Synthetic) + { + return null; + } + MethodWrapper method = new MethodWrapper(this, md, mb, ModifiersAttribute.GetModifiers(mb)); + if(mb is ConstructorInfo) + { + method.EmitCall = CodeEmitter.Create(OpCodes.Call, (ConstructorInfo)mb); + method.EmitNewobj = CodeEmitter.Create(OpCodes.Newobj, (ConstructorInfo)mb); + } + else + { + method.EmitCall = CodeEmitter.Create(OpCodes.Call, (MethodInfo)mb); + if(!mb.IsStatic) + { + method.EmitCallvirt = CodeEmitter.Create(OpCodes.Callvirt, (MethodInfo)mb); + } + } + return method; + } + + protected override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + // TODO this is a crappy implementation, just to get going, but it needs to be revisited + if(md.Name == "<init>") + { + ConstructorInfo ci = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, md.ArgTypes, null); + if(ci != null) + { + return CreateMethodWrapper(md, ci); + } + } + else + { + MethodInfo mi = type.GetMethod(md.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, null, CallingConventions.Standard, md.ArgTypes, null); + if(mi != null) + { + return CreateMethodWrapper(md, mi); + } + } + return null; + } + + public override Type Type + { + get + { + return type; + } + } + + public override void Finish() + { + } +} + +public sealed class JniHack : IJniHack +{ + public override System.Reflection.MethodBase GetMethod(object clazz, string name, string sig, bool isStatic) + { + // TODO this is totally broken, because JNI needs to support redirection + TypeWrapper wrapper = ClassLoaderWrapper.GetWrapperFromType(NativeCode.java.lang.Class.getType(clazz)); + wrapper.Finish(); + MethodDescriptor md = new MethodDescriptor(wrapper.GetClassLoader(), name, sig); + BindingFlags bindings = BindingFlags.Public | BindingFlags.NonPublic; + if(isStatic) + { + bindings |= BindingFlags.Static; + } + else + { + bindings |= BindingFlags.Instance; + } + if(name == "<init>") + { + return wrapper.Type.GetConstructor(bindings, null, md.ArgTypes, null); + } + Type type = wrapper.Type; + while(type != null) + { + MethodInfo m = type.GetMethod(name, bindings, null, CallingConventions.Standard, md.ArgTypes, null); + if(m != null) + { + return m; + } + type = type.BaseType; + } + return null; + } + + public override System.Reflection.FieldInfo GetField(object clazz, string name, string sig, bool isStatic) + { + // TODO this is totally broken, because JNI needs to support redirection + TypeWrapper wrapper = ClassLoaderWrapper.GetWrapperFromType(NativeCode.java.lang.Class.getType(clazz)); + wrapper.Finish(); + MethodDescriptor md = new MethodDescriptor(wrapper.GetClassLoader(), name, sig); + BindingFlags bindings = BindingFlags.Public | BindingFlags.NonPublic; + if(isStatic) + { + bindings |= BindingFlags.Static; + } + else + { + bindings |= BindingFlags.Instance; + } + return wrapper.Type.GetField(name, bindings); + } + + public override object FindClass(string javaName) + { + TypeWrapper wrapper = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName(javaName); + wrapper.Finish(); + return NativeCode.java.lang.Class.getClassFromWrapper(wrapper); + } + + public override Exception UnsatisfiedLinkError(string msg) + { + return JavaException.UnsatisfiedLinkError(msg); + } + + public override object GetClassFromType(Type type) + { + return NativeCode.java.lang.Class.getClassFromType(type); + } +} + +public abstract class CodeEmitter +{ + internal abstract void Emit(ILGenerator ilgen); + + private class ChainCodeEmitter : CodeEmitter + { + private CodeEmitter left; + private CodeEmitter right; + + internal ChainCodeEmitter(CodeEmitter left, CodeEmitter right) + { + this.left = left; + this.right = right; + } + + internal override void Emit(ILGenerator ilgen) + { + left.Emit(ilgen); + right.Emit(ilgen); + } + } + + public static CodeEmitter operator+(CodeEmitter left, CodeEmitter right) + { + if(left == null) + { + return right; + } + return new ChainCodeEmitter(left, right); + } + + private class MethodInfoCodeEmitter : CodeEmitter + { + private OpCode opcode; + private MethodInfo mi; + + internal MethodInfoCodeEmitter(OpCode opcode, MethodInfo mi) + { + this.opcode = opcode; + this.mi = mi; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, mi); + } + } + + internal static CodeEmitter Create(OpCode opcode, MethodInfo mi) + { + return new MethodInfoCodeEmitter(opcode, mi); + } + + private class ConstructorInfoCodeEmitter : CodeEmitter + { + private OpCode opcode; + private ConstructorInfo ci; + + internal ConstructorInfoCodeEmitter(OpCode opcode, ConstructorInfo ci) + { + this.opcode = opcode; + this.ci = ci; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, ci); + } + } + + internal static CodeEmitter Create(OpCode opcode, ConstructorInfo ci) + { + return new ConstructorInfoCodeEmitter(opcode, ci); + } + + private class FieldInfoCodeEmitter : CodeEmitter + { + private OpCode opcode; + private FieldInfo fi; + + internal FieldInfoCodeEmitter(OpCode opcode, FieldInfo fi) + { + this.opcode = opcode; + this.fi = fi; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, fi); + } + } + + internal static CodeEmitter Create(OpCode opcode, FieldInfo fi) + { + return new FieldInfoCodeEmitter(opcode, fi); + } + + private class OpCodeEmitter : CodeEmitter + { + private OpCode opcode; + + internal OpCodeEmitter(OpCode opcode) + { + this.opcode = opcode; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode); + } + } + + internal static CodeEmitter Create(OpCode opcode) + { + return new OpCodeEmitter(opcode); + } + + private class TypeCodeEmitter : CodeEmitter + { + private OpCode opcode; + private Type type; + + internal TypeCodeEmitter(OpCode opcode, Type type) + { + this.opcode = opcode; + this.type = type; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, type); + } + } + + internal static CodeEmitter Create(OpCode opcode, Type type) + { + return new TypeCodeEmitter(opcode, type); + } + + private class IntCodeEmitter : CodeEmitter + { + private OpCode opcode; + private int i; + + internal IntCodeEmitter(OpCode opcode, int i) + { + this.opcode = opcode; + this.i = i; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, i); + } + } + + internal static CodeEmitter Create(OpCode opcode, int i) + { + return new IntCodeEmitter(opcode, i); + } + + private class FloatCodeEmitter : CodeEmitter + { + private OpCode opcode; + private float f; + + internal FloatCodeEmitter(OpCode opcode, float f) + { + this.opcode = opcode; + this.f = f; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, f); + } + } + + internal static CodeEmitter Create(OpCode opcode, float f) + { + return new FloatCodeEmitter(opcode, f); + } + + private class DoubleCodeEmitter : CodeEmitter + { + private OpCode opcode; + private double d; + + internal DoubleCodeEmitter(OpCode opcode, double d) + { + this.opcode = opcode; + this.d = d; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, d); + } + } + + internal static CodeEmitter Create(OpCode opcode, double d) + { + return new DoubleCodeEmitter(opcode, d); + } + + private class StringCodeEmitter : CodeEmitter + { + private OpCode opcode; + private string s; + + internal StringCodeEmitter(OpCode opcode, string s) + { + this.opcode = opcode; + this.s = s; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, s); + } + } + + internal static CodeEmitter Create(OpCode opcode, string s) + { + return new StringCodeEmitter(opcode, s); + } + + private class LongCodeEmitter : CodeEmitter + { + private OpCode opcode; + private long l; + + internal LongCodeEmitter(OpCode opcode, long l) + { + this.opcode = opcode; + this.l = l; + } + + internal override void Emit(ILGenerator ilgen) + { + ilgen.Emit(opcode, l); + } + } + + internal static CodeEmitter Create(OpCode opcode, long l) + { + return new LongCodeEmitter(opcode, l); + } +} + +sealed class MethodWrapper +{ + private TypeWrapper declaringType; + private MethodDescriptor md; + private MethodBase originalMethod; + private Modifiers modifiers; + private bool isRemappedVirtual; + private bool isRemappedOverride; + internal CodeEmitter EmitCall; + internal CodeEmitter EmitCallvirt; + internal CodeEmitter EmitNewobj; + + internal static MethodWrapper Create(TypeWrapper declaringType, MethodDescriptor md, MethodBase originalMethod, MethodBase method, Modifiers modifiers) + { + if(method == null) + { + throw new InvalidOperationException(); + } + MethodWrapper wrapper = new MethodWrapper(declaringType, md, originalMethod, modifiers); + if(method is ConstructorInfo) + { + wrapper.EmitCall = CodeEmitter.Create(OpCodes.Call, (ConstructorInfo)method); + wrapper.EmitCallvirt = null; + wrapper.EmitNewobj = CodeEmitter.Create(OpCodes.Newobj, (ConstructorInfo)method); + } + else + { + wrapper.EmitCall = CodeEmitter.Create(OpCodes.Call, (MethodInfo)method); + if(originalMethod != null && originalMethod != method) + { + // if we're calling a virtual method that is redirected, that overrides an already + // existing method, we have to call it virtually, instead of redirecting + wrapper.EmitCallvirt = CodeEmitter.Create(OpCodes.Callvirt, (MethodInfo)originalMethod); + } + else if(method.IsStatic) + { + // because of redirection, it can be legal to call a static method with invokevirtual + wrapper.EmitCallvirt = CodeEmitter.Create(OpCodes.Call, (MethodInfo)method); + } + else + { + wrapper.EmitCallvirt = CodeEmitter.Create(OpCodes.Callvirt, (MethodInfo)method); + } + wrapper.EmitNewobj = CodeEmitter.Create(OpCodes.Call, (MethodInfo)method); + } + return wrapper; + } + + internal MethodWrapper(TypeWrapper declaringType, MethodDescriptor md, MethodBase originalMethod, Modifiers modifiers) + { + this.declaringType = declaringType; + this.md = md; + // NOTE originalMethod may be null + this.originalMethod = originalMethod; + this.modifiers = modifiers; + } + + internal TypeWrapper DeclaringType + { + get + { + return declaringType; + } + } + + internal MethodDescriptor Descriptor + { + get + { + return md; + } + } + + internal string Name + { + get + { + return md.Name; + } + } + + internal TypeWrapper ReturnType + { + get + { + return declaringType.GetClassLoader().RetTypeWrapperFromSig(md.Signature); + } + } + + internal TypeWrapper[] GetParameters() + { + return declaringType.GetClassLoader().ArgTypeWrapperListFromSig(md.Signature); + } + + internal Modifiers Modifiers + { + get + { + return modifiers; + } + } + + // IsRemappedOverride (only possible for remapped types) indicates whether the method in the + // remapped type overrides (i.e. replaces) the method from the underlying type + // Example: System.Object.ToString() is overriden by java.lang.Object.toString(), + // because java.lang.Object's toString() does something different from + // System.Object's ToString(). + internal bool IsRemappedOverride + { + get + { + return isRemappedOverride; + } + set + { + isRemappedOverride = value; + } + } + + // IsRemappedVirtual (only possible for remapped types) indicates that the method is virtual, + // but doesn't really exist in the underlying type (there is no vtable slot to reuse) + internal bool IsRemappedVirtual + { + get + { + return isRemappedVirtual; + } + set + { + isRemappedVirtual = value; + } + } + + // we expose the underlying MethodBase object, + // for Java types, this is the method that contains the compiled Java bytecode + // for remapped types, this is the method that underlies the remapped method + internal MethodBase GetMethod() + { + return originalMethod; + } + + // this returns the Java method's attributes in .NET terms (e.g. used to create stubs for this method) + internal MethodAttributes GetMethodAttributes() + { + MethodAttributes attribs = 0; + if(IsStatic) + { + attribs |= MethodAttributes.Static; + } + if(IsPublic) + { + attribs |= MethodAttributes.Public; + } + else if(IsPrivate) + { + attribs |= MethodAttributes.Private; + } + else if(IsProtected) + { + attribs |= MethodAttributes.FamORAssem; + } + else + { + attribs |= MethodAttributes.Family; + } + // constructors aren't virtual + if(!IsStatic && !IsPrivate && md.Name != "<init>") + { + attribs |= MethodAttributes.Virtual; + } + if(IsFinal) + { + attribs |= MethodAttributes.Final; + } + if(IsAbstract) + { + attribs |= MethodAttributes.Abstract; + } + return attribs; + } + + internal bool IsStatic + { + get + { + return (modifiers & Modifiers.Static) != 0; + } + } + + internal bool IsPublic + { + get + { + return (modifiers & Modifiers.Public) != 0; + } + } + + internal bool IsPrivate + { + get + { + return (modifiers & Modifiers.Private) != 0; + } + } + + internal bool IsProtected + { + get + { + return (modifiers & Modifiers.Protected) != 0; + } + } + + internal bool IsFinal + { + get + { + return (modifiers & Modifiers.Final) != 0; + } + } + + internal bool IsAbstract + { + get + { + return (modifiers & Modifiers.Abstract) != 0; + } + } +} + +class CastEmitter : CodeEmitter +{ + private Type retType; + private string sig; + + internal CastEmitter(string sig) + { + this.sig = sig; + } + + internal override void Emit(ILGenerator ilgen) + { + if(retType == null) + { + retType = ClassLoaderWrapper.GetBootstrapClassLoader().RetTypeFromSig(sig); + } + ilgen.Emit(OpCodes.Castclass, retType); + } +} + +class VirtualEmitter : CodeEmitter +{ + private MethodDescriptor md; + private RemappedTypeWrapper wrapper; + private MethodInfo method; + + internal VirtualEmitter(MethodDescriptor md, RemappedTypeWrapper wrapper) + { + this.md = md; + this.wrapper = wrapper; + } + + internal override void Emit(ILGenerator ilgen) + { + if(method == null) + { + Type[] args = new Type[md.ArgTypes.Length + 1]; + md.ArgTypes.CopyTo(args, 1); + args[0] = wrapper.Type; + method = wrapper.VirtualsHelperHack.GetMethod(md.Name, BindingFlags.Public | BindingFlags.Static, null, CallingConventions.Standard, args, null); + } + ilgen.Emit(OpCodes.Call, method); + } +} + +sealed class FieldWrapper +{ + private TypeWrapper declaringType; + private string name; + private string sig; + private Modifiers modifiers; + internal CodeEmitter EmitGet; + internal CodeEmitter EmitSet; + + internal FieldWrapper(TypeWrapper declaringType, string name, string sig, Modifiers modifiers) + { + if(modifiers == Modifiers.Synthetic) + { + throw new InvalidOperationException(); + } + this.declaringType = declaringType; + this.name = name; + this.sig = sig; + this.modifiers = modifiers; + } + + internal TypeWrapper DeclaringType + { + get + { + return declaringType; + } + } + + internal string Name + { + get + { + return name; + } + } + + internal Type FieldType + { + get + { + return declaringType.GetClassLoader().ExpressionType(sig); + } + } + + internal Modifiers Modifiers + { + get + { + return modifiers; + } + } + + internal bool IsStatic + { + get + { + return (modifiers & Modifiers.Static) != 0; + } + } + + internal bool IsPublic + { + get + { + return (modifiers & Modifiers.Public) != 0; + } + } + + internal bool IsPrivate + { + get + { + return (modifiers & Modifiers.Private) != 0; + } + } + + internal bool IsProtected + { + get + { + return (modifiers & Modifiers.Protected) != 0; + } + } + + internal bool IsFinal + { + get + { + return (modifiers & Modifiers.Final) != 0; + } + } + + internal bool IsVolatile + { + get + { + return (modifiers & Modifiers.Volatile) != 0; + } + } + + private class VolatileLongDoubleGetter : CodeEmitter + { + private static MethodInfo getFieldFromHandle = typeof(FieldInfo).GetMethod("GetFieldFromHandle"); + private static MethodInfo monitorEnter = typeof(System.Threading.Monitor).GetMethod("Enter"); + private static MethodInfo monitorExit = typeof(System.Threading.Monitor).GetMethod("Exit"); + private FieldInfo fi; + + internal VolatileLongDoubleGetter(FieldInfo fi) + { + this.fi = fi; + } + + internal override void Emit(ILGenerator ilgen) + { + if(!fi.IsStatic) + { + ilgen.Emit(OpCodes.Dup); + Label label = ilgen.DefineLabel(); + ilgen.Emit(OpCodes.Brtrue, label); + ilgen.ThrowException(typeof(NullReferenceException)); + ilgen.MarkLabel(label); + } + // HACK we lock on the FieldInfo object + ilgen.Emit(OpCodes.Ldtoken, fi); + ilgen.Emit(OpCodes.Call, getFieldFromHandle); + ilgen.Emit(OpCodes.Call, monitorEnter); + if(fi.IsStatic) + { + ilgen.Emit(OpCodes.Ldsfld, fi); + } + else + { + ilgen.Emit(OpCodes.Ldfld, fi); + } + ilgen.Emit(OpCodes.Ldtoken, fi); + ilgen.Emit(OpCodes.Call, getFieldFromHandle); + ilgen.Emit(OpCodes.Call, monitorExit); + } + } + + private class VolatileLongDoubleSetter : CodeEmitter + { + private static MethodInfo getFieldFromHandle = typeof(FieldInfo).GetMethod("GetFieldFromHandle"); + private static MethodInfo monitorEnter = typeof(System.Threading.Monitor).GetMethod("Enter"); + private static MethodInfo monitorExit = typeof(System.Threading.Monitor).GetMethod("Exit"); + private FieldInfo fi; + + internal VolatileLongDoubleSetter(FieldInfo fi) + { + this.fi = fi; + } + + internal override void Emit(ILGenerator ilgen) + { + if(!fi.IsStatic) + { + LocalBuilder local = ilgen.DeclareLocal(fi.FieldType); + ilgen.Emit(OpCodes.Stloc, local); + ilgen.Emit(OpCodes.Dup); + Label label = ilgen.DefineLabel(); + ilgen.Emit(OpCodes.Brtrue, label); + ilgen.ThrowException(typeof(NullReferenceException)); + ilgen.MarkLabel(label); + ilgen.Emit(OpCodes.Ldloc, local); + } + // HACK we lock on the FieldInfo object + ilgen.Emit(OpCodes.Ldtoken, fi); + ilgen.Emit(OpCodes.Call, getFieldFromHandle); + ilgen.Emit(OpCodes.Call, monitorEnter); + if(fi.IsStatic) + { + ilgen.Emit(OpCodes.Stsfld, fi); + } + else + { + ilgen.Emit(OpCodes.Stfld, fi); + } + ilgen.Emit(OpCodes.Ldtoken, fi); + ilgen.Emit(OpCodes.Call, getFieldFromHandle); + ilgen.Emit(OpCodes.Call, monitorExit); + } + } + + internal static FieldWrapper Create(TypeWrapper declaringType, FieldInfo fi, string sig, Modifiers modifiers) + { + FieldWrapper field = new FieldWrapper(declaringType, fi.Name, sig, modifiers); + if(field.IsVolatile) + { + // long & double field accesses must be made atomic + if(fi.FieldType == typeof(long) || fi.FieldType == typeof(double)) + { + field.EmitGet = new VolatileLongDoubleGetter(fi); + field.EmitSet = new VolatileLongDoubleSetter(fi); + return field; + } + field.EmitGet += CodeEmitter.Create(OpCodes.Volatile); + field.EmitSet += CodeEmitter.Create(OpCodes.Volatile); + } + if(field.IsStatic) + { + field.EmitGet += CodeEmitter.Create(OpCodes.Ldsfld, fi); + field.EmitSet += CodeEmitter.Create(OpCodes.Stsfld, fi); + } + else + { + field.EmitGet += CodeEmitter.Create(OpCodes.Ldfld, fi); + field.EmitSet += CodeEmitter.Create(OpCodes.Stfld, fi); + } + return field; + } +} diff --git a/IK.VM.NET/WeakHashtable.cs b/IK.VM.NET/WeakHashtable.cs new file mode 100644 index 00000000..0db13142 --- /dev/null +++ b/IK.VM.NET/WeakHashtable.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections; + +// TODO implement this properly, instead of this quick hack +public class WeakHashtable : IDictionary +{ + private struct KeyValue + { + public WeakReference Key; + public object Value; + } + private KeyValue[] items = new KeyValue[101]; + + public WeakHashtable() + { + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + public IDictionaryEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + public void Remove(object key) + { + throw new NotImplementedException(); + } + + public bool Contains(object key) + { + return ContainsKey(key); + } + + public bool ContainsKey(object key) + { + lock(this) + { + int index = FindKey(key); + return index != -1 && items[index].Key != null && items[index].Key.Target != null; + } + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public void Add(object key, object value) + { + lock(this) + { + int index = FindKey(key); + if(index != -1 && items[index].Key != null && items[index].Key.Target != null) + { + throw new ArgumentException(); + } + int newSize = items.Length; + while(index == -1) + { + Rehash(newSize); + newSize = items.Length * 2 - 1; + index = FindKey(key); + } + items[index].Key = new WeakReference(key); + items[index].Value = value; + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + // this returns the slot containing the key, + // the empty slot that should contain the key, or -1 if the + // table is too full to contain the key + private int FindKey(object key) + { + int start = key.GetHashCode() % items.Length; + int end = (start + 5) % items.Length; + for(int index = start; ; index = (index + 1) % items.Length) + { + if(items[index].Key == null) + { + return index; + } + if(key.Equals(items[index].Key.Target)) + { + return index; + } + if(index == end) + { + return -1; + } + } + } + + private void Rehash(int newSize) + { + KeyValue[] curr = items; + restart: + items = new KeyValue[newSize]; + for(int i = 0; i < curr.Length; i++) + { + if(curr[i].Key != null) + { + object key = curr[i].Key.Target; + if(key != null) + { + int index = FindKey(key); + if(index == -1) + { + newSize = newSize * 2 - 1; + goto restart; + } + items[index].Key = new WeakReference(key); + items[index].Value = curr[i].Value; + } + } + } + } + + public object this[object key] + { + get + { + lock(this) + { + int index = FindKey(key); + if(index >= 0) + { + return items[index].Value; + } + return null; + } + } + set + { + lock(this) + { + int index = FindKey(key); + int newSize = items.Length; + while(index == -1) + { + Rehash(newSize); + newSize = items.Length * 2 - 1; + index = FindKey(key); + } + items[index].Key = new WeakReference(key); + items[index].Value = value; + } + } + } + + public ICollection Values + { + get + { + throw new NotImplementedException(); + } + } + + public ICollection Keys + { + get + { + throw new NotImplementedException(); + } + } + + public bool IsFixedSize + { + get + { + return false; + } + } + + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + public bool IsSynchronized + { + get + { + return true; + } + } + + public int Count + { + get + { + throw new NotImplementedException(); + } + } + + public object SyncRoot + { + get + { + return this; + } + } +} diff --git a/IK.VM.NET/attributes.cs b/IK.VM.NET/attributes.cs new file mode 100644 index 00000000..93d7b7dd --- /dev/null +++ b/IK.VM.NET/attributes.cs @@ -0,0 +1,260 @@ +/* + 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 + +*/ +using System; +using System.Reflection; +using System.Reflection.Emit; + +[AttributeUsage(AttributeTargets.Class)] +public class OverrideStubTypeAttribute : Attribute +{ +} + +[AttributeUsage(AttributeTargets.Assembly)] +public class IKVMAssemblyAttribute : Attribute +{ +} + +[AttributeUsage(AttributeTargets.All)] +public class ModifiersAttribute : Attribute +{ + private Modifiers modifiers; + + public ModifiersAttribute(Modifiers modifiers) + { + this.modifiers = modifiers; + } + + public bool IsSynthetic + { + get + { + return modifiers == Modifiers.Synthetic; + } + } + + public Modifiers Modifiers + { + get + { + return modifiers; + } + } + + public static Modifiers GetModifiers(MethodBase mb) + { + object[] customAttribute = mb.GetCustomAttributes(typeof(ModifiersAttribute), false); + if(customAttribute.Length == 1) + { + return ((ModifiersAttribute)customAttribute[0]).Modifiers; + } + Modifiers modifiers = 0; + if(mb.IsPublic) + { + modifiers |= Modifiers.Public; + } + if(mb.IsPrivate) + { + modifiers |= Modifiers.Private; + } + if(mb.IsFamily || mb.IsFamilyOrAssembly) + { + modifiers |= Modifiers.Protected; + } + // NOTE Java doesn't support non-virtual methods, but we set the Final modifier for + // non-virtual methods to approximate the semantics + if(mb.IsFinal || (!mb.IsStatic && !mb.IsVirtual)) + { + modifiers |= Modifiers.Final; + } + if(mb.IsAbstract) + { + modifiers |= Modifiers.Abstract; + } + if(mb.IsStatic) + { + modifiers |= Modifiers.Static; + } + if((mb.GetMethodImplementationFlags() & MethodImplAttributes.Synchronized) != 0) + { + modifiers |= Modifiers.Synchronized; + } + if((mb.Attributes & MethodAttributes.PinvokeImpl) != 0) + { + modifiers |= Modifiers.Native; + } + return modifiers; + } + + public static Modifiers GetModifiers(FieldInfo fi) + { + object[] customAttribute = fi.GetCustomAttributes(typeof(ModifiersAttribute), false); + if(customAttribute.Length == 1) + { + return ((ModifiersAttribute)customAttribute[0]).Modifiers; + } + // NOTE privatescope fields are always treated as synthetic (even when they are in a .NET type, because + // Java wouldn't be able to cope with them anyway, because of potential name clashes) + if((fi.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.PrivateScope) + { + return Modifiers.Synthetic; + } + Modifiers modifiers = 0; + if(fi.IsPublic) + { + modifiers |= Modifiers.Public; + } + if(fi.IsPrivate) + { + modifiers |= Modifiers.Private; + } + if(fi.IsFamily || fi.IsFamilyOrAssembly) + { + modifiers |= Modifiers.Protected; + } + if(fi.IsInitOnly || fi.IsLiteral) + { + modifiers |= Modifiers.Final; + } + if(fi.IsNotSerialized) + { + modifiers |= Modifiers.Transient; + } + if(fi.IsStatic) + { + modifiers |= Modifiers.Static; + } + // TODO reflection doesn't support volatile + return modifiers; + } + + public static Modifiers GetModifiers(Type type) + { + object[] customAttribute = type.GetCustomAttributes(typeof(ModifiersAttribute), false); + if(customAttribute.Length == 1) + { + return ((ModifiersAttribute)customAttribute[0]).Modifiers; + } + // only returns public, protected, private, final, static, abstract and interface (as per + // the documentation of Class.getModifiers()) + Modifiers modifiers = 0; + if(type.IsPublic) + { + modifiers |= Modifiers.Public; + } + if(type.IsNestedPrivate) + { + modifiers |= Modifiers.Private; + } + if(type.IsNestedFamily || type.IsNestedFamORAssem) + { + modifiers |= Modifiers.Protected; + } + if(type.IsSealed) + { + modifiers |= Modifiers.Final; + } + if(type.DeclaringType != null) + { + modifiers |= Modifiers.Static; + } + if(type.IsAbstract) + { + modifiers |= Modifiers.Abstract; + } + if(type.IsInterface) + { + modifiers |= Modifiers.Interface; + } + return modifiers; + } + + public static void SetModifiers(MethodBuilder mb, Modifiers modifiers) + { + CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(typeof(ModifiersAttribute).GetConstructor(new Type[] { typeof(Modifiers) }), new object[] { modifiers }); + mb.SetCustomAttribute(customAttributeBuilder); + } + + public static void SetModifiers(FieldBuilder fb, Modifiers modifiers) + { + CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(typeof(ModifiersAttribute).GetConstructor(new Type[] { typeof(Modifiers) }), new object[] { modifiers }); + fb.SetCustomAttribute(customAttributeBuilder); + } +} + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property | AttributeTargets.Class)] +public class StackTraceInfoAttribute : Attribute +{ + private bool hidden; + private string className; + private bool truncate; + private int eatFrames; + + public bool Hidden + { + get + { + return hidden; + } + set + { + hidden = value; + } + } + + public int EatFrames + { + get + { + return eatFrames; + } + set + { + eatFrames = value; + } + } + + public string Class + { + get + { + return className; + } + set + { + className = value; + } + } + + public bool Truncate + { + get + { + return truncate; + } + set + { + truncate = value; + } + } +} diff --git a/IK.VM.NET/classpath.cs b/IK.VM.NET/classpath.cs new file mode 100644 index 00000000..86667cb3 --- /dev/null +++ b/IK.VM.NET/classpath.cs @@ -0,0 +1,1636 @@ +/* + 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 + +*/ +using System; +using System.Collections; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Text; +using NetSystem = System; + +namespace java.lang +{ + public interface Cloneable + { + } + + // TODO should be serializable + public sealed class StackTraceElement + { + private static readonly long serialVersionUID = 6992337162326171013L; + private string fileName; + private int lineNumber; + private string className; + private string methodName; + [NonSerialized] + private bool isNative; + + internal long bogus_method_to_prevent_warning() + { + return serialVersionUID; + } + + internal StackTraceElement(string fileName, int lineNumber, string className, string methodName, bool isNative) + { + this.fileName = fileName; + this.lineNumber = lineNumber; + this.className = className; + this.methodName = methodName; + this.isNative = isNative; + } + + public string getFileName() + { + return fileName; + } + + public int getLineNumber() + { + return lineNumber; + } + + public string getClassName() + { + return className; + } + + public string getMethodName() + { + return methodName; + } + + public bool isNativeMethod() + { + return isNative; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + if(className != null) + { + sb.Append(className); + if(methodName != null) + { + sb.Append('.'); + } + } + if(methodName != null) + { + sb.Append(methodName); + } + sb.Append('('); + if(fileName != null) + { + sb.Append(fileName); + } + else + { + sb.Append(isNative ? "Native Method" : "Unknown Source"); + } + if(lineNumber >= 0) + { + sb.Append(':').Append(lineNumber); + } + sb.Append(')'); + return sb.ToString(); + } + + public override bool Equals(object o) + { + if(o != null && o.GetType() == GetType()) + { + StackTraceElement ste = (StackTraceElement)o; + return ste.className == className && + ste.fileName == fileName && + ste.lineNumber == lineNumber && + ste.methodName == methodName; + } + return false; + } + + public override int GetHashCode() + { + return GetHashCode(className) ^ GetHashCode(fileName) ^ GetHashCode(methodName) ^ lineNumber; + } + + private static int GetHashCode(string o) + { + if(o == null) + { + return 0; + } + return o.GetHashCode(); + } + } +} + +namespace java.io +{ + public interface Serializable + { + } +} + +namespace NativeCode.java +{ + namespace lang + { + namespace reflect + { + public class Array + { + public static object createObjectArray(object clazz, int dim) + { + if(dim >= 0) + { + return NetSystem.Array.CreateInstance(Class.getType(clazz), dim); + } + throw JavaException.NegativeArraySizeException(); + } + } + + public class Method + { + public static String GetName(object methodCookie) + { + MethodWrapper wrapper = (MethodWrapper)methodCookie; + return wrapper.Name; + } + + public static int GetModifiers(object methodCookie) + { + MethodWrapper wrapper = (MethodWrapper)methodCookie; + return (int)wrapper.Modifiers; + } + + public static object GetReturnType(object methodCookie) + { + MethodWrapper wrapper = (MethodWrapper)methodCookie; + return Class.getClassFromWrapper(wrapper.ReturnType); + } + + public static object[] GetParameterTypes(object methodCookie) + { + MethodWrapper wrapper = (MethodWrapper)methodCookie; + TypeWrapper[] parameters = wrapper.GetParameters(); + object[] parameterClasses = new object[parameters.Length]; + for(int i = 0; i < parameters.Length; i++) + { + parameterClasses[i] = Class.getClassFromWrapper(parameters[i]); + } + return parameterClasses; + } + + public static object[] GetExceptionTypes(object methodCookie) + { + MethodWrapper wrapper = (MethodWrapper)methodCookie; + // TODO + return new object[0]; + } + + public static object Invoke(object methodCookie, object o, object[] args) + { + // TODO this is a very lame implementation, no where near correct + MethodWrapper wrapper = (MethodWrapper)methodCookie; + wrapper.DeclaringType.Finish(); + TypeWrapper[] argWrappers = wrapper.GetParameters(); + Type[] argTypes = new Type[argWrappers.Length]; + for(int i = 0; i < argTypes.Length; i++) + { + argWrappers[i].Finish(); + argTypes[i] = argWrappers[i].Type; + if(argTypes[i].IsPrimitive) + { + if(argTypes[i] == typeof(int)) + { + args[i] = ClassLoaderWrapper.GetType("java.lang.Integer").GetMethod("intValue").Invoke(args[i], new object[0]); + } + else if(argTypes[i] == typeof(bool)) + { + args[i] = ClassLoaderWrapper.GetType("java.lang.Boolean").GetMethod("booleanValue").Invoke(args[i], new object[0]); + } + else if(argTypes[i] == typeof(short)) + { + args[i] = ClassLoaderWrapper.GetType("java.lang.Short").GetMethod("shortValue").Invoke(args[i], new object[0]); + } + else + { + throw new NotImplementedException("argtype: " + argTypes[i].FullName); + } + } + } + try + { + if(wrapper.Name == "<init>") + { + if(o == null) + { + return wrapper.DeclaringType.Type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, argTypes, null).Invoke(args); + } + else + { + throw new NotImplementedException("invoking constructor on existing instance"); + } + } + else + { + MethodInfo mi; + if(wrapper.GetMethod() is NetSystem.Reflection.Emit.MethodBuilder || wrapper.GetMethod() == null) + { + mi = wrapper.DeclaringType.Type.GetMethod(wrapper.Name, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, argTypes, null); + } + else + { + mi = (MethodInfo)wrapper.GetMethod(); + } + if(mi == null) + { + throw new InvalidOperationException("Method not found: " + wrapper.DeclaringType.Name + "." + wrapper.Name + wrapper.Descriptor.Signature); + } + object retval = mi.Invoke(o, args); + if(wrapper.ReturnType.Type.IsValueType) + { + if(wrapper.ReturnType.Type == typeof(int)) + { + retval = Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.Integer"), new object[] { (int)retval }); + } + else if(wrapper.ReturnType.Type == typeof(bool)) + { + retval = Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.Boolean"), new object[] { (bool)retval }); + } + else if(wrapper.ReturnType.Type == typeof(void)) + { + // nothing to do + } + else + { + throw new NotImplementedException("rettype: " + wrapper.ReturnType.Type.FullName); + } + } + return retval; + } + } + catch(TargetInvocationException x) + { + throw JavaException.InvocationTargetException(ExceptionHelper.MapException(x.InnerException, typeof(Exception))); + } + } + + // TODO remove this, it isn't used anymore + public static Exception mapException(Exception x) + { + return ExceptionHelper.MapException(x, typeof(Exception)); + } + } + + public class Field + { + public static string GetName(object fieldCookie) + { + FieldWrapper wrapper = (FieldWrapper)fieldCookie; + return wrapper.Name; + } + + public static int GetModifiers(object fieldCookie) + { + FieldWrapper wrapper = (FieldWrapper)fieldCookie; + return (int)wrapper.Modifiers; + } + + public static object GetFieldType(object fieldCookie) + { + FieldWrapper wrapper = (FieldWrapper)fieldCookie; + return Class.getClassFromType(wrapper.FieldType); + } + + public static object GetValue(object fieldCookie, object o) + { + FieldWrapper wrapper = (FieldWrapper)fieldCookie; + wrapper.DeclaringType.Finish(); + FieldInfo fi = wrapper.DeclaringType.Type.GetField(wrapper.Name, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if(fi.FieldType.IsValueType) + { + if(fi.FieldType == typeof(long)) + { + return Activator.CreateInstance(ClassLoaderWrapper.GetType("java.lang.Long"), new object[] { fi.GetValue(o) }); + } + else + { + throw new NotImplementedException("GetValue: " + fi.FieldType.FullName); + } + } + return fi.GetValue(o); + } + + public static void SetValue(object fieldCookie, object o, object v) + { + // TODO this is a very lame implementation, no where near correct + FieldWrapper wrapper = (FieldWrapper)fieldCookie; + wrapper.DeclaringType.Finish(); + FieldInfo fi = wrapper.DeclaringType.Type.GetField(wrapper.Name, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if(fi.FieldType.IsValueType) + { + throw new NotImplementedException("SetValue: " + fi.FieldType.FullName); + } + fi.SetValue(o, v); + } + } + } + + public class Runtime + { + public static void insertSystemProperties(object properties) + { + MethodInfo m = properties.GetType().GetMethod("setProperty"); + // TODO set all these properties to something useful + m.Invoke(properties, new string[] { "java.version", "1.1" }); + m.Invoke(properties, new string[] { "java.vendor", "Jeroen Frijters" }); + m.Invoke(properties, new string[] { "java.vendor.url", "http://jeroen.nu/" }); + m.Invoke(properties, new string[] { "java.home", "" }); + m.Invoke(properties, new string[] { "java.vm.specification.version", "" }); + m.Invoke(properties, new string[] { "java.vm.specification.vendor", "" }); + m.Invoke(properties, new string[] { "java.vm.specification.name", "" }); + m.Invoke(properties, new string[] { "java.vm.version", "" }); + m.Invoke(properties, new string[] { "java.vm.vendor", "" }); + m.Invoke(properties, new string[] { "java.vm.name", "" }); + m.Invoke(properties, new string[] { "java.specification.version", "" }); + m.Invoke(properties, new string[] { "java.specification.vendor", "" }); + m.Invoke(properties, new string[] { "java.specification.name", "" }); + m.Invoke(properties, new string[] { "java.class.version", "" }); + string classpath = Environment.GetEnvironmentVariable("CLASSPATH"); + if(classpath == null) + { + classpath = "."; + } + m.Invoke(properties, new string[] { "java.class.path", classpath }); + m.Invoke(properties, new string[] { "java.library.path", "." }); + m.Invoke(properties, new string[] { "java.io.tmpdir", Path.GetTempPath() }); + m.Invoke(properties, new string[] { "java.compiler", "" }); + m.Invoke(properties, new string[] { "java.ext.dirs", "" }); + m.Invoke(properties, new string[] { "os.name", "Windows" }); + m.Invoke(properties, new string[] { "os.arch", "" }); + m.Invoke(properties, new string[] { "os.version", Environment.OSVersion.ToString() }); + m.Invoke(properties, new string[] { "file.separator", Path.DirectorySeparatorChar.ToString() }); + m.Invoke(properties, new string[] { "path.separator", Path.PathSeparator.ToString() }); + m.Invoke(properties, new string[] { "line.separator", Environment.NewLine }); + m.Invoke(properties, new string[] { "user.name", Environment.UserName }); + m.Invoke(properties, new string[] { "user.home", "" }); + m.Invoke(properties, new string[] { "user.dir", Environment.CurrentDirectory }); + m.Invoke(properties, new string[] { "awt.toolkit", "ikvm.awt.NetToolkit, awt, Version=1.0, Culture=neutral, PublicKeyToken=null" }); + } + + public static string nativeGetLibname(string pathname, string libname) + { + // TODO + return libname; + } + + public static int nativeLoad(object obj, string filename) + { + return JNI.LoadNativeLibrary(filename); + } + + public static void gc(object obj) + { + NetSystem.GC.Collect(); + } + + public static void runFinalization(object obj) + { + NetSystem.GC.WaitForPendingFinalizers(); + } + + public static void runFinalizersOnExitInternal(bool b) + { + // the CLR always runs the finalizers, so we can ignore this + } + + public static void exitInternal(object obj, int rc) + { + NetSystem.Environment.Exit(rc); + } + + public static int availableProcessors(object obj) + { + string s = NetSystem.Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS"); + if(s != null) + { + return int.Parse(s); + } + return 1; + } + + public static object execInternal(object obj, string[] cmd, string[] env, object dir) + { + // TODO + throw new NotImplementedException(); + } + + public static long freeMemory(object obj) + { + // TODO figure out if there is anything meaningful we can return here + return 10 * 1024 * 1024; + } + + public static long maxMemory(object obj) + { + return long.MaxValue; + } + + public static long totalMemory(object obj) + { + // NOTE this really is a bogus number, but we have to return something + return NetSystem.GC.GetTotalMemory(false); + } + + public static void traceInstructions(object obj, bool b) + { + // not supported + } + + public static void traceMethodCalls(object obj, bool b) + { + // not supported + } + } + + public class System + { + private static long timebase; + + static System() + { + timebase = ((TimeZone.CurrentTimeZone.ToUniversalTime(DateTime.Now) - new DateTime(1970, 1, 1)).Ticks / 10000L) - Environment.TickCount; + } + + public static bool isWordsBigEndian() + { + return !BitConverter.IsLittleEndian; + } + + public static long currentTimeMillis() + { + // NOTE this wraps after 24.9 days, but it is much faster than calling DateTime.Now every time + return timebase + Environment.TickCount; + } + + public static void setErr0(object printStream) + { + ClassLoaderWrapper.GetType("java.lang.System").GetField("err", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, printStream); + } + + public static void setIn0(object inputStream) + { + ClassLoaderWrapper.GetType("java.lang.System").GetField("in", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, inputStream); + } + + public static void setOut0(object printStream) + { + ClassLoaderWrapper.GetType("java.lang.System").GetField("out", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, printStream); + } + } + + public class Math + { + public static double pow(double x, double y) + { + return NetSystem.Math.Pow(x, y); + } + + public static double exp(double d) + { + return NetSystem.Math.Exp(d); + } + + public static double rint(double d) + { + return NetSystem.Math.Round(d); + } + + public static double IEEEremainder(double f1, double f2) + { + return NetSystem.Math.IEEERemainder(f1, f2); + } + + public static double sqrt(double d) + { + return NetSystem.Math.Sqrt(d); + } + + public static double floor(double d) + { + return NetSystem.Math.Floor(d); + } + + public static double ceil(double d) + { + return NetSystem.Math.Ceiling(d); + } + + public static double log(double d) + { + return NetSystem.Math.Log(d); + } + + public static double sin(double d) + { + return NetSystem.Math.Sin(d); + } + + public static double asin(double d) + { + return NetSystem.Math.Asin(d); + } + + public static double cos(double d) + { + return NetSystem.Math.Cos(d); + } + + public static double acos(double d) + { + return NetSystem.Math.Acos(d); + } + + public static double tan(double d) + { + return NetSystem.Math.Tan(d); + } + + public static double atan(double d) + { + return NetSystem.Math.Atan(d); + } + + public static double atan2(double y, double x) + { + return NetSystem.Math.Atan2(y, x); + } + } + + public class Double + { + public static void initIDs() + { + } + + public static double parseDouble(string s) + { + // TODO I doubt that this is correct + return double.Parse(s); + } + + public static long doubleToLongBits(double v) + { + if(double.IsNaN(v)) + { + return 0x7ff8000000000000L; + } + return BitConverter.DoubleToInt64Bits(v); + } + + public static long doubleToRawLongBits(double v) + { + return BitConverter.DoubleToInt64Bits(v); + } + + public static double longBitsToDouble(long bits) + { + return BitConverter.Int64BitsToDouble(bits); + } + + public static string toString(double d, bool isFloat) + { + return isFloat ? StringHelper.valueOf((float)d) : StringHelper.valueOf(d); + } + } + + public class Float + { + public static float intBitsToFloat(int v) + { + return BitConverter.ToSingle(BitConverter.GetBytes(v), 0); + } + + public static int floatToIntBits(float v) + { + if(float.IsNaN(v)) + { + return 0x7fc00000; + } + return BitConverter.ToInt32(BitConverter.GetBytes(v), 0); + } + + public static int floatToRawIntBits(float v) + { + return BitConverter.ToInt32(BitConverter.GetBytes(v), 0); + } + } + + public class VMSecurityManager + { + public static object getClassContext() + { + ArrayList ar = new ArrayList(); + NetSystem.Diagnostics.StackTrace st = new NetSystem.Diagnostics.StackTrace(); + for(int i = 0; i < st.FrameCount; i++) + { + NetSystem.Diagnostics.StackFrame frame = st.GetFrame(i); + // HACK very insecure + // TODO handle reflection scenario + if(frame.GetMethod().Name != "getClassContext") + { + ar.Add(Class.getClassFromType(frame.GetMethod().DeclaringType)); + } + } + return ar.ToArray(ClassLoaderWrapper.GetType("java.lang.Class")); + } + + public static object currentClassLoader() + { + // TODO handle PrivilegedAction + NetSystem.Diagnostics.StackTrace st = new NetSystem.Diagnostics.StackTrace(); + for(int i = 0; i < st.FrameCount; i++) + { + NetSystem.Diagnostics.StackFrame frame = st.GetFrame(i); + TypeWrapper wrapper = ClassLoaderWrapper.GetWrapperFromTypeFast(frame.GetMethod().DeclaringType); + if(wrapper != null && wrapper.GetClassLoader().GetJavaClassLoader() != null) + { + return wrapper.GetClassLoader().GetJavaClassLoader(); + } + } + return null; + } + } + + public class VMSystem + { + public static void arraycopy(object src, int srcStart, object dest, int destStart, int len) + { + // TODO + Array.Copy((Array)src, srcStart, (Array)dest, destStart, len); + } + + private static MethodInfo hashCodeMethod; + private static object[] noargs = new object[0]; + + public static int identityHashCode(object arg) + { + if(arg == null) + { + return 0; + } + // TODO this should be optimized, using reflection is probably too slow + if(hashCodeMethod == null) + { + hashCodeMethod = typeof(object).GetMethod("GetHashCode"); + } + return (int)hashCodeMethod.Invoke(arg, noargs); + } + } + + public class VMClassLoader + { + public static Assembly findResourceAssembly(string name) + { + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + for(int i = 0; i < assemblies.Length; i++) + { + if(!(assemblies[i] is NetSystem.Reflection.Emit.AssemblyBuilder)) + { + if(assemblies[i].GetLoadedModules()[0].GetField(name) != null) + { + return assemblies[i]; + } + } + } + return null; + } + + public static Type getPrimitiveType(char type) + { + switch(type) + { + case 'Z': + return typeof(bool); + case 'B': + return typeof(sbyte); + case 'C': + return typeof(char); + case 'D': + return typeof(double); + case 'F': + return typeof(float); + case 'I': + return typeof(int); + case 'J': + return typeof(long); + case 'S': + return typeof(short); + case 'V': + return typeof(void); + default: + throw new InvalidOperationException(); + } + } + + public static object defineClass(object classLoader, string name, byte[] data, int offset, int length, object protectionDomain) + { + // TODO handle errors + ClassFile classFile = new ClassFile(data, offset, length, name); + if(name != null && classFile.Name.Replace('/', '.') != name) + { + throw JavaException.NoClassDefFoundError("{0} (wrong name: {1})", name, classFile.Name); + } +// if(classFile.Name == "org/eclipse/core/internal/boot/InternalBootLoader") +// { +// using(FileStream fs = File.Create("internalbootloader.class")) +// { +// fs.Write(data, offset, length); +// } +// } + TypeWrapper type = ClassLoaderWrapper.GetClassLoaderWrapper(classLoader).DefineClass(classFile); + object clazz = Class.CreateInstance(null, type); + if(protectionDomain != null) + { + // TODO cache the FieldInfo + clazz.GetType().GetField("pd", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(clazz, protectionDomain); + } + return clazz; + } + } + + public class Thread + { + public static void sleep(long millis, int nanos) + { + NetSystem.Threading.Thread.Sleep(new TimeSpan(millis * 10000 + (nanos + 99) / 100)); + } + + public static void joinInternal(NetSystem.Threading.Thread nativeThread, long millis, int nanos) + { + nativeThread.Join(new TimeSpan(millis * 10000 + (nanos + 99) / 100)); + } + } + + public class Class + { + private static Hashtable map = new Hashtable(); + private static ConstructorInfo classConstructor; + private static MethodInfo getTypeMethod; + + public static object loadBootstrapClass(string name, bool initialize) + { + TypeWrapper type = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedName(name); + type.Finish(); + if(initialize) + { + RuntimeHelpers.RunClassConstructor(type.Type.TypeHandle); + } + return getClassFromType(type.Type); + } + + internal static object CreateInstance(Type type, TypeWrapper wrapper) + { + if(classConstructor == null) + { + classConstructor = ClassLoaderWrapper.GetType("java.lang.Class").GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard, new Type[] { typeof(Type), typeof(object) }, null); + } + object clazz = classConstructor.Invoke(new object[] { type, wrapper }); + lock(map.SyncRoot) + { + if(type != null) + { + map.Add(type, clazz); + } + if(wrapper != null) + { + map.Add(wrapper, clazz); + } + } + return clazz; + } + + public static Type getTypeFromWrapper(object clazz, object wrapper) + { + ((TypeWrapper)wrapper).Finish(); + Type type = ((TypeWrapper)wrapper).Type; + lock(map.SyncRoot) + { + // NOTE since this method can be called multiple times (or after getClassFromType has added + // the Class to the map), we don't use Add() here, but the indexer because that can handle + // "overwriting" the existing association (which should always be the same as the new one) + map[type] = clazz; + } + return type; + } + + public static Type getType(object clazz) + { + if(getTypeMethod == null) + { + getTypeMethod = ClassLoaderWrapper.GetType("java.lang.Class").GetMethod("getType", BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); + } + return (Type)getTypeMethod.Invoke(clazz, new object[0]); + } + + internal static object getClassFromWrapper(TypeWrapper wrapper) + { + lock(map.SyncRoot) + { + object clazz = map[wrapper]; + if(clazz == null) + { + // Maybe the Class object was already constructed from the type + clazz = map[wrapper.Type]; + if(clazz == null) + { + clazz = CreateInstance(null, wrapper); + } + } + return clazz; + } + } + + public static object getClassFromType(Type type) + { + if(type == null) + { + return null; + } + if(type is NetSystem.Reflection.Emit.TypeBuilder) + { + throw new InvalidOperationException(); + } + lock(map.SyncRoot) + { + object clazz = map[type]; + if(clazz == null) + { + // maybe the Class object was constructed from the wrapper + TypeWrapper wrapper = ClassLoaderWrapper.GetWrapperFromTypeFast(type); + if(wrapper != null) + { + clazz = map[wrapper]; + if(clazz != null) + { + map.Add(type, clazz); + return clazz; + } + } + // NOTE we need to get the bootstrap classloader to trigger its construction (if it + // hasn't been created yet), because otherwise CreateInstance will do that and this + // causes the same class object to be created multiple times) + ClassLoaderWrapper.GetBootstrapClassLoader(); + clazz = map[type]; + if(clazz == null) + { + // if this type is an override stub (e.g. java.lang.Object), we need to return the + // class object for the parent type + if(type.IsDefined(typeof(OverrideStubTypeAttribute), false)) + { + clazz = getClassFromType(type.BaseType); + map.Add(type, clazz); + } + else + { + // TODO should we specify the wrapper? + // NOTE CreateInstance adds the Class to the "map" + clazz = CreateInstance(type, null); + } + } + } + return clazz; + } + } + + public static string getName(Type type) + { + return GetName(type, null); + } + + public static string GetName(Type type, object wrapperType) + { + if(type == null) + { + string name = ((TypeWrapper)wrapperType).Name; + // HACK name is null for primitives + if(name != null) + { + return name.Replace('/', '.'); + } + type = ((TypeWrapper)wrapperType).Type; + } + if(type.IsValueType) + { + if(type == typeof(void)) + { + return "void"; + } + else if(type == typeof(bool)) + { + return "boolean"; + } + else if(type == typeof(sbyte)) + { + return "byte"; + } + else if(type == typeof(char)) + { + return "char"; + } + else if(type == typeof(short)) + { + return "short"; + } + else if(type == typeof(int)) + { + return "int"; + } + else if(type == typeof(long)) + { + return "long"; + } + else if(type == typeof(float)) + { + return "float"; + } + else if(type == typeof(double)) + { + return "double"; + } + else + { + return type.FullName; + } + } + else if(type.IsArray) + { + StringBuilder sb = new StringBuilder(); + while(type.IsArray) + { + sb.Append('['); + type = type.GetElementType(); + } + if(type.IsValueType) + { + if(type == typeof(void)) + { + sb.Append('V'); + } + else if(type == typeof(bool)) + { + sb.Append('Z'); + } + else if(type == typeof(sbyte)) + { + sb.Append('B'); + } + else if(type == typeof(char)) + { + sb.Append('C'); + } + else if(type == typeof(short)) + { + sb.Append('S'); + } + else if(type == typeof(int)) + { + sb.Append('I'); + } + else if(type == typeof(long)) + { + sb.Append('J'); + } + else if(type == typeof(float)) + { + sb.Append('F'); + } + else if(type == typeof(double)) + { + sb.Append('D'); + } + else + { + sb.Append(type.FullName); + } + } + else + { + sb.Append('L').Append(GetName(type, null)).Append(';'); + } + return sb.ToString(); + } + else + { + while(type.IsDefined(typeof(OverrideStubTypeAttribute), false)) + { + type = type.BaseType; + } + // TODO look for our custom attribute (which doesn't exist yet), that contains the real name of the type + TypeWrapper wrapper = ClassLoaderWrapper.GetWrapperFromTypeFast(type); + if(wrapper != null) + { + return wrapper.Name.Replace('/', '.'); + } + return type.FullName; + } + } + + [StackTraceInfo(Hidden = true)] + public static void initializeType(Type type) + { + RuntimeHelpers.RunClassConstructor(type.TypeHandle); + } + + public static object getClassLoader0(Type type) + { + return ClassLoaderWrapper.GetClassLoader(type).GetJavaClassLoader(); + } + + public static object[] GetDeclaredMethods(Type type, object cwrapper) + { + TypeWrapper wrapper = (TypeWrapper)cwrapper; + if(wrapper == null) + { + wrapper = ClassLoaderWrapper.GetWrapperFromType(type); + } + // we need to finish the type otherwise all methods will not be in the method map yet + wrapper.Finish(); + return wrapper.GetMethods(); + } + + public static object[] GetDeclaredFields(Type type, object cwrapper) + { + TypeWrapper wrapper = (TypeWrapper)cwrapper; + if(wrapper == null) + { + wrapper = ClassLoaderWrapper.GetWrapperFromType(type); + } + // we need to finish the type otherwise all fields will not be in the field map yet + wrapper.Finish(); + return wrapper.GetFields(); + } + + public static object[] GetDeclaredClasses(Type type, object cwrapper) + { + // TODO + return new object[0]; + } + + public static object[] GetInterfaces(Type type, object cwrapper) + { + TypeWrapper wrapper = (TypeWrapper)cwrapper; + if(wrapper == null) + { + wrapper = ClassLoaderWrapper.GetWrapperFromType(type); + } + // we need to finish the type otherwise all fields will not be in the field map yet + wrapper.Finish(); + TypeWrapper[] interfaceWrappers = wrapper.Interfaces; + object[] interfaces = new object[interfaceWrappers.Length]; + for(int i = 0; i < interfaces.Length; i++) + { + interfaces[i] = getClassFromWrapper(interfaceWrappers[i]); + } + return interfaces; + } + + public static int GetModifiers(Type type, Object cwrapper) + { + TypeWrapper wrapper = (TypeWrapper)cwrapper; + if(wrapper == null) + { + wrapper = ClassLoaderWrapper.GetWrapperFromType(type); + } + // only returns public, protected, private, final, static, abstract and interface (as per + // the documentation of Class.getModifiers()) + Modifiers mask = Modifiers.Public | Modifiers.Protected | Modifiers.Private | Modifiers.Final | + Modifiers.Static | Modifiers.Abstract | Modifiers.Interface; + return (int)(wrapper.Modifiers & mask); + } + } + } + + namespace io + { + public class File + { + internal static string DemanglePath(string path) + { + // HACK for some reason Java accepts: \c:\foo.txt + // I don't know what else, but for now lets just support this + if(path.Length > 3 && path[0] == '\\' && path[2] == ':') + { + path = path.Substring(1); + } + return path; + } + + public static bool existsInternal(object obj, string path) + { + path = DemanglePath(path); + try + { + return NetSystem.IO.File.Exists(path) || NetSystem.IO.Directory.Exists(path); + } + catch(Exception) + { + return false; + } + } + + public static bool isFileInternal(object obj, string path) + { + // TODO handle errors + // TODO make sure semantics are the same + try + { + return NetSystem.IO.File.Exists(DemanglePath(path)); + } + catch(Exception) + { + return false; + } + } + + public static bool isDirectoryInternal(object obj, string path) + { + // TODO handle errors + // TODO make sure semantics are the same + try + { + return NetSystem.IO.Directory.Exists(DemanglePath(path)); + } + catch(Exception) + { + return false; + } + } + + public static long lengthInternal(object obj, string path) + { + // TODO handle errors + try + { + return new NetSystem.IO.FileInfo(DemanglePath(path)).Length; + } + catch(Exception) + { + return 0; + } + } + + public static bool mkdirInternal(object obj, string path) + { + // TODO handle errors + // TODO shouldn't we demangle the path? + if (!NetSystem.IO.Directory.Exists(NetSystem.IO.Directory.GetParent(path).FullName) || + NetSystem.IO.Directory.Exists(path)) + { + return false; + } + return NetSystem.IO.Directory.CreateDirectory(path) != null; + } + + public static bool deleteInternal(object obj, string path) + { + // TODO handle errors + // TODO shouldn't we demangle the path? + if (NetSystem.IO.Directory.Exists(path)) + { + NetSystem.IO.Directory.Delete(path); + } + else if (NetSystem.IO.File.Exists(path)) + { + NetSystem.IO.File.Delete(path); + } + else + { + return false; + } + return true; + } + + public static bool createInternal(string path) + { + // TODO handle errors + // TODO shouldn't we demangle the path? + try + { + NetSystem.IO.File.Open(path, FileMode.CreateNew).Close(); + return true; + } + catch(Exception) + { + return false; + } + } + + private static long DateTimeToJavaLongTime(DateTime datetime) + { + return (TimeZone.CurrentTimeZone.ToUniversalTime(datetime) - new DateTime(1970, 1, 1)).Ticks / 10000L; + } + + public static DateTime JavaLongTimeToDateTime(long datetime) + { + return TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(new DateTime(1970, 1, 1).Ticks + datetime * 10000L)); + } + + public static long lastModifiedInternal(object obj, string path) + { + try + { + return DateTimeToJavaLongTime(NetSystem.IO.File.GetLastWriteTime(DemanglePath(path))); + } + catch(Exception) + { + return 0; + } + } + + public static string[] listInternal(object obj, string dirname) + { + // TODO error handling + try + { + string[] l = NetSystem.IO.Directory.GetFileSystemEntries(dirname); + for(int i = 0; i < l.Length; i++) + { + int pos = l[i].LastIndexOf(Path.DirectorySeparatorChar); + if(pos >= 0) + { + l[i] = l[i].Substring(pos + 1); + } + } + return l; + } + catch(Exception) + { + return null; + } + } + + public static bool canReadInternal(object obj, string file) + { + try + { + // HACK if file refers to a directory, we always return true + if(NetSystem.IO.Directory.Exists(file)) + { + return true; + } + new FileInfo(file).Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite).Close(); + return true; + } + catch(Exception) + { + return false; + } + } + + public static bool canWriteInternal(object obj, string file) + { + try + { + // HACK if file refers to a directory, we always return true + if(NetSystem.IO.Directory.Exists(file)) + { + return true; + } + new FileInfo(file).Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite).Close(); + return true; + } + catch(Exception) + { + return false; + } + } + + public static bool renameToInternal(object obj, string oldName, string newName) + { + try + { + new FileInfo(oldName).MoveTo(newName); + return true; + } + catch(Exception) + { + return false; + } + } + + public static bool setLastModifiedInternal(object obj, string file, long lastModified) + { + try + { + new FileInfo(file).LastWriteTime = JavaLongTimeToDateTime(lastModified); + return true; + } + catch(Exception) + { + return false; + } + } + + public static bool setReadOnlyInternal(object obj, string file) + { + try + { + new FileInfo(file).Attributes |= FileAttributes.ReadOnly; + return true; + } + catch(Exception) + { + return false; + } + } + } + + public class ObjectInputStream + { + public static object currentClassLoader(object sm) + { + // TODO calling currentClassLoader in SecurityManager results in null being returned, so we use our own + // version for now, don't know what the security implications of this are + // SECURITY + return NativeCode.java.lang.VMSecurityManager.currentClassLoader(); + } + + public static void callReadMethod(object ois, object obj, object clazz) + { + Type type = NativeCode.java.lang.Class.getType(clazz); + MethodInfo mi = type.GetMethod("readObject", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { ois.GetType() }, null); + mi.Invoke(obj, new object[] { ois }); + } + + public static object allocateObject(object ois, object clazz) + { + Type type = NativeCode.java.lang.Class.getType(clazz); + return NetSystem.Runtime.Serialization.FormatterServices.GetUninitializedObject(type); + } + + public static void callConstructor(object ois, object clazz, object obj) + { + Type type = NativeCode.java.lang.Class.getType(clazz); + type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke(obj, null); + } + + public static void setBooleanField(object ois, object obj, object clazz, string field_name, bool val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + public static void setByteField(object ois, object obj, object clazz, string field_name, sbyte val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + public static void setCharField(object ois, object obj, object clazz, string field_name, char val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + public static void setDoubleField(object ois, object obj, object clazz, string field_name, double val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + public static void setFloatField(object ois, object obj, object clazz, string field_name, float val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + public static void setIntField(object ois, object obj, object clazz, string field_name, int val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + public static void setLongField(object ois, object obj, object clazz, string field_name, long val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + public static void setShortField(object ois, object obj, object clazz, string field_name, short val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + public static void setObjectField(object ois, object obj, object clazz, string field_name, string type_code, object val) + { + SetFieldValue(obj, clazz, field_name, val); + } + + private static void SetFieldValue(object obj, object clazz, string field_name, object val) + { + // TODO support overloaded field name + Type type = NativeCode.java.lang.Class.getType(clazz); +// while(type != null) + { + FieldInfo fi = type.GetField(field_name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if(fi != null) + { + fi.SetValue(obj, val); + return; + } + // NOTE if not found, we're moving up the hierarchy, even though I'd expect GetField to do that, it doesn't, at least + // not for private fields +// type = type.BaseType; + } + throw new InvalidOperationException("SetFieldValue: field not found, field_name = " + field_name + ", obj = " + obj); + } + } + + public class ObjectOutputStream + { + public static void callWriteMethod(object oos, object obj) + { + Type type = obj.GetType(); + MethodInfo mi = type.GetMethod("writeObject", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { oos.GetType() }, null); + mi.Invoke(obj, new object[] { oos }); + } + + public static bool getBooleanField(object oos, object obj, object clazz, string field_name) + { + return (bool)GetFieldValue(obj, clazz, field_name); + } + + public static sbyte getByteField(object oos, object obj, object clazz, string field_name) + { + return (sbyte)GetFieldValue(obj, clazz, field_name); + } + + public static char getCharField(object oos, object obj, object clazz, string field_name) + { + return (char)GetFieldValue(obj, clazz, field_name); + } + + public static double getDoubleField(object oos, object obj, object clazz, string field_name) + { + return (double)GetFieldValue(obj, clazz, field_name); + } + + public static float getFloatField(object oos, object obj, object clazz, string field_name) + { + return (float)GetFieldValue(obj, clazz, field_name); + } + + public static int getIntField(object oos, object obj, object clazz, string field_name) + { + return (int)GetFieldValue(obj, clazz, field_name); + } + + public static long getLongField(object oos, object obj, object clazz, string field_name) + { + return (long)GetFieldValue(obj, clazz, field_name); + } + + public static short getShortField(object oos, object obj, object clazz, string field_name) + { + return (short)GetFieldValue(obj, clazz, field_name); + } + + public static object getObjectField(object oos, object obj, object clazz, string field_name, string type_code) + { + return GetFieldValue(obj, clazz, field_name); + } + + private static object GetFieldValue(object obj, object clazz, string field_name) + { + // TODO support overloaded field name + Type type = NativeCode.java.lang.Class.getType(clazz); +// while(type != null) + { + FieldInfo fi = type.GetField(field_name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if(fi != null) + { + return fi.GetValue(obj); + } + // NOTE if not found, we're moving up the hierarchy, even though I'd expect GetField to do that, it doesn't, at least + // not for private fields +// type = type.BaseType; + } + throw new InvalidOperationException("GetFieldValue: field not found, field_name = " + field_name + ", obj = " + obj); + } + } + } + + namespace util + { + public class TimeZone + { + public static string getDefaultTimeZoneId() + { + // HACK return null, classpath then assumes GMT, which is fine by me, for the time being + return null; + } + } + } + + namespace net + { + public class InetAddress + { + public static sbyte[] lookupInaddrAny() + { + return new sbyte[] { 0, 0, 0, 0 }; + } + + public static string getLocalHostName() + { + // TODO error handling + return NetSystem.Net.Dns.GetHostName(); + } + + public static int[][] getHostByName(string name) + { + // TODO error handling + try + { + NetSystem.Net.IPHostEntry he = NetSystem.Net.Dns.GetHostByName(name); + NetSystem.Net.IPAddress[] addresses = he.AddressList; + int[][] list = new int[addresses.Length][]; + for(int i = 0; i < addresses.Length; i++) + { + list[i] = AddressToIntArray((int)addresses[i].Address); + } + return list; + } + catch(Exception x) + { + throw JavaException.UnknownHostException(x.Message); + } + } + + public static string getHostByAddr(byte[] address) + { + return NetSystem.Net.Dns.GetHostByAddress(string.Format("{0}.{1}.{2}.{3}", address[0], address[1], address[2], address[3])).HostName; + } + + private static int[] AddressToIntArray(int address) + { + // TODO check for correctness + return new int[] { address & 0xff, (address >> 8) & 0xff, (address >> 16) & 0xff, (address >> 24) & 0xff }; + } + } + + public class PlainDatagramSocketImpl + { + // TODO this method lives here, because UdpClient.Receive has a ByRef parameter and NetExp doesn't support that + // I have to figure out a way to support ref parameters from Java + public static void receive(object obj, object packet) + { + sbyte[] data = (sbyte[])packet.GetType().InvokeMember("getData", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, packet, new object[0]); + int length = (int)packet.GetType().InvokeMember("getLength", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, packet, new object[0]); + object s = obj.GetType().GetField("socket", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj); + NetSystem.Net.Sockets.UdpClient socket = (NetSystem.Net.Sockets.UdpClient)s; + NetSystem.Net.IPEndPoint remoteEP = new NetSystem.Net.IPEndPoint(0, 0); + byte[] buf = socket.Receive(ref remoteEP); + for(int i = 0; i < Math.Min(length, buf.Length); i++) + { + data[i] = (sbyte)buf[i]; + } + packet.GetType().InvokeMember("setLength", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, packet, new object[] { buf.Length }); + long remoteIP = remoteEP.Address.Address; + string remote = (remoteIP & 0xff) + "." + ((remoteIP >> 8) & 0xff) + "." + ((remoteIP >> 16) & 0xff) + "." + ((remoteIP >> 24) & 0xff); + object remoteAddress = ClassLoaderWrapper.GetType("java.net.InetAddress").GetMethod("getByName").Invoke(null, new object[] { remote }); + packet.GetType().InvokeMember("setAddress", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, packet, new object[] { remoteAddress }); + packet.GetType().InvokeMember("setPort", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, packet, new object[] { remoteEP.Port }); + } + } + } +} + +namespace NativeCode.gnu.java.net.protocol.ikvmres +{ + public class IkvmresURLConnection + { + public static void InitArray(sbyte[] buf, FieldInfo field) + { + NetSystem.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(buf, field.FieldHandle); + } + } +} diff --git a/IK.VM.NET/compiler.cs b/IK.VM.NET/compiler.cs new file mode 100644 index 00000000..4cb61968 --- /dev/null +++ b/IK.VM.NET/compiler.cs @@ -0,0 +1,2450 @@ +/* + 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 + +*/ +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using System.Diagnostics.SymbolStore; +using ExceptionTableEntry = ClassFile.Method.ExceptionTableEntry; +using Instruction = ClassFile.Method.Instruction; + +class ReturnCookie +{ + public Label Stub; + public LocalBuilder Local; +} + +class BranchCookie +{ + public Label Stub; + public int TargetIndex; + public Stack Stack = new Stack(); +} + +class ExceptionSorter : IComparer +{ + public int Compare(object x, object y) + { + ExceptionTableEntry e1 = (ExceptionTableEntry)x; + ExceptionTableEntry e2 = (ExceptionTableEntry)y; + if(e1.start_pc < e2.start_pc) + { + return -1; + } + if(e1.start_pc == e2.start_pc) + { + if(e1.end_pc == e2.end_pc) + { + if(e1.ordinal > e2.ordinal) + { + return -1; + } + return 1; + } + if(e1.end_pc > e2.end_pc) + { + return -1; + } + } + return 1; + } +} + +class Compiler +{ + private static MethodInfo mapExceptionMethod = typeof(ExceptionHelper).GetMethod("MapException"); + private static MethodInfo mapExceptionFastMethod = typeof(ExceptionHelper).GetMethod("MapExceptionFast"); + private static MethodInfo fillInStackTraceMethod = typeof(ExceptionHelper).GetMethod("fillInStackTrace"); + private static MethodInfo getTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle"); + private static MethodInfo multiANewArrayMethod = typeof(ByteCodeHelper).GetMethod("multianewarray"); + private static MethodInfo monitorEnterMethod = typeof(ByteCodeHelper).GetMethod("monitorenter"); + private static MethodInfo monitorExitMethod = typeof(ByteCodeHelper).GetMethod("monitorexit"); + private static MethodInfo throwHack = typeof(ExceptionHelper).GetMethod("ThrowHack"); + private TypeWrapper clazz; + private ClassFile.Method.Code m; + private ILGenerator ilGenerator; + private ClassLoaderWrapper classLoader; + private MethodAnalyzer ma; + private Hashtable locals = new Hashtable(); + private ClassFile.Method.ExceptionTableEntry[] exceptions; + private ISymbolDocumentWriter symboldocument; + + private Compiler(TypeWrapper clazz, ClassFile.Method.Code m, ILGenerator ilGenerator, ClassLoaderWrapper classLoader) + { + this.clazz = clazz; + this.m = m; + this.ilGenerator = ilGenerator; + this.classLoader = classLoader; + if(JVM.Debug) + { + string sourcefile = m.Method.ClassFile.SourceFileAttribute; + if(sourcefile != null) + { + this.symboldocument = classLoader.ModuleBuilder.DefineDocument(sourcefile, Guid.Empty, Guid.Empty, Guid.Empty); + } + } + Profiler.Enter("MethodAnalyzer"); + ma = new MethodAnalyzer(m, classLoader); + Profiler.Leave("MethodAnalyzer"); + ArrayList ar = new ArrayList(m.ExceptionTable); +// Console.WriteLine("before processing:"); +// foreach(ExceptionTableEntry e in ar) +// { +// Console.WriteLine("{0} to {1} handler {2}", e.start_pc, e.end_pc, e.handler_pc); +// } + // TODO it's very bad practice to mess with ExceptionTableEntrys that are owned by the Method, yet we + // do that here, should be changed to use our own ETE class (which should also contain the ordinal, instead + // of the one in ClassFile.cs) + // OPTIMIZE there must be a more efficient algorithm to do this... + restart: + for(int i = 0; i < ar.Count; i++) + { + ExceptionTableEntry ei = (ExceptionTableEntry)ar[i]; + for(int j = i + 1; j < ar.Count; j++) + { + ExceptionTableEntry ej = (ExceptionTableEntry)ar[j]; + if(ei.start_pc <= ej.start_pc && ei.end_pc > ej.start_pc) + { + // try1.j + if(ej.end_pc > ei.end_pc) + { + ExceptionTableEntry emi = new ExceptionTableEntry(); + emi.start_pc = ej.start_pc; + emi.end_pc = ei.end_pc; + emi.catch_type = ei.catch_type; + emi.handler_pc = ei.handler_pc; + ExceptionTableEntry emj = new ExceptionTableEntry(); + emj.start_pc = ej.start_pc; + emj.end_pc = ei.end_pc; + emj.catch_type = ej.catch_type; + emj.handler_pc = ej.handler_pc; + ei.end_pc = emi.start_pc; + ej.start_pc = emj.end_pc; + ar.Insert(j, emj); + ar.Insert(i + 1, emi); + goto restart; + } + else if(ej.end_pc < ei.end_pc) // try2.j + { + ExceptionTableEntry emi = new ExceptionTableEntry(); + emi.start_pc = ej.start_pc; + emi.end_pc = ej.end_pc; + emi.catch_type = ei.catch_type; + emi.handler_pc = ei.handler_pc; + ExceptionTableEntry eei = new ExceptionTableEntry(); + eei.start_pc = ej.end_pc; + eei.end_pc = ei.end_pc; + eei.catch_type = ei.catch_type; + eei.handler_pc = ei.handler_pc; + ei.end_pc = emi.start_pc; + ar.Insert(i + 1, eei); + ar.Insert(i + 1, emi); + goto restart; + } + } + } + } + // __jsr inside a try block (to a PC outside the try block) causes the try + // block to be broken into two blocks surrounding the __jsr + // This is actually pretty common. Take, for example, the following code: + // class hello + // { + // public static void main(String[] args) + // { + // try + // { + // for(;;) + // { + // if(args.length == 0) return; + // } + // } + // finally + // { + // System.out.println("Hello, world!"); + // } + // } + // } + restart_jsr: + for(int i = 0; i < ar.Count; i++) + { + ExceptionTableEntry ei = (ExceptionTableEntry)ar[i]; + for(int j = FindPcIndex(ei.start_pc), e = FindPcIndex(ei.end_pc); j < e; j++) + { + if(m.Instructions[j].NormalizedOpCode == NormalizedByteCode.__jsr) + { + int targetPC = m.Instructions[j].NormalizedArg1 + m.Instructions[j].PC; + if(targetPC < ei.start_pc || targetPC >= ei.end_pc) + { + ExceptionTableEntry en = new ExceptionTableEntry(); + en.catch_type = ei.catch_type; + en.handler_pc = ei.handler_pc; + en.start_pc = (ushort)m.Instructions[j + 1].PC; + en.end_pc = ei.end_pc; + ei.end_pc = (ushort)m.Instructions[j].PC; + ar.Insert(i + 1, en); + goto restart_jsr; + } + } + } + } + // Split try blocks at branch targets (branches from outside the try block) + for(int i = 0; i < ar.Count; i++) + { + ExceptionTableEntry ei = (ExceptionTableEntry)ar[i]; + int start = FindPcIndex(ei.start_pc); + int end = FindPcIndex(ei.end_pc); + for(int j = 0; j < m.Instructions.Length; j++) + { + if(j < start || j >= end) + { + switch(m.Instructions[j].NormalizedOpCode) + { + case NormalizedByteCode.__lookupswitch: + // TODO if the switch branches out of the try block, that should be handled too +// for(int j = 0; j < instr.Values.Length; j++) +// { +// state[FindPcIndex(instr.PC + instr.TargetOffsets[j])] += s; +// } +// state[FindPcIndex(instr.PC + instr.DefaultOffset)] += s; + break; + case NormalizedByteCode.__ifeq: + case NormalizedByteCode.__ifne: + case NormalizedByteCode.__iflt: + case NormalizedByteCode.__ifge: + case NormalizedByteCode.__ifgt: + case NormalizedByteCode.__ifle: + case NormalizedByteCode.__if_icmpeq: + case NormalizedByteCode.__if_icmpne: + case NormalizedByteCode.__if_icmplt: + case NormalizedByteCode.__if_icmpge: + case NormalizedByteCode.__if_icmpgt: + case NormalizedByteCode.__if_icmple: + case NormalizedByteCode.__if_acmpeq: + case NormalizedByteCode.__if_acmpne: + case NormalizedByteCode.__ifnull: + case NormalizedByteCode.__ifnonnull: + case NormalizedByteCode.__goto: + case NormalizedByteCode.__jsr: + { + int targetPC = m.Instructions[j].PC + m.Instructions[j].Arg1; + if(targetPC > ei.start_pc && targetPC < ei.end_pc) + { + ExceptionTableEntry en = new ExceptionTableEntry(); + en.catch_type = ei.catch_type; + en.handler_pc = ei.handler_pc; + en.start_pc = (ushort)targetPC; + en.end_pc = ei.end_pc; + ei.end_pc = (ushort)targetPC; + ar.Insert(i + 1, en); + goto restart_jsr; + } + break; + } + } + } + } + } + // exception handlers are also a kind of jump, so we need to split try blocks around handlers as well + for(int i = 0; i < ar.Count; i++) + { + ExceptionTableEntry ei = (ExceptionTableEntry)ar[i]; + // TODO verify that we don't need to start at j = 0 + for(int j = i; j < ar.Count; j++) + { + ExceptionTableEntry ej = (ExceptionTableEntry)ar[j]; + if(ej.handler_pc > ei.start_pc && ej.handler_pc < ei.end_pc) + { + ExceptionTableEntry en = new ExceptionTableEntry(); + en.catch_type = ei.catch_type; + en.handler_pc = ei.handler_pc; + en.start_pc = (ushort)ej.handler_pc; + en.end_pc = ei.end_pc; + ei.end_pc = (ushort)ej.handler_pc; + ar.Insert(i + 1, en); + goto restart_jsr; + } + } + } + // filter out zero length try blocks + for(int i = 0; i < ar.Count; i++) + { + ExceptionTableEntry ei = (ExceptionTableEntry)ar[i]; + if(ei.start_pc == ei.end_pc) + { + ar.RemoveAt(i); + i--; + } + } +// Console.WriteLine("after processing:"); +// foreach(ExceptionTableEntry e in ar) +// { +// Console.WriteLine("{0} to {1} handler {2}", e.start_pc, e.end_pc, e.handler_pc); +// } + + exceptions = new ExceptionTableEntry[ar.Count]; + ar.CopyTo(exceptions, 0); + for(int i = 0; i < exceptions.Length; i++) + { + exceptions[i].ordinal = i; + } + Array.Sort(exceptions, new ExceptionSorter()); + + // TODO remove these checks, if the above exception untangling is correct, this shouldn't ever + // be triggered + for(int i = 0; i < exceptions.Length; i++) + { + for(int j = i + 1; j < exceptions.Length; j++) + { + // check for partially overlapping try blocks (which is legal for the JVM, but not the CLR) + if(exceptions[i].start_pc < exceptions[j].start_pc && + exceptions[j].start_pc < exceptions[i].end_pc && + exceptions[i].end_pc < exceptions[j].end_pc) + { + throw new Exception("Partially overlapping try blocks is broken"); + } + // check that we didn't destroy the ordering, when sorting + if(exceptions[i].start_pc <= exceptions[j].start_pc && + exceptions[i].end_pc >= exceptions[j].end_pc && + exceptions[i].ordinal < exceptions[j].ordinal) + { + throw new Exception("Non recursive try blocks is broken"); + } + } + // make sure __jsr doesn't jump out of try block + for(int j = FindPcIndex(exceptions[i].start_pc), e = FindPcIndex(exceptions[i].end_pc); j < e; j++) + { + if(m.Instructions[j].NormalizedOpCode == NormalizedByteCode.__jsr) + { + int targetPC = m.Instructions[j].NormalizedArg1 + m.Instructions[j].PC; + if(targetPC < exceptions[i].start_pc || targetPC >= exceptions[i].end_pc) + { + Console.WriteLine("i = " + i); + Console.WriteLine("j = " + j); + Console.WriteLine("targetPC = " + targetPC); + throw new Exception("Try block splitting around __jsr is broken"); + } + } + } + } + } + + private struct DupHelper + { + private ClassLoaderWrapper classLoader; + private ILGenerator ilgen; + private bool[] isnull; + private LocalBuilder[] locals; + + internal DupHelper(ClassLoaderWrapper classLoader, ILGenerator ilgen, int count) + { + this.classLoader = classLoader; + this.ilgen = ilgen; + isnull = new bool[count]; + locals = new LocalBuilder[count]; + } + + internal DupHelper SetType(int i, string type) + { + if(type == "Lnull") + { + isnull[i] = true; + } + else if(type[0] != 'N') + { + // TODO handle class not found + locals[i] = ilgen.DeclareLocal(classLoader.ExpressionType(type)); + } + return this; + } + + internal DupHelper Load(int i) + { + if(isnull[i]) + { + ilgen.Emit(OpCodes.Ldnull); + } + else if(locals[i] != null) + { + ilgen.Emit(OpCodes.Ldloc, locals[i]); + } + return this; + } + + internal DupHelper Store(int i) + { + if(isnull[i]) + { + ilgen.Emit(OpCodes.Pop); + } + else if(locals[i] != null) + { + ilgen.Emit(OpCodes.Stloc, locals[i]); + } + return this; + } + } + + internal static void Compile(TypeWrapper clazz, ClassFile.Method m, ILGenerator ilGenerator, ClassLoaderWrapper classLoader) + { + Compiler c; + try + { + Profiler.Enter("new Compiler"); + c = new Compiler(clazz, m.CodeAttribute, ilGenerator, classLoader); + Profiler.Leave("new Compiler"); + } + catch(VerifyError x) + { + // because in Java the method is only verified if it is actually called, + // we generate code here to throw the VerificationError + Type verifyError = ClassLoaderWrapper.GetType("java.lang.VerifyError"); + ilGenerator.Emit(OpCodes.Ldstr, string.Format("(class: {0}, method: {1}, signature: {2}, offset: {3}, instruction: {4}) {5}", x.Class, x.Method, x.Signature, x.ByteCodeOffset, x.Instruction, x.Message)); + ilGenerator.Emit(OpCodes.Newobj, verifyError.GetConstructor(new Type[] { typeof(string) })); + ilGenerator.Emit(OpCodes.Throw); + return; + } + Profiler.Enter("Compile"); + c.Compile(0, 0, null); + Profiler.Leave("Compile"); + } + + private void Compile(int initialInstructionIndex, int exceptionIndex, ArrayList exits) + { + int rangeBegin; + int rangeEnd; // NOTE points past the last instruction in the range + if(exceptionIndex == 0) + { + rangeBegin = 0; + // because the last instruction in the code array is always the additional __nop, put there + // by our classfile reader, this works + rangeEnd = m.Instructions[m.Instructions.Length - 1].PC; + } + else + { + rangeBegin = exceptions[exceptionIndex - 1].start_pc; + rangeEnd = exceptions[exceptionIndex - 1].end_pc; + } + object[] labels = new object[m.Instructions[m.Instructions.Length - 1].PC]; + // used to track instructions that are 'live' + bool[] inuse = new bool[m.Instructions.Length]; + // used to track instructions that have been compiled + bool[] done = new bool[m.Instructions.Length]; + bool quit = false; + inuse[initialInstructionIndex] = true; + Instruction[] code = m.Instructions; + while(!quit) + { + quit = true; + for(int i = 0; i < code.Length; i++) + { + restart: + if(!inuse[i] || done[i]) + { + continue; + } + quit = false; + done[i] = true; + Instruction instr = code[i]; + + // make sure we didn't branch into a try block + // NOTE this check is not strict enough + // UPDATE since we're now splitting try blocks around branch targets, this shouldn't be possible anymore + if(exceptionIndex < exceptions.Length && + instr.PC > exceptions[exceptionIndex].start_pc && + instr.PC < exceptions[exceptionIndex].end_pc) + { + throw new NotImplementedException("branch into try block not implemented: " + clazz.Name + "." + m.Method.Name + m.Method.Signature + " (index = " + exceptionIndex + ", pc = " + instr.PC + ")"); + } + + // every instruction has an associated label, for now + if(true) + { + object label = labels[instr.PC]; + if(label == null) + { + label = ilGenerator.DefineLabel(); + labels[instr.PC] = label; + } + ilGenerator.MarkLabel((Label)label); + if(symboldocument != null) + { + // TODO this needs to be done better + ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute; + if(table != null) + { + for(int j = 0; j < table.Length; j++) + { + if(table[j].start_pc == instr.PC && table[j].line_number != 0) + { + ilGenerator.MarkSequencePoint(symboldocument, table[j].line_number, 0, table[j].line_number + 1, 0); + break; + } + } + } + } + } + + // handle the try block here + for(int j = exceptionIndex; j < exceptions.Length; j++) + { + if(exceptions[j].start_pc == instr.PC) + { + if(ma.GetStackHeight(i) != 0) + { + Stack stack = new Stack(); + int stackHeight = ma.GetStackHeight(i); + for(int n = 0; n < stackHeight; n++) + { + // TODO handle class not found + string t = ma.GetRawStackType(i, n); + if(t.Length > 1 && t[0] == 'N') + { + // unitialized references aren't really there + continue; + } + if(t == "Lnull") + { + stack.Push(null); + } + else + { + LocalBuilder local = ilGenerator.DeclareLocal(classLoader.ExpressionType(t)); + stack.Push(local); + ilGenerator.Emit(OpCodes.Stloc, local); + } + } + ilGenerator.BeginExceptionBlock(); + while(stack.Count != 0) + { + LocalBuilder local = (LocalBuilder)stack.Pop(); + if(local == null) + { + ilGenerator.Emit(OpCodes.Ldnull); + } + else + { + ilGenerator.Emit(OpCodes.Ldloc, local); + } + } + } + else + { + ilGenerator.BeginExceptionBlock(); + } + ArrayList newExits = new ArrayList(); + Compile(i, j + 1, newExits); + for(int k = 0; k < newExits.Count; k++) + { + object exit = newExits[k]; + BranchCookie bc = exit as BranchCookie; + if(bc != null) + { + ilGenerator.MarkLabel(bc.Stub); + int stack = ma.GetStackHeight(bc.TargetIndex); + for(int n = 0; n < stack; n++) + { + // TODO handle class not found + string t = ma.GetRawStackType(bc.TargetIndex, n); + if((t.Length > 1 && t[0] == 'N') || t == "Lnull") + { + // unitialized references aren't really there, but at the push site we + // need to know that we have to skip this slot, so we push a null as well, + // and then at the push site we'll look at the stack type to figure out + // if it is a real null or an uniti + bc.Stack.Push(null); + } + else + { + LocalBuilder local = ilGenerator.DeclareLocal(classLoader.ExpressionType(t)); + bc.Stack.Push(local); + ilGenerator.Emit(OpCodes.Stloc, local); + } + } + bc.Stub = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Leave, bc.Stub); + } + } + Type excType; + if(exceptions[j].catch_type == 0) + { + excType = typeof(Exception); + } + else + { + // TODO handle class not found + excType = classLoader.LoadClassBySlashedName(m.Method.ClassFile.GetConstantPoolClass(exceptions[j].catch_type)).Type; + } + if(true) + { + ilGenerator.BeginCatchBlock(typeof(Exception)); + Label label = ilGenerator.DefineLabel(); + LocalBuilder local = ilGenerator.DeclareLocal(excType); + // special case for catch(Throwable) (and finally), that produces less code and + // should be faster + if(excType == typeof(Exception)) + { + ilGenerator.Emit(OpCodes.Call, mapExceptionFastMethod); + ilGenerator.Emit(OpCodes.Stloc, local); + ilGenerator.Emit(OpCodes.Leave, label); + } + else + { + ilGenerator.Emit(OpCodes.Ldtoken, excType); + ilGenerator.Emit(OpCodes.Call, getTypeFromHandleMethod); + ilGenerator.Emit(OpCodes.Call, mapExceptionMethod); + ilGenerator.Emit(OpCodes.Castclass, excType); + ilGenerator.Emit(OpCodes.Stloc, local); + ilGenerator.Emit(OpCodes.Ldloc, local); + Label rethrow = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Brfalse, rethrow); + ilGenerator.Emit(OpCodes.Leave, label); + ilGenerator.MarkLabel(rethrow); + ilGenerator.Emit(OpCodes.Rethrow); + } + ilGenerator.EndExceptionBlock(); + ilGenerator.MarkLabel(label); + ilGenerator.Emit(OpCodes.Ldloc, local); + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, exceptions[j].handler_pc, inuse, rangeBegin, rangeEnd, exits)); + } + for(int k = 0; k < newExits.Count; k++) + { + object exit = newExits[k]; + ReturnCookie rc = exit as ReturnCookie; + if(rc != null) + { + if(exceptionIndex == 0) + { + ilGenerator.MarkLabel(rc.Stub); + if(rc.Local != null) + { + ilGenerator.Emit(OpCodes.Ldloc, rc.Local); + } + ilGenerator.Emit(OpCodes.Ret); + } + else + { + ReturnCookie rc1 = new ReturnCookie(); + rc1.Local = rc.Local; + rc1.Stub = ilGenerator.DefineLabel(); + ilGenerator.MarkLabel(rc.Stub); + ilGenerator.Emit(OpCodes.Leave, rc1.Stub); + exits.Add(rc1); + } + } + else + { + BranchCookie bc = exit as BranchCookie; + if(bc != null) + { + ilGenerator.MarkLabel(bc.Stub); + int stack = ma.GetStackHeight(bc.TargetIndex); + for(int n = 0; n < stack; n++) + { + LocalBuilder local = (LocalBuilder)bc.Stack.Pop(); + if(local == null) + { + if(ma.GetRawStackType(bc.TargetIndex, (stack - 1) - n) == "Lnull") + { + ilGenerator.Emit(OpCodes.Ldnull); + } + else + { + // if the type is not Lnull, it means it was an unitialized object reference, + // which don't really exist on our stack + } + } + else + { + ilGenerator.Emit(OpCodes.Ldloc, local); + } + } + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, code[bc.TargetIndex].PC, inuse, rangeBegin, rangeEnd, exits)); + } + } + } + goto restart; + } + } + + switch(instr.NormalizedOpCode) + { + case NormalizedByteCode.__getstatic: + { + ClassFile.ConstantPoolItemFieldref cpi = m.Method.ClassFile.GetFieldref(instr.Arg1); + FieldWrapper field = GetField(cpi, true, null, false); + if(field != null) + { + field.EmitGet.Emit(ilGenerator); + } + else + { + EmitPlaceholder(cpi.Signature); + } + break; + } + case NormalizedByteCode.__putstatic: + { + ClassFile.ConstantPoolItemFieldref cpi = m.Method.ClassFile.GetFieldref(instr.Arg1); + FieldWrapper field = GetField(cpi, true, null, true); + if(field != null) + { + // because of the way interface merging works, an object reference is valid + // for any interface reference + if(field.FieldType != typeof(object) && ma.GetRawStackType(i, 0) == "Ljava/lang/Object;") + { + ilGenerator.Emit(OpCodes.Castclass, field.FieldType); + } + field.EmitSet.Emit(ilGenerator); + } + else + { + ilGenerator.Emit(OpCodes.Pop); + } + break; + } + case NormalizedByteCode.__getfield: + { + ClassFile.ConstantPoolItemFieldref cpi = m.Method.ClassFile.GetFieldref(instr.Arg1); + TypeWrapper thisType = LoadClass(SigTypeToClassName(ma.GetRawStackType(i, 0), cpi.Class)); + if(thisType != null) + { + FieldWrapper field = GetField(cpi, false, thisType, false); + if(field != null) + { + field.EmitGet.Emit(ilGenerator); + break; + } + } + ilGenerator.Emit(OpCodes.Pop); + EmitPlaceholder(cpi.Signature); + break; + } + case NormalizedByteCode.__putfield: + { + ClassFile.ConstantPoolItemFieldref cpi = m.Method.ClassFile.GetFieldref(instr.Arg1); + TypeWrapper thisType = LoadClass(SigTypeToClassName(ma.GetRawStackType(i, 1), cpi.Class)); + if(thisType != null) + { + FieldWrapper field = GetField(cpi, false, thisType, true); + if(field != null) + { + // because of the way interface merging works, an object reference is valid + // for any interface reference + if(field.FieldType != typeof(object) && ma.GetRawStackType(i, 0) == "Ljava/lang/Object;") + { + ilGenerator.Emit(OpCodes.Castclass, field.FieldType); + } + field.EmitSet.Emit(ilGenerator); + break; + } + } + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Pop); + break; + } + case NormalizedByteCode.__aconst_null: + ilGenerator.Emit(OpCodes.Ldnull); + break; + case NormalizedByteCode.__iconst: + switch(instr.NormalizedArg1) + { + case -1: + ilGenerator.Emit(OpCodes.Ldc_I4_M1); + break; + case 0: + ilGenerator.Emit(OpCodes.Ldc_I4_0); + break; + case 1: + ilGenerator.Emit(OpCodes.Ldc_I4_1); + break; + case 2: + ilGenerator.Emit(OpCodes.Ldc_I4_2); + break; + case 3: + ilGenerator.Emit(OpCodes.Ldc_I4_3); + break; + case 4: + ilGenerator.Emit(OpCodes.Ldc_I4_4); + break; + case 5: + ilGenerator.Emit(OpCodes.Ldc_I4_5); + break; + case 6: + ilGenerator.Emit(OpCodes.Ldc_I4_6); + break; + case 7: + ilGenerator.Emit(OpCodes.Ldc_I4_7); + break; + case 8: + ilGenerator.Emit(OpCodes.Ldc_I4_8); + break; + default: + if(instr.NormalizedArg1 >= -128 && instr.NormalizedArg1 <= 127) + { + ilGenerator.Emit(OpCodes.Ldc_I4_S, (sbyte)instr.NormalizedArg1); + } + else + { + ilGenerator.Emit(OpCodes.Ldc_I4, instr.NormalizedArg1); + } + break; + } + break; + case NormalizedByteCode.__lconst_0: + ilGenerator.Emit(OpCodes.Ldc_I8, 0L); + break; + case NormalizedByteCode.__lconst_1: + ilGenerator.Emit(OpCodes.Ldc_I8, 1L); + break; + case NormalizedByteCode.__fconst_0: + ilGenerator.Emit(OpCodes.Ldc_R4, 0.0f); + break; + case NormalizedByteCode.__fconst_1: + ilGenerator.Emit(OpCodes.Ldc_R4, 1.0f); + break; + case NormalizedByteCode.__fconst_2: + ilGenerator.Emit(OpCodes.Ldc_R4, 2.0f); + break; + case NormalizedByteCode.__dconst_0: + ilGenerator.Emit(OpCodes.Ldc_R8, 0.0d); + break; + case NormalizedByteCode.__dconst_1: + ilGenerator.Emit(OpCodes.Ldc_R8, 1.0d); + break; + case NormalizedByteCode.__ldc: + { + object o = instr.MethodCode.Method.ClassFile.GetConstantPoolConstant(instr.Arg1); + if(o is string) + { + ilGenerator.Emit(OpCodes.Ldstr, (string)o); + } + else if(o is float) + { + ilGenerator.Emit(OpCodes.Ldc_R4, (float)o); + } + else if(o is double) + { + ilGenerator.Emit(OpCodes.Ldc_R8, (double)o); + } + else if(o is int) + { + ilGenerator.Emit(OpCodes.Ldc_I4, (int)o); + } + else if(o is long) + { + ilGenerator.Emit(OpCodes.Ldc_I8, (long)o); + } + else + { + throw new NotImplementedException(o.GetType().Name); + } + break; + } + case NormalizedByteCode.__invokestatic: + { + ClassFile.ConstantPoolItemFMI cpi = m.Method.ClassFile.GetMethodref(instr.Arg1); + MethodWrapper method = GetMethod(cpi, null, NormalizedByteCode.__invokestatic); + if(method != null) + { + method.EmitCall.Emit(ilGenerator); + } + else + { + SigEnumerator sig = new SigEnumerator(cpi.Signature); + while(sig.MoveNext()) + { + ilGenerator.Emit(OpCodes.Pop); + } + EmitPlaceholder(cpi.Signature.Substring(cpi.Signature.LastIndexOf(')') + 1)); + } + break; + } + case NormalizedByteCode.__invokevirtual: + case NormalizedByteCode.__invokeinterface: + case NormalizedByteCode.__invokespecial: + { + // TODO invokespecial should check for null "this" reference + ClassFile.ConstantPoolItemFMI cpi = m.Method.ClassFile.GetMethodref(instr.Arg1); + SigEnumerator sig = new SigEnumerator(cpi.Signature); + int argcount = 0; + while(sig.MoveNext()) + { + argcount++; + } + string type = ma.GetRawStackType(i, argcount); + TypeWrapper thisType = LoadClass(SigTypeToClassName(type, cpi.Class)); + // invokeinterface needs to have special support for downcasting to the interface (because + // the verifier may not be able to merge two interfaces, but the resulting code would still be valid) + if(instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface && + thisType == ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/Object")) + { + thisType = LoadClass(cpi.Class); + if(thisType != null) + { + DupHelper dup = new DupHelper(classLoader, ilGenerator, argcount); + for(int k = 0; k < argcount; k++) + { + dup.SetType(k, ma.GetRawStackType(i, k)); + } + for(int k = 0; k < argcount; k++) + { + dup.Store(k); + } + // TODO this IncompatibleClassChangeError check should also be applied + // for other locations where we can "consume" an object reference in the + // place of an interface reference (putstatic / putfield / arguments for invoke*). + // TODO it turns out that when an interface ref is expected, *any* type will be accepted! + ilGenerator.Emit(OpCodes.Dup); + Label label = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Brfalse_S, label); + ilGenerator.Emit(OpCodes.Isinst, thisType.Type); + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.Emit(OpCodes.Brtrue_S, label); + EmitError("java.lang.IncompatibleClassChangeError", null); + ilGenerator.MarkLabel(label); + for(int k = argcount - 1; k >= 0; k--) + { + dup.Load(k); + } + } + } + else if(thisType != null && !thisType.IsSubTypeOf(LoadClass(cpi.Class))) + { + EmitError("java.lang.IncompatibleClassChangeError", null); + thisType = null; + } + MethodWrapper method = (thisType != null) ? GetMethod(cpi, thisType, instr.NormalizedOpCode) : null; + if(instr.NormalizedOpCode == NormalizedByteCode.__invokespecial) + { + if(cpi.Name == "<init>") + { + if(type[0] == 'N') + { + if(thisType != null && (thisType.IsAbstract || thisType.IsInterface)) + { + // the CLR gets confused when we do a newobj on an abstract class, + // so we set method to null, to basically just comment out the constructor + // call (the InstantionError was already emitted at the "new" bytecode) + method = null; + } + // we have to construct a list of all the unitialized references to the object + // we're about to create on the stack, so that we can reconstruct the stack after + // the "newobj" instruction + int trivcount = 0; + bool nontrivial = false; + bool[] stackfix = new bool[ma.GetStackHeight(i) - (argcount + 1)]; + bool[] localsfix = new bool[m.MaxLocals]; + for(int j = 0; j < stackfix.Length; j++) + { + if(ma.GetRawStackType(i, argcount + 1 + j) == type) + { + stackfix[j] = true; + if(trivcount == j) + { + trivcount++; + } + else + { + // if there is other stuff on the stack between the new object + // references, we need to do more work to construct the proper stack + // layout after the newobj instruction + nontrivial = true; + } + } + } + for(int j = 0; j < localsfix.Length; j++) + { + if(ma.GetLocalType(i, j) == type) + { + localsfix[j] = true; + nontrivial = true; + } + } + if(method != null) + { + method.EmitNewobj.Emit(ilGenerator); + } + else + { + for(int j = 0; j < argcount; j++) + { + ilGenerator.Emit(OpCodes.Pop); + } + ilGenerator.Emit(OpCodes.Ldnull); + } + // TODO it is probably a better idea to do this in the constructor for each class + // derived from java.lang.Throwable, but if we do move this to the constructor, we + // should still call it here for non-Java exceptions (that aren't derived from Throwable) + Type t = ExpressionType(type.Substring(type.IndexOf(';') + 1)); + if(t == null) + { + // If the type couldn't be loaded, we continue we object to make sure + // the code remains verifiable (the ExpressionType call above already generated + // code to throw an exception, but the remaing code still needs to be verifiable, + // even though it is unreachable). + t = typeof(object); + } + if(typeof(Exception).IsAssignableFrom(t)) + { + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.Emit(OpCodes.Call, fillInStackTraceMethod); + ilGenerator.Emit(OpCodes.Pop); + } + if(nontrivial) + { + // this could be done a little more efficiently, but since in practice this + // code never runs (for code compiled from Java source) it doesn't + // really matter + LocalBuilder newobj = ilGenerator.DeclareLocal(t); + ilGenerator.Emit(OpCodes.Stloc, newobj); + LocalBuilder[] tempstack = new LocalBuilder[stackfix.Length]; + for(int j = 0; j < stackfix.Length; j++) + { + if(!stackfix[j]) + { + string stacktype = ma.GetRawStackType(i, argcount + 1 + j); + // it could be another new object reference (not from current invokespecial <init> + // instruction) + if(stacktype[0] != 'N') + { + // TODO handle Lnull stack entries + // TODO handle class not found + LocalBuilder lb = ilGenerator.DeclareLocal(classLoader.ExpressionType(stacktype)); + ilGenerator.Emit(OpCodes.Stloc, lb); + tempstack[j] = lb; + } + } + } + for(int j = stackfix.Length - 1; j >= 0; j--) + { + if(stackfix[j]) + { + ilGenerator.Emit(OpCodes.Ldloc, newobj); + } + else if(tempstack[j] != null) + { + ilGenerator.Emit(OpCodes.Ldloc, tempstack[j]); + } + } + for(int j = 0; j < localsfix.Length; j++) + { + if(localsfix[j]) + { + ilGenerator.Emit(OpCodes.Ldloc, newobj); + ilGenerator.Emit(OpCodes.Stloc, GetLocal(typeof(object), j)); + } + } + } + else + { + if(trivcount == 0) + { + ilGenerator.Emit(OpCodes.Pop); + } + else + { + for(int j = 1; j < trivcount; j++) + { + ilGenerator.Emit(OpCodes.Dup); + } + } + } + } + else + { + if(method != null) + { + method.EmitCall.Emit(ilGenerator); + } + else + { + // if we're a constructor and the call to the base class constructor + // wasn't accessible, we need make sure that there is no code path that + // returns from the constructor, otherwise the method will be not verifiable + // TODO this isn't anywhere near a proper solution, but for the time being it works + // some things to consider: + // - only pull this full when calls to the base class constructor fail + // - when control flow is complex, this trivial solution will not work + ilGenerator.Emit(OpCodes.Ldnull); + ilGenerator.Emit(OpCodes.Throw); + return; +// for(int j = 0; j < argcount + 1; j++) +// { +// ilGenerator.Emit(OpCodes.Pop); +// } +// EmitPlaceholder(cpi.Signature.Substring(cpi.Signature.LastIndexOf(')') + 1)); + } + } + } + else + { + if(method != null) + { + method.EmitCall.Emit(ilGenerator); + } + else + { + for(int j = 0; j < argcount + 1; j++) + { + ilGenerator.Emit(OpCodes.Pop); + } + EmitPlaceholder(cpi.Signature.Substring(cpi.Signature.LastIndexOf(')') + 1)); + } + } + } + else + { + if(method != null) + { + method.EmitCallvirt.Emit(ilGenerator); + } + else + { + for(int j = 0; j < argcount + 1; j++) + { + ilGenerator.Emit(OpCodes.Pop); + } + EmitPlaceholder(cpi.Signature.Substring(cpi.Signature.LastIndexOf(')') + 1)); + } + } + break; + } + case NormalizedByteCode.__return: + case NormalizedByteCode.__areturn: + case NormalizedByteCode.__ireturn: + case NormalizedByteCode.__lreturn: + case NormalizedByteCode.__freturn: + case NormalizedByteCode.__dreturn: + { + if(exceptionIndex != 0) + { + // if we're inside an exception block, copy TOS to local, emit "leave" and push item onto our "todo" list + ReturnCookie rc = new ReturnCookie(); + if(instr.NormalizedOpCode != NormalizedByteCode.__return) + { + // TODO handle class not found + Type retType = classLoader.RetTypeFromSig(m.Method.Signature); + rc.Local = ilGenerator.DeclareLocal(retType); + // because of the way interface merging works, an object reference is valid + // for any interface reference + if(retType != typeof(object) && ma.GetRawStackType(i, 0) == "Ljava/lang/Object;") + { + ilGenerator.Emit(OpCodes.Castclass, retType); + } + ilGenerator.Emit(OpCodes.Stloc, rc.Local); + } + rc.Stub = ilGenerator.DefineLabel(); + // NOTE leave automatically discards any junk that may be on the stack + ilGenerator.Emit(OpCodes.Leave, rc.Stub); + exits.Add(rc); + } + else + { + // if there is junk on the stack (other than the return value), we must pop it off + // because in .NET this is invalid (unlike in Java) + int stackHeight = ma.GetStackHeight(i); + if(instr.NormalizedOpCode == NormalizedByteCode.__return) + { + for(int j = 0; j < stackHeight; j++) + { + ilGenerator.Emit(OpCodes.Pop); + } + ilGenerator.Emit(OpCodes.Ret); + } + else + { + // TODO handle class not found + Type retType = classLoader.RetTypeFromSig(m.Method.Signature); + // because of the way interface merging works, an object reference is valid + // for any interface reference + if(retType != typeof(object) && ma.GetRawStackType(i, 0) == "Ljava/lang/Object;") + { + ilGenerator.Emit(OpCodes.Castclass, retType); + } + if(stackHeight != 1) + { + LocalBuilder local = ilGenerator.DeclareLocal(retType); + ilGenerator.Emit(OpCodes.Stloc, local); + for(int j = 1; j < stackHeight; j++) + { + ilGenerator.Emit(OpCodes.Pop); + } + ilGenerator.Emit(OpCodes.Ldloc, local); + } + ilGenerator.Emit(OpCodes.Ret); + } + } + break; + } + case NormalizedByteCode.__aload: + { + string type = ma.GetLocalType(i, instr.NormalizedArg1); + if(type == "Lnull") + { + // if the local is known to be null, we just emit a null + ilGenerator.Emit(OpCodes.Ldnull); + } + else if(type[0] == 'N') + { + // since new objects aren't represented on the stack, we don't need to do anything here + } + else if(type[0] == 'U') + { + // any unitialized reference has to be the this reference + // TODO when we get support for overwriting the this reference, this code + // needs to be aware of that (or, this overwriting should be handled specially for <init>) + ilGenerator.Emit(OpCodes.Ldarg_0); + } + else + { + Load(instr, typeof(object)); + if(instr.NormalizedArg1 >= m.ArgMap.Length) + { + // HACK since, for now, all locals are of type object, we've got to cast them to the proper type + // UPDATE the above is no longer true, we now have at least some idea of the type of the local + if(type != ma.GetDeclaredLocalType(instr.NormalizedArg1)) + { + // TODO handle class not found + ilGenerator.Emit(OpCodes.Castclass, classLoader.ExpressionType(ma.GetLocalType(i, instr.NormalizedArg1))); + } + } + } + break; + } + case NormalizedByteCode.__astore: + { + string type = ma.GetRawStackType(i, 0); + // HACK we use "int" to track the return address of a jsr + if(type.StartsWith("Lret;")) + { + Store(instr, typeof(int)); + } + else if(type[0] == 'N') + { + // NOTE new objects aren't really on the stack, so we can't copy them into the local. + // We do store a null in the local, to prevent it from retaining an unintentional reference + // to whatever object reference happens to be there + ilGenerator.Emit(OpCodes.Ldnull); + Store(instr, typeof(object)); + } + else if(type[0] == 'U') + { + // any unitialized reference, is always the this reference, we don't store anything + // here (because CLR wont allow unitialized references in locals) and then when + // the unitialized ref is loaded we redirect to the this reference + ilGenerator.Emit(OpCodes.Pop); + } + else + { + Store(instr, typeof(object)); + } + break; + } + case NormalizedByteCode.__iload: + Load(instr, typeof(int)); + break; + case NormalizedByteCode.__istore: + Store(instr, typeof(int)); + break; + case NormalizedByteCode.__lload: + Load(instr, typeof(long)); + break; + case NormalizedByteCode.__lstore: + Store(instr, typeof(long)); + break; + case NormalizedByteCode.__fload: + Load(instr, typeof(float)); + break; + case NormalizedByteCode.__fstore: + Store(instr, typeof(float)); + break; + case NormalizedByteCode.__dload: + Load(instr, typeof(double)); + break; + case NormalizedByteCode.__dstore: + Store(instr, typeof(double)); + break; + case NormalizedByteCode.__new: + { + TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); + if(wrapper != null && (wrapper.IsAbstract || wrapper.IsInterface)) + { + EmitError("java.lang.InstantiationError", wrapper.Name); + } + // we don't do anything here, the call to <init> will be converted into a newobj instruction + break; + } + case NormalizedByteCode.__multianewarray: + { + TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); + if(wrapper != null) + { + LocalBuilder localArray = ilGenerator.DeclareLocal(typeof(int[])); + LocalBuilder localInt = ilGenerator.DeclareLocal(typeof(int)); + ilGenerator.Emit(OpCodes.Ldc_I4, instr.Arg2); + ilGenerator.Emit(OpCodes.Newarr, typeof(int)); + ilGenerator.Emit(OpCodes.Stloc, localArray); + for(int j = 1; j <= instr.Arg2; j++) + { + ilGenerator.Emit(OpCodes.Stloc, localInt); + ilGenerator.Emit(OpCodes.Ldloc, localArray); + ilGenerator.Emit(OpCodes.Ldc_I4, instr.Arg2 - j); + ilGenerator.Emit(OpCodes.Ldloc, localInt); + ilGenerator.Emit(OpCodes.Stelem_I4); + } + Type type = wrapper.Type; + ilGenerator.Emit(OpCodes.Ldtoken, type); + ilGenerator.Emit(OpCodes.Ldloc, localArray); + ilGenerator.Emit(OpCodes.Call, multiANewArrayMethod); + ilGenerator.Emit(OpCodes.Castclass, type); + } + break; + } + case NormalizedByteCode.__anewarray: + { + TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); + if(wrapper != null) + { + ilGenerator.Emit(OpCodes.Newarr, wrapper.Type); + } + break; + } + case NormalizedByteCode.__newarray: + switch(instr.Arg1) + { + case 4: + ilGenerator.Emit(OpCodes.Newarr, typeof(bool)); + break; + case 5: + ilGenerator.Emit(OpCodes.Newarr, typeof(char)); + break; + case 6: + ilGenerator.Emit(OpCodes.Newarr, typeof(float)); + break; + case 7: + ilGenerator.Emit(OpCodes.Newarr, typeof(double)); + break; + case 8: + ilGenerator.Emit(OpCodes.Newarr, typeof(sbyte)); + break; + case 9: + ilGenerator.Emit(OpCodes.Newarr, typeof(short)); + break; + case 10: + ilGenerator.Emit(OpCodes.Newarr, typeof(int)); + break; + case 11: + ilGenerator.Emit(OpCodes.Newarr, typeof(long)); + break; + default: + throw new InvalidOperationException(); + } + break; + case NormalizedByteCode.__checkcast: + { + TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); + if(wrapper != null) + { + ilGenerator.Emit(OpCodes.Castclass, wrapper.Type); + } + break; + } + case NormalizedByteCode.__instanceof: + { + TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); + if(wrapper != null) + { + ilGenerator.Emit(OpCodes.Isinst, wrapper.Type); + ilGenerator.Emit(OpCodes.Ldnull); + ilGenerator.Emit(OpCodes.Ceq); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.Emit(OpCodes.Ceq); + } + break; + } + case NormalizedByteCode.__aaload: + ilGenerator.Emit(OpCodes.Ldelem_Ref); + break; + case NormalizedByteCode.__baload: + // NOTE both the JVM and the CLR use signed bytes for boolean arrays (how convenient!) + ilGenerator.Emit(OpCodes.Ldelem_I1); + break; + case NormalizedByteCode.__bastore: + ilGenerator.Emit(OpCodes.Stelem_I1); + break; + case NormalizedByteCode.__caload: + ilGenerator.Emit(OpCodes.Ldelem_U2); + break; + case NormalizedByteCode.__castore: + ilGenerator.Emit(OpCodes.Stelem_I2); + break; + case NormalizedByteCode.__saload: + ilGenerator.Emit(OpCodes.Ldelem_I2); + break; + case NormalizedByteCode.__sastore: + ilGenerator.Emit(OpCodes.Stelem_I2); + break; + case NormalizedByteCode.__iaload: + ilGenerator.Emit(OpCodes.Ldelem_I4); + break; + case NormalizedByteCode.__iastore: + ilGenerator.Emit(OpCodes.Stelem_I4); + break; + case NormalizedByteCode.__laload: + ilGenerator.Emit(OpCodes.Ldelem_I8); + break; + case NormalizedByteCode.__lastore: + ilGenerator.Emit(OpCodes.Stelem_I8); + break; + case NormalizedByteCode.__faload: + ilGenerator.Emit(OpCodes.Ldelem_R4); + break; + case NormalizedByteCode.__fastore: + ilGenerator.Emit(OpCodes.Stelem_R4); + break; + case NormalizedByteCode.__daload: + ilGenerator.Emit(OpCodes.Ldelem_R8); + break; + case NormalizedByteCode.__dastore: + ilGenerator.Emit(OpCodes.Stelem_R8); + break; + case NormalizedByteCode.__aastore: + ilGenerator.Emit(OpCodes.Stelem_Ref); + break; + case NormalizedByteCode.__arraylength: + ilGenerator.Emit(OpCodes.Ldlen); + break; + case NormalizedByteCode.__lcmp: + { + LocalBuilder value1 = ilGenerator.DeclareLocal(typeof(long)); + LocalBuilder value2 = ilGenerator.DeclareLocal(typeof(long)); + ilGenerator.Emit(OpCodes.Stloc, value2); + ilGenerator.Emit(OpCodes.Stloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label res1 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Bgt_S, res1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label res0 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Beq_S, res0); + ilGenerator.Emit(OpCodes.Ldc_I4_M1); + Label end = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Br_S, end); + ilGenerator.MarkLabel(res1); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Br_S, end); + ilGenerator.MarkLabel(res0); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.MarkLabel(end); + break; + } + case NormalizedByteCode.__fcmpl: + { + LocalBuilder value1 = ilGenerator.DeclareLocal(typeof(float)); + LocalBuilder value2 = ilGenerator.DeclareLocal(typeof(float)); + ilGenerator.Emit(OpCodes.Stloc, value2); + ilGenerator.Emit(OpCodes.Stloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label res1 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Bgt_S, res1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label res0 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Beq_S, res0); + ilGenerator.Emit(OpCodes.Ldc_I4_M1); + Label end = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Br_S, end); + ilGenerator.MarkLabel(res1); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Br_S, end); + ilGenerator.MarkLabel(res0); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.MarkLabel(end); + break; + } + case NormalizedByteCode.__fcmpg: + { + LocalBuilder value1 = ilGenerator.DeclareLocal(typeof(float)); + LocalBuilder value2 = ilGenerator.DeclareLocal(typeof(float)); + ilGenerator.Emit(OpCodes.Stloc, value2); + ilGenerator.Emit(OpCodes.Stloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label resm1 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Blt_S, resm1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label res0 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Beq_S, res0); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + Label end = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Br_S, end); + ilGenerator.MarkLabel(resm1); + ilGenerator.Emit(OpCodes.Ldc_I4_M1); + ilGenerator.Emit(OpCodes.Br_S, end); + ilGenerator.MarkLabel(res0); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.MarkLabel(end); + break; + } + case NormalizedByteCode.__dcmpl: + { + LocalBuilder value1 = ilGenerator.DeclareLocal(typeof(double)); + LocalBuilder value2 = ilGenerator.DeclareLocal(typeof(double)); + ilGenerator.Emit(OpCodes.Stloc, value2); + ilGenerator.Emit(OpCodes.Stloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label res1 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Bgt_S, res1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label res0 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Beq_S, res0); + ilGenerator.Emit(OpCodes.Ldc_I4_M1); + Label end = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Br_S, end); + ilGenerator.MarkLabel(res1); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Br_S, end); + ilGenerator.MarkLabel(res0); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.MarkLabel(end); + break; + } + case NormalizedByteCode.__dcmpg: + { + LocalBuilder value1 = ilGenerator.DeclareLocal(typeof(double)); + LocalBuilder value2 = ilGenerator.DeclareLocal(typeof(double)); + ilGenerator.Emit(OpCodes.Stloc, value2); + ilGenerator.Emit(OpCodes.Stloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label resm1 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Blt, resm1); + ilGenerator.Emit(OpCodes.Ldloc, value1); + ilGenerator.Emit(OpCodes.Ldloc, value2); + Label res0 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Beq, res0); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + Label end = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Br, end); + ilGenerator.MarkLabel(resm1); + ilGenerator.Emit(OpCodes.Ldc_I4, -1); + ilGenerator.Emit(OpCodes.Br, end); + ilGenerator.MarkLabel(res0); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.MarkLabel(end); + break; + } + case NormalizedByteCode.__if_icmpeq: + ilGenerator.Emit(OpCodes.Beq, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__if_icmpne: + ilGenerator.Emit(OpCodes.Bne_Un, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__if_icmple: + ilGenerator.Emit(OpCodes.Ble, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__if_icmplt: + ilGenerator.Emit(OpCodes.Blt, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__if_icmpge: + ilGenerator.Emit(OpCodes.Bge, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__if_icmpgt: + ilGenerator.Emit(OpCodes.Bgt, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__ifle: + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.Emit(OpCodes.Ble, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__iflt: + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.Emit(OpCodes.Blt, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__ifge: + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.Emit(OpCodes.Bge, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__ifgt: + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.Emit(OpCodes.Bgt, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__ifne: + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.Emit(OpCodes.Bne_Un, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__ifeq: + ilGenerator.Emit(OpCodes.Ldc_I4_0); + ilGenerator.Emit(OpCodes.Beq, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__ifnonnull: + ilGenerator.Emit(OpCodes.Brtrue, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__ifnull: + ilGenerator.Emit(OpCodes.Brfalse, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__if_acmpeq: + ilGenerator.Emit(OpCodes.Beq, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__if_acmpne: + ilGenerator.Emit(OpCodes.Bne_Un, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__goto: + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__ineg: + case NormalizedByteCode.__lneg: + case NormalizedByteCode.__fneg: + case NormalizedByteCode.__dneg: + ilGenerator.Emit(OpCodes.Neg); + break; + case NormalizedByteCode.__iadd: + case NormalizedByteCode.__ladd: + case NormalizedByteCode.__fadd: + case NormalizedByteCode.__dadd: + ilGenerator.Emit(OpCodes.Add); + break; + case NormalizedByteCode.__isub: + case NormalizedByteCode.__lsub: + case NormalizedByteCode.__fsub: + case NormalizedByteCode.__dsub: + ilGenerator.Emit(OpCodes.Sub); + break; + case NormalizedByteCode.__ixor: + case NormalizedByteCode.__lxor: + ilGenerator.Emit(OpCodes.Xor); + break; + case NormalizedByteCode.__ior: + case NormalizedByteCode.__lor: + ilGenerator.Emit(OpCodes.Or); + break; + case NormalizedByteCode.__iand: + case NormalizedByteCode.__land: + ilGenerator.Emit(OpCodes.And); + break; + case NormalizedByteCode.__imul: + case NormalizedByteCode.__lmul: + case NormalizedByteCode.__fmul: + case NormalizedByteCode.__dmul: + ilGenerator.Emit(OpCodes.Mul); + break; + case NormalizedByteCode.__idiv: + case NormalizedByteCode.__ldiv: + { + // we need to special case dividing by -1, because the CLR div instruction + // throws an OverflowException when dividing Int32.MinValue by -1, and + // Java just silently overflows + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.Emit(OpCodes.Ldc_I4_M1); + if(instr.NormalizedOpCode == NormalizedByteCode.__ldiv) + { + ilGenerator.Emit(OpCodes.Conv_I8); + } + Label label = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Bne_Un_S, label); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Neg); + Label label2 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Br_S, label2); + ilGenerator.MarkLabel(label); + ilGenerator.Emit(OpCodes.Div); + ilGenerator.MarkLabel(label2); + break; + } + case NormalizedByteCode.__fdiv: + case NormalizedByteCode.__ddiv: + ilGenerator.Emit(OpCodes.Div); + break; + case NormalizedByteCode.__irem: + case NormalizedByteCode.__lrem: + { + // we need to special case taking the remainder of dividing by -1, + // because the CLR rem instruction throws an OverflowException when + // taking the remainder of dividing Int32.MinValue by -1, and + // Java just silently overflows + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.Emit(OpCodes.Ldc_I4_M1); + if(instr.NormalizedOpCode == NormalizedByteCode.__lrem) + { + ilGenerator.Emit(OpCodes.Conv_I8); + } + Label label = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Bne_Un_S, label); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Ldc_I4_0); + if(instr.NormalizedOpCode == NormalizedByteCode.__lrem) + { + ilGenerator.Emit(OpCodes.Conv_I8); + } + Label label2 = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Br_S, label2); + ilGenerator.MarkLabel(label); + ilGenerator.Emit(OpCodes.Rem); + ilGenerator.MarkLabel(label2); + break; + } + case NormalizedByteCode.__frem: + case NormalizedByteCode.__drem: + ilGenerator.Emit(OpCodes.Rem); + break; + case NormalizedByteCode.__ishl: + case NormalizedByteCode.__lshl: + ilGenerator.Emit(OpCodes.Shl); + break; + case NormalizedByteCode.__iushr: + case NormalizedByteCode.__lushr: + ilGenerator.Emit(OpCodes.Shr_Un); + break; + case NormalizedByteCode.__ishr: + case NormalizedByteCode.__lshr: + ilGenerator.Emit(OpCodes.Shr); + break; + case NormalizedByteCode.__swap: + new DupHelper(classLoader, ilGenerator, 2) + .SetType(0, ma.GetRawStackType(i, 0)) + .SetType(1, ma.GetRawStackType(i, 1)) + .Store(0) + .Store(1) + .Load(0) + .Load(1); + break; + case NormalizedByteCode.__dup: + // if the TOS contains a "new" object, it isn't really there, so we wont dup it either + if(ma.GetRawStackType(i, 0)[0] != 'N') + { + ilGenerator.Emit(OpCodes.Dup); + } + break; + case NormalizedByteCode.__dup2: + { + string type1 = ma.GetRawStackType(i, 0); + if(type1 == "D" || type1 == "J") + { + ilGenerator.Emit(OpCodes.Dup); + } + else + { + new DupHelper(classLoader, ilGenerator, 2) + .SetType(0, type1) + .SetType(1, ma.GetRawStackType(i, 1)) + .Store(0) + .Store(1) + .Load(1) + .Load(0) + .Load(1) + .Load(0); + } + break; + } + case NormalizedByteCode.__dup_x1: + new DupHelper(classLoader, ilGenerator, 2) + .SetType(0, ma.GetRawStackType(i, 0)) + .SetType(1, ma.GetRawStackType(i, 1)) + .Store(0) + .Store(1) + .Load(0) + .Load(1) + .Load(0); + break; + case NormalizedByteCode.__dup2_x1: + { + string type1 = ma.GetRawStackType(i, 0); + if(type1 == "D" || type1 == "J") + { + new DupHelper(classLoader, ilGenerator, 2) + .SetType(0, type1) + .SetType(1, ma.GetRawStackType(i, 1)) + .Store(0) + .Store(1) + .Load(0) + .Load(1) + .Load(0); + } + else + { + new DupHelper(classLoader, ilGenerator, 3) + .SetType(0, type1) + .SetType(1, ma.GetRawStackType(i, 1)) + .SetType(2, ma.GetRawStackType(i, 2)) + .Store(0) + .Store(1) + .Store(2) + .Load(1) + .Load(0) + .Load(2) + .Load(1) + .Load(0); + } + break; + } + case NormalizedByteCode.__dup2_x2: + { + string type1 = ma.GetRawStackType(i, 0); + string type2 = ma.GetRawStackType(i, 1); + if(type1 == "D" || type1 == "J") + { + if(type2 == "D" || type2 == "J") + { + // Form 4 + new DupHelper(classLoader, ilGenerator, 2) + .SetType(0, type1) + .SetType(1, type2) + .Store(0) + .Store(1) + .Load(0) + .Load(1) + .Load(0); + } + else + { + // Form 2 + new DupHelper(classLoader, ilGenerator, 3) + .SetType(0, type1) + .SetType(1, type2) + .SetType(2, ma.GetRawStackType(i, 2)) + .Store(0) + .Store(1) + .Store(2) + .Load(0) + .Load(2) + .Load(1) + .Load(0); + } + } + else + { + string type3 = ma.GetRawStackType(i, 2); + if(type3 == "D" || type3 == "J") + { + // Form 3 + new DupHelper(classLoader, ilGenerator, 3) + .SetType(0, type1) + .SetType(1, type2) + .SetType(2, type3) + .Store(0) + .Store(1) + .Store(2) + .Load(1) + .Load(0) + .Load(2) + .Load(1) + .Load(0); + } + else + { + // Form 1 + new DupHelper(classLoader, ilGenerator, 4) + .SetType(0, type1) + .SetType(1, type2) + .SetType(2, type3) + .SetType(3, ma.GetRawStackType(i, 3)) + .Store(0) + .Store(1) + .Store(2) + .Store(3) + .Load(1) + .Load(0) + .Load(3) + .Load(2) + .Load(1) + .Load(0); + } + } + break; + } + case NormalizedByteCode.__dup_x2: + new DupHelper(classLoader, ilGenerator, 3) + .SetType(0, ma.GetRawStackType(i, 0)) + .SetType(1, ma.GetRawStackType(i, 1)) + .SetType(2, ma.GetRawStackType(i, 2)) + .Store(0) + .Store(1) + .Store(2) + .Load(0) + .Load(2) + .Load(1) + .Load(0); + break; + case NormalizedByteCode.__pop2: + { + string type1 = ma.GetRawStackType(i, 0); + if(type1 == "D" || type1 == "J") + { + ilGenerator.Emit(OpCodes.Pop); + } + else + { + if(type1[0] != 'N') + { + ilGenerator.Emit(OpCodes.Pop); + } + if(ma.GetRawStackType(i, 1)[0] != 'N') + { + ilGenerator.Emit(OpCodes.Pop); + } + } + break; + } + case NormalizedByteCode.__pop: + // if the TOS is a new object, it isn't really there, so we don't need to pop it + if(ma.GetRawStackType(i, 0)[0] != 'N') + { + ilGenerator.Emit(OpCodes.Pop); + } + break; + case NormalizedByteCode.__monitorenter: + ilGenerator.Emit(OpCodes.Call, monitorEnterMethod); + break; + case NormalizedByteCode.__monitorexit: + ilGenerator.Emit(OpCodes.Call, monitorExitMethod); + break; + case NormalizedByteCode.__athrow: + ilGenerator.Emit(OpCodes.Throw); + break; + case NormalizedByteCode.__lookupswitch: + // TODO use OpCodes.Switch + for(int j = 0; j < instr.Values.Length; j++) + { + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.Emit(OpCodes.Ldc_I4, instr.Values[j]); + Label label = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Bne_Un, label); + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, instr.PC + instr.TargetOffsets[j], inuse, rangeBegin, rangeEnd, exits)); + ilGenerator.MarkLabel(label); + } + ilGenerator.Emit(OpCodes.Pop); + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, instr.PC + instr.DefaultOffset, inuse, rangeBegin, rangeEnd, exits)); + break; + case NormalizedByteCode.__iinc: + Load(instr, typeof(int)); + ilGenerator.Emit(OpCodes.Ldc_I4, instr.Arg2); + ilGenerator.Emit(OpCodes.Add); + Store(instr, typeof(int)); + break; + case NormalizedByteCode.__i2b: + ilGenerator.Emit(OpCodes.Conv_I1); + break; + case NormalizedByteCode.__i2c: + ilGenerator.Emit(OpCodes.Conv_U2); + break; + case NormalizedByteCode.__i2s: + ilGenerator.Emit(OpCodes.Conv_I2); + break; + case NormalizedByteCode.__l2i: + case NormalizedByteCode.__f2i: + case NormalizedByteCode.__d2i: + ilGenerator.Emit(OpCodes.Conv_I4); + break; + case NormalizedByteCode.__i2l: + case NormalizedByteCode.__f2l: + case NormalizedByteCode.__d2l: + ilGenerator.Emit(OpCodes.Conv_I8); + break; + case NormalizedByteCode.__i2f: + case NormalizedByteCode.__l2f: + case NormalizedByteCode.__d2f: + ilGenerator.Emit(OpCodes.Conv_R4); + break; + case NormalizedByteCode.__i2d: + case NormalizedByteCode.__l2d: + case NormalizedByteCode.__f2d: + ilGenerator.Emit(OpCodes.Conv_R8); + break; + case NormalizedByteCode.__jsr: + { + int index = FindPcIndex(instr.PC + instr.Arg1); + int[] callsites = ma.GetCallSites(index); + for(int j = 0; j < callsites.Length; j++) + { + if(callsites[j] == i) + { + ilGenerator.Emit(OpCodes.Ldc_I4, j); + break; + } + } + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, instr.PC + instr.Arg1, inuse, rangeBegin, rangeEnd, exits)); + break; + } + case NormalizedByteCode.__ret: + { + // NOTE using a OpCodes.Switch here is not efficient, because 99 out of a 100 cases + // there are either one or two call sites. + string subid = ma.GetLocalType(i, instr.Arg1); + int[] callsites = ma.GetCallSites(int.Parse(subid.Substring("Lret;".Length))); + for(int j = 0; j < callsites.Length - 1; j++) + { + Load(instr, typeof(int)); + ilGenerator.Emit(OpCodes.Ldc_I4, j); + ilGenerator.Emit(OpCodes.Beq, GetLabel(labels, m.Instructions[callsites[j] + 1].PC, inuse, rangeBegin, rangeEnd, exits)); + } + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, m.Instructions[callsites[callsites.Length - 1] + 1].PC, inuse, rangeBegin, rangeEnd, exits)); + break; + } + case NormalizedByteCode.__nop: + ilGenerator.Emit(OpCodes.Nop); + break; + default: + throw new NotImplementedException(instr.NormalizedOpCode.ToString()); + } + // mark next instruction as inuse + switch(instr.NormalizedOpCode) + { + case NormalizedByteCode.__lookupswitch: + case NormalizedByteCode.__goto: + case NormalizedByteCode.__jsr: + case NormalizedByteCode.__ret: + case NormalizedByteCode.__ireturn: + case NormalizedByteCode.__lreturn: + case NormalizedByteCode.__freturn: + case NormalizedByteCode.__dreturn: + case NormalizedByteCode.__areturn: + case NormalizedByteCode.__return: + case NormalizedByteCode.__athrow: + break; + default: + // don't fall through end of try block + if(m.Instructions[i + 1].PC == rangeEnd) + { + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, m.Instructions[i + 1].PC, inuse, rangeBegin, rangeEnd, exits)); + } + else + { + inuse[i + 1] = true; + if(done[i + 1]) + { + // since we've already processed the code that is supposed to come next, we have + // to emit a branch to it + ilGenerator.Emit(OpCodes.Br, GetLabel(labels, m.Instructions[i + 1].PC, inuse, rangeBegin, rangeEnd, exits)); + } + } + break; + } + } + } + } + + private FieldWrapper GetField(ClassFile.ConstantPoolItemFieldref cpi, bool isStatic, TypeWrapper thisType, bool write) + { + TypeWrapper wrapper = LoadClass(cpi.Class); + if(wrapper != null) + { + FieldWrapper field = wrapper.GetFieldWrapper(cpi.Name); + if(field != null) + { + if(field.IsStatic == isStatic) + { + if(field.IsPublic || + (field.IsProtected && (isStatic ? clazz.IsSubTypeOf(field.DeclaringType) : clazz.IsSubTypeOf(thisType))) || + (field.IsPrivate && clazz == wrapper) || + (!(field.IsPublic || field.IsPrivate) && clazz.IsInSamePackageAs(field.DeclaringType))) + { + // are we trying to mutate a final field (they are read-only from outside of the defining class) + if(write && field.IsFinal && (isStatic ? clazz != wrapper : clazz != thisType)) + { + EmitError("java.lang.IllegalAccessError", "Field " + cpi.Class + "." + cpi.Name + " is final"); + } + else + { + return field; + } + } + else + { + EmitError("java.lang.IllegalAccessError", "Try to access field " + cpi.Class + "." + cpi.Name + " from class " + clazz.Name); + } + } + else + { + EmitError("java.lang.IncompatibleClassChangeError", null); + } + } + else + { + EmitError("java.lang.NoSuchFieldError", cpi.Class + "." + cpi.Name); + } + } + return null; + } + + private static MethodWrapper GetInterfaceMethod(TypeWrapper wrapper, MethodDescriptor md) + { + MethodWrapper method = wrapper.GetMethodWrapper(md, false); + if(method != null) + { + return method; + } + TypeWrapper[] interfaces = wrapper.Interfaces; + for(int i = 0; i < interfaces.Length; i++) + { + method = GetInterfaceMethod(interfaces[i], md); + if(method != null) + { + return method; + } + } + return null; + } + + private MethodWrapper GetMethod(ClassFile.ConstantPoolItemFMI cpi, TypeWrapper thisType, NormalizedByteCode invoke) + { + // TODO when there is an error resolving a call to the super class constructor (in the constructor of this type), + // we cannot use EmitError, because that will yield an invalid constructor (that doesn't call the superclass constructor) + TypeWrapper wrapper = LoadClass(cpi.Class); + if(wrapper != null) + { + if(wrapper.IsInterface != (invoke == NormalizedByteCode.__invokeinterface)) + { + EmitError("java.lang.IncompatibleClassChangeError", null); + } + else + { + if(invoke == NormalizedByteCode.__invokespecial && m.Method.ClassFile.IsSuper && thisType != wrapper && thisType.IsSubTypeOf(wrapper)) + { + wrapper = thisType.BaseTypeWrapper; + } + MethodDescriptor md = new MethodDescriptor(classLoader, cpi.Name, cpi.Signature); + MethodWrapper method = null; + if(invoke == NormalizedByteCode.__invokeinterface) + { + method = GetInterfaceMethod(wrapper, md); + // NOTE vmspec 5.4.3.4 clearly states that an interfacemethod may also refer to a method in Object + if(method == null) + { + method = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/Object").GetMethodWrapper(md, false); + } + } + else + { + method = wrapper.GetMethodWrapper(md, md.Name != "<init>"); + // if the method is not found, we might have to simulate a Miranda method + if(method == null && invoke == NormalizedByteCode.__invokevirtual) + { + method = GetInterfaceMethod(wrapper, md); + } + } + if(method != null) + { + if(method.IsStatic == (invoke == NormalizedByteCode.__invokestatic)) + { + if(method.IsAbstract && invoke == NormalizedByteCode.__invokespecial) + { + EmitError("java.lang.AbstractMethodError", cpi.Class + "." + cpi.Name + cpi.Signature); + } + else if(method.IsPublic || + (method.IsProtected && (method.IsStatic ? clazz.IsSubTypeOf(method.DeclaringType) : clazz.IsSubTypeOf(thisType))) || + (method.IsPrivate && clazz == method.DeclaringType) || + (!(method.IsPublic || method.IsPrivate) && clazz.IsInSamePackageAs(method.DeclaringType))) + { + return method; + } + else + { + // HACK special case for incorrect invocation of Object.clone(), because this could mean + // we're calling clone() on an array + // (bug in javac, see http://developer.java.sun.com/developer/bugParade/bugs/4329886.html) + if(!method.IsStatic && cpi.Name == "clone" && wrapper.Type == typeof(object) && thisType.Type.IsArray) + { + method = thisType.GetMethodWrapper(new MethodDescriptor(classLoader, cpi.Name, cpi.Signature), false); + if(method != null && method.IsPublic) + { + return method; + } + } + EmitError("java.lang.IllegalAccessError", "Try to access method " + method.DeclaringType.Name + "." + cpi.Name + cpi.Signature + " from class " + clazz.Name); + } + } + else + { + EmitError("java.lang.IncompatibleClassChangeError", null); + } + } + else + { + EmitError("java.lang.NoSuchMethodError", cpi.Class + "." + cpi.Name + cpi.Signature); + } + } + } + return null; + } + + private void EmitError(string errorType, string message) + { + if(message != null) + { + if(JVM.IsStaticCompiler) + { + Console.Error.WriteLine(errorType + ": " + message); + Console.Error.WriteLine("\tat " + m.Method.ClassFile.Name + "." + m.Method.Name + m.Method.Signature); + } + ilGenerator.Emit(OpCodes.Ldstr, message); + TypeWrapper type = classLoader.LoadClassByDottedName(errorType); + MethodWrapper method = type.GetMethodWrapper(new MethodDescriptor(classLoader, "<init>", "(Ljava/lang/String;)V"), false); + method.EmitNewobj.Emit(ilGenerator); + } + else + { + if(JVM.IsStaticCompiler) + { + Console.Error.WriteLine(errorType); + Console.Error.WriteLine("\tat " + m.Method.ClassFile.Name + "." + m.Method.Name + m.Method.Signature); + } + TypeWrapper type = classLoader.LoadClassByDottedName(errorType); + MethodWrapper method = type.GetMethodWrapper(new MethodDescriptor(classLoader, "<init>", "()V"), false); + method.EmitNewobj.Emit(ilGenerator); + } + // we emit a call to ThrowHack instead of a throw instruction, because otherwise the verifier will know + // that execution won't continue at the next instruction, and the emitted code won't be verifiable + ilGenerator.Emit(OpCodes.Call, throwHack); + } + + private TypeWrapper LoadClass(string classname) + { + try + { + TypeWrapper type = classLoader.LoadClassBySlashedName(classname); + if(!type.IsPublic && !clazz.IsInSamePackageAs(type)) + { + // TODO all classnames in error messages should be dotted instead of slashed + EmitError("java.lang.IllegalAccessError", "Try to access class " + classname + " from class " + clazz.Name); + return null; + } + return type; + } + catch(Exception) + { + // TODO we should freeze the exception here, instead of always throwing a NoClassDefFoundError + EmitError("java.lang.NoClassDefFoundError", classname); + return null; + } + } + + private Type ExpressionType(string type) + { + try + { + return classLoader.ExpressionType(type); + } + catch(Exception) + { + // TODO we should freeze the exception here, instead of always throwing a NoClassDefFoundError + EmitError("java.lang.NoClassDefFoundError", type); + return null; + } + } + + private static string SigTypeToClassName(string type, string nullType) + { + switch(type[0]) + { + case 'N': + case 'U': + { + string chop = type.Substring(type.IndexOf(';') + 2); + return chop.Substring(0, chop.Length - 1); + } + case 'L': + if(type == "Lnull") + { + return nullType; + } + else + { + return type.Substring(1, type.Length - 2); + } + case '[': + return type; + default: + throw new InvalidOperationException(); + } + } + + private void EmitPlaceholder(string sig) + { + switch(sig[0]) + { + case 'L': + case '[': + ilGenerator.Emit(OpCodes.Ldnull); + break; + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + ilGenerator.Emit(OpCodes.Ldc_I4_0); + break; + case 'J': + ilGenerator.Emit(OpCodes.Ldc_I8, 0L); + break; + case 'F': + ilGenerator.Emit(OpCodes.Ldc_R4, 0.0f); + break; + case 'D': + ilGenerator.Emit(OpCodes.Ldc_R8, 0.0); + break; + case 'V': + break; + default: + throw new InvalidOperationException(); + } + } + + private int FindPcIndex(int target) + { + return m.PcIndexMap[target]; + } + + private void Load(ClassFile.Method.Instruction instr, Type type) + { + // TODO this check will become more complex, once we support changing the type of an argument 'local' + if(instr.NormalizedArg1 >= m.ArgMap.Length) + { + // OPTIMIZE use short form when possible + ilGenerator.Emit(OpCodes.Ldloc, GetLocal(type, instr.NormalizedArg1)); + } + else + { + int i = m.ArgMap[instr.NormalizedArg1]; + switch(i) + { + case 0: + ilGenerator.Emit(OpCodes.Ldarg_0); + break; + case 1: + ilGenerator.Emit(OpCodes.Ldarg_1); + break; + case 2: + ilGenerator.Emit(OpCodes.Ldarg_2); + break; + case 3: + ilGenerator.Emit(OpCodes.Ldarg_3); + break; + default: + if(i < 256) + { + ilGenerator.Emit(OpCodes.Ldarg_S, (byte)i); + } + else + { + ilGenerator.Emit(OpCodes.Ldarg, (ushort)i); + } + break; + } + } + } + + private void Store(ClassFile.Method.Instruction instr, Type type) + { + // TODO this check will become more complex, once we support changing the type of an argument 'local' + if(instr.NormalizedArg1 >= m.ArgMap.Length) + { + ilGenerator.Emit(OpCodes.Stloc, GetLocal(type, instr.NormalizedArg1)); + } + else + { + int i = m.ArgMap[instr.NormalizedArg1]; + if(i < 256) + { + ilGenerator.Emit(OpCodes.Starg_S, (byte)i); + } + else + { + ilGenerator.Emit(OpCodes.Starg, (ushort)i); + } + } + } + + private LocalBuilder GetLocal(Type type, int index) + { + string name; + if(type.IsValueType) + { + name = type.Name + index; + } + else + { + name = "Obj" + index; + string t = ma.GetDeclaredLocalType(index); + if(t != null && t != "Lnull") + { + // TODO handle class not found + type = classLoader.ExpressionType(t); + } + } + LocalBuilder lb = (LocalBuilder)locals[name]; + if(lb == null) + { + lb = ilGenerator.DeclareLocal(type); + locals[name] = lb; + // the local variable table is disabled, because we need to have + // better support for overloaded indexes to make this usefull + if(JVM.Debug && false) + { + // TODO this should be done better + ClassFile.Method.LocalVariableTableEntry[] table = m.LocalVariableTableAttribute; + if(table != null) + { + for(int i = 0; i < table.Length; i++) + { + if(table[i].index == index) + { + lb.SetLocalSymInfo(table[i].name); + break; + } + } + } + } + } + return lb; + } + + private Label GetLabel(object[] labels, int targetPC, bool[] inuse, int rangeBegin, int rangeEnd, ArrayList exits) + { + if(rangeBegin <= targetPC && targetPC < rangeEnd) + { + inuse[FindPcIndex(targetPC)] = true; + object l = labels[targetPC]; + if(l == null) + { + l = ilGenerator.DefineLabel(); + labels[targetPC] = l; + } + return (Label)l; + } + else + { + BranchCookie bc = new BranchCookie(); + bc.TargetIndex = FindPcIndex(targetPC); + bc.Stub = ilGenerator.DefineLabel(); + exits.Add(bc); + return bc.Stub; + } + } +} diff --git a/IK.VM.NET/ik.vm.net.build b/IK.VM.NET/ik.vm.net.build new file mode 100644 index 00000000..8fbb00da --- /dev/null +++ b/IK.VM.NET/ik.vm.net.build @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<project name="ik.vm.net" default="ik.vm.net"> + <target name="ik.vm.net"> + <csc target="library" output="../bin/ik.vm.net.dll"> + <sources> + <includes name="*.cs" /> + </sources> + <references> + <includes name="../bin/IK.VM.JNI.dll" asis="true" /> + </references> + <arg value="/resource:map.xml" /> + </csc> + </target> +</project> diff --git a/IK.VM.NET/map.xml b/IK.VM.NET/map.xml new file mode 100644 index 00000000..26334044 --- /dev/null +++ b/IK.VM.NET/map.xml @@ -0,0 +1,446 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- + 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 + +--> +<root> + <remappings> + <class name="java.lang.Object" type="System.Object" modifiers="public"> + <constructor sig="()V" modifiers="public" /> + <method name="notifyAll" sig="()V" modifiers="public final"> + <redirect class="ObjectHelper" name="notifyAll" sig="(Ljava/lang/Object;)V" type="static" /> + </method> + <method name="notify" sig="()V" modifiers="public final"> + <redirect class="ObjectHelper" name="notify" sig="(Ljava/lang/Object;)V" type="static" /> + </method> + <method name="wait" sig="()V" modifiers="public final"> + <redirect class="ObjectHelper" name="wait" sig="(Ljava/lang/Object;)V" type="static" /> + </method> + <method name="wait" sig="(J)V" modifiers="public final"> + <redirect class="ObjectHelper" name="wait" sig="(Ljava/lang/Object;J)V" type="static" /> + </method> + <method name="wait" sig="(JI)V" modifiers="public final"> + <redirect class="ObjectHelper" name="wait" sig="(Ljava/lang/Object;JI)V" type="static" /> + </method> + <method name="getClass" sig="()Ljava/lang/Class;" modifiers="public final"> + <redirect class="ObjectHelper" name="getClass" sig="(Ljava/lang/Object;)Ljava/lang/Object;" type="static" /> + </method> + <method name="clone" sig="()Ljava/lang/Object;" modifiers="protected"> + <invokespecial> + <!-- TODO if we already know statically that the type implements java/lang/Cloneable this check can be omitted --> + <dup /> + <call class="ObjectHelper" name="clonecheck" /> + <call class="System.Object" name="MemberwiseClone" /> + </invokespecial> + <invokevirtual> + <call class="ObjectHelper" name="virtualclone" /> + </invokevirtual> + </method> + <!-- virtual methods --> + <!-- TODO every object that doesn't override toString or hashCode will now be called through these + conditional constructs, this is unnecessary (and not very efficient), so I need to figure out a way + to prevent this --> + <method name="toString" sig="()Ljava/lang/String;" modifiers="public"> + <override name="ToString" /> + <invokespecial> + <call class="ObjectHelper" name="toStringSpecial" /> + </invokespecial> + <invokevirtual> + <dup /> + <isinst class="System.Array" /> + <brfalse name="skip" /> + <call class="ObjectHelper" name="toStringSpecial" /> + <br name="end" /> + <label name="skip" /> + <callvirt class="System.Object" name="ToString" /> + <label name="end" /> + </invokevirtual> + </method> + <method name="hashCode" sig="()I" modifiers="public"> + <override name="GetHashCode" /> + <invokevirtual> + <dup /> + <isinst class="System.String" /> + <brfalse name="skip" /> + <call class="StringHelper" name="hashCode" /> + <br name="end" /> + <label name="skip" /> + <callvirt class="System.Object" name="GetHashCode" /> + <label name="end" /> + </invokevirtual> + </method> + <method name="equals" sig="(Ljava/lang/Object;)Z" modifiers="public"> + <override name="Equals" /> + </method> + <method name="finalize" sig="()V" modifiers="protected"> + <override name="Finalize" /> + </method> + </class> + <class name="java.lang.String" type="System.String" modifiers="public final"> + <constructor sig="()V" modifiers="public"> + <!-- TODO --> + <newobj> + <ldstr value="" /> + <call class="System.String" name="Copy" /> + </newobj> + </constructor> + <constructor sig="([C)V" modifiers="public" /> + <constructor sig="([CII)V" modifiers="public" /> + <!-- Package private constructor, that we redirect to static helper --> + <constructor sig="([CIIZ)V" modifiers=""> + <redirect class="StringHelper" name="NewString" type="static" sig="([CIIZ)Ljava/lang/String;" /> + </constructor> + <constructor sig="(Ljava/lang/String;)V" modifiers="public"> + <redirect class="System.String" name="Copy" type="static" sig="(Ljava/lang/String;)Ljava/lang/String;" /> + </constructor> + <constructor sig="(Ljava/lang/StringBuffer;)V" modifiers="public"> + <redirect class="System.Text.StringBuilder" name="ToString" sig="()Ljava/lang/String;" /> + </constructor> + <constructor sig="([B)V" modifiers="public"> + <redirect class="StringHelper" name="NewString" type="static" sig="([B)Ljava/lang/String;" /> + </constructor> + <constructor sig="([BI)V" modifiers="public"> + <redirect class="StringHelper" name="NewString" type="static" sig="([BI)Ljava/lang/String;" /> + </constructor> + <constructor sig="([BII)V" modifiers="public"> + <redirect class="StringHelper" name="NewString" type="static" sig="([BII)Ljava/lang/String;" /> + </constructor> + <constructor sig="([BIII)V" modifiers="public"> + <redirect class="StringHelper" name="NewString" type="static" sig="([BIII)Ljava/lang/String;" /> + </constructor> + <constructor sig="([BLjava/lang/String;)V" modifiers="public"> + <redirect class="StringHelper" name="NewString" type="static" sig="([BLjava/lang/String;)Ljava/lang/String;" /> + </constructor> + <constructor sig="([BIILjava/lang/String;)V" modifiers="public"> + <redirect class="StringHelper" name="NewString" type="static" sig="([BIILjava/lang/String;)Ljava/lang/String;" /> + </constructor> + <method name="hashCode" sig="()I" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;)I" /> + </method> + <method name="valueOf" sig="(Z)Ljava/lang/String;" modifiers="public static"> + <redirect class="StringHelper" /> + </method> + <method name="valueOf" sig="(I)Ljava/lang/String;" modifiers="public static"> + <redirect class="StringHelper" /> + </method> + <method name="valueOf" sig="(J)Ljava/lang/String;" modifiers="public static"> + <redirect class="StringHelper" /> + </method> + <method name="valueOf" sig="(C)Ljava/lang/String;" modifiers="public static"> + <redirect class="StringHelper" /> + </method> + <method name="valueOf" sig="(F)Ljava/lang/String;" modifiers="public static"> + <redirect class="StringHelper" /> + </method> + <method name="valueOf" sig="(D)Ljava/lang/String;" modifiers="public static"> + <redirect class="StringHelper" /> + </method> + <method name="valueOf" sig="(Ljava/lang/Object;)Ljava/lang/String;" modifiers="public static"> + <redirect class="StringHelper" /> + </method> + <method name="substring" sig="(I)Ljava/lang/String;" modifiers="public"> + <redirect name="Substring" /> + </method> + <method name="length" sig="()I" modifiers="public"> + <redirect name="get_Length" /> + </method> + <method name="charAt" sig="(I)C" modifiers="public"> + <redirect name="get_Chars" /> + </method> + <method name="substring" sig="(II)Ljava/lang/String;" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;II)Ljava/lang/String;" /> + </method> + <method name="indexOf" sig="(I)I" modifiers="public"> + <redirect name="IndexOf" sig="(C)I" /> + </method> + <method name="indexOf" sig="(II)I" modifiers="public"> + <redirect name="IndexOf" sig="(CI)I" /> + </method> + <method name="indexOf" sig="(Ljava/lang/String;)I" modifiers="public"> + <redirect name="IndexOf" /> + </method> + <method name="indexOf" sig="(Ljava/lang/String;I)I" modifiers="public"> + <redirect name="IndexOf" /> + </method> + <method name="lastIndexOf" sig="(I)I" modifiers="public"> + <redirect name="LastIndexOf" sig="(C)I" /> + </method> + <method name="lastIndexOf" sig="(II)I" modifiers="public"> + <redirect name="LastIndexOf" sig="(CI)I" /> + </method> + <method name="lastIndexOf" sig="(Ljava/lang/String;)I" modifiers="public"> + <redirect name="LastIndexOf" /> + </method> + <method name="toCharArray" sig="()[C" modifiers="public"> + <redirect name="ToCharArray" /> + </method> + <method name="getChars" sig="(II[CI)V" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;II[CI)V" /> + </method> + <method name="startsWith" sig="(Ljava/lang/String;)Z" modifiers="public"> + <redirect name="StartsWith" /> + </method> + <method name="startsWith" sig="(Ljava/lang/String;I)Z" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;Ljava/lang/String;I)Z" /> + </method> + <method name="endsWith" sig="(Ljava/lang/String;)Z" modifiers="public"> + <redirect name="EndsWith" /> + </method> + <method name="toUpperCase" sig="()Ljava/lang/String;" modifiers="public"> + <redirect name="ToUpper" /> + </method> + <method name="toUpperCase" sig="(Ljava/util/Locale;)Ljava/lang/String;" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;" /> + </method> + <method name="toLowerCase" sig="()Ljava/lang/String;" modifiers="public"> + <redirect name="ToLower" /> + </method> + <method name="compareToIgnoreCase" sig="(Ljava/lang/String;)I" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;Ljava/lang/String;)I" /> + </method> + <method name="equalsIgnoreCase" sig="(Ljava/lang/String;)Z" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;Ljava/lang/String;)Z" /> + </method> + <method name="intern" sig="()Ljava/lang/String;" modifiers="public"> + <redirect type="static" name="Intern" sig="(Ljava/lang/String;)Ljava/lang/String;" /> + </method> + <method name="compareTo" sig="(Ljava/lang/String;)I" modifiers="public"> + <redirect name="CompareTo" /> + </method> + <method name="replace" sig="(CC)Ljava/lang/String;" modifiers="public"> + <redirect name="Replace" /> + </method> + <method name="getBytes" sig="()[B" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;)[B" /> + </method> + <method name="getBytes" sig="(Ljava/lang/String;)[B" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;Ljava/lang/String;)[B" /> + </method> + <method name="subSequence" sig="(II)Ljava/lang/CharSequence;" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;II)Ljava/lang/Object;" /> + </method> + <method name="trim" sig="()Ljava/lang/String;" modifiers="public"> + <redirect name="Trim" /> + </method> + <method name="regionMatches" sig="(ZILjava/lang/String;II)Z" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;ZILjava/lang/String;II)Z" /> + </method> + <method name="regionMatches" sig="(ILjava/lang/String;II)Z" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;ILjava/lang/String;II)Z" /> + </method> + <method name="getBytes" sig="(II[BI)V" modifiers="public"> + <redirect class="StringHelper" type="static" sig="(Ljava/lang/String;II[BI)V" /> + </method> + <method name="concat" sig="(Ljava/lang/String;)Ljava/lang/String;" modifiers="public"> + <redirect class="System.String" type="static" name="Concat" sig="(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" /> + </method> + <method name="copyValueOf" sig="([CII)Ljava/lang/String;" modifiers="public static"> + <invokestatic> + <newobj class="System.String" name=".ctor" sig="([CII)V" /> + </invokestatic> + </method> + <!-- NOTE we're redirecting fields to static methods here! + NOTE only reading fields can be redirected + --> + <field name="count" sig="I" modifiers=""> + <redirect class="StringHelper" type="static" name="GetCountField" sig="(Ljava/lang/String;)I" /> + </field> + <!-- Making a new char array instead of directly accessing the contents of the string, probably isn't + as efficient as the coder of this construct wished for, but hey ;-) At least it works... + TODO we should probably make StringBuffer a native class, to make it perform better + NOTE since StringBuffer is remapped to StringBuilder now, these might not be needed anymore + --> + <method name="zeroBasedStringValue" sig="(Ljava/lang/String;)[C" modifiers="static"> + <redirect class="StringHelper" name="GetValueField" /> + </method> + <field name="value" sig="[C" modifiers=""> + <redirect class="StringHelper" type="static" name="GetValueField" sig="(Ljava/lang/String;)[C" /> + </field> + <field name="offset" sig="I" modifiers=""> + <redirect class="StringHelper" type="static" name="GetOffsetField" sig="(Ljava/lang/String;)I" /> + </field> + </class> + <!-- TODO it is incorrect to map StringBuffer to StringBuilder, because StringBuffer is thread safe, puke! --> + <class name="java.lang.StringBuffer" type="System.Text.StringBuilder" modifiers="public final"> + <constructor sig="()V" modifiers="public" /> + <constructor sig="(Ljava/lang/String;)V" modifiers="public" /> + <constructor sig="(I)V" modifiers="public" /> + <method name="length" sig="()I" modifiers="public"> + <redirect name="get_Length" /> + </method> + <method name="setLength" sig="(I)V" modifiers="public"> + <redirect name="set_Length" /> + </method> + <method name="ensureCapacity" sig="(I)V" modifiers="public"> + <invokevirtual> + <call class="System.Text.StringBuilder" name="EnsureCapacity" /> + <pop /> + </invokevirtual> + <invokespecial> + <call class="System.Text.StringBuilder" name="EnsureCapacity" /> + <pop /> + </invokespecial> + </method> + <method name="append" sig="(Ljava/lang/Object;)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;Ljava/lang/Object;)Ljava/lang/StringBuffer;" /> + </method> + <method name="append" sig="(Ljava/lang/String;)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;Ljava/lang/String;)Ljava/lang/StringBuffer;" /> + </method> + <method name="append" sig="(Z)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;Z)Ljava/lang/StringBuffer;" /> + </method> + <method name="append" sig="(C)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect name="Append" /> + </method> + <method name="append" sig="(I)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect name="Append" /> + </method> + <method name="append" sig="(J)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect name="Append" /> + </method> + <method name="append" sig="(F)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;F)Ljava/lang/StringBuffer;" /> + </method> + <method name="append" sig="(D)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;D)Ljava/lang/StringBuffer;" /> + </method> + <method name="append" sig="([C)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect name="Append" /> + </method> + <method name="append" sig="([CII)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect name="Append" /> + </method> + <method name="insert" sig="(IC)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect name="Insert" /> + </method> + <method name="insert" sig="(ILjava/lang/String;)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;ILjava/lang/String;)Ljava/lang/StringBuffer;" /> + </method> + <method name="replace" sig="(IILjava/lang/String;)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;IILjava/lang/String;)Ljava/lang/StringBuffer;" /> + </method> + <method name="delete" sig="(II)Ljava/lang/StringBuffer;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;II)Ljava/lang/StringBuffer;" /> + </method> + <method name="setCharAt" sig="(IC)V" modifiers="public"> + <redirect name="set_Chars" /> + </method> + <method name="charAt" sig="(I)C" modifiers="public"> + <redirect name="get_Chars" /> + </method> + <method name="substring" sig="(II)Ljava/lang/String;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;II)Ljava/lang/String;" /> + </method> + <method name="substring" sig="(I)Ljava/lang/String;" modifiers="public"> + <redirect class="StringBufferHelper" type="static" sig="(Ljava/lang/StringBuffer;I)Ljava/lang/String;" /> + </method> + </class> + <class name="java.lang.Throwable" type="System.Exception" modifiers="public"> + <constructor sig="()V" modifiers="public"> + <invokespecial> + <call class="ExceptionHelper" name="get_NullString" /> + <call class="System.Exception" name=".ctor" sig="(Ljava/lang/String;)V" /> + </invokespecial> + </constructor> + <constructor sig="(Ljava/lang/String;)V" modifiers="public"> + <invokespecial> + <call class="ExceptionHelper" name="FilterMessage" /> + <call class="System.Exception" name=".ctor" sig="(Ljava/lang/String;)V" /> + </invokespecial> + </constructor> + <constructor sig="(Ljava/lang/String;Ljava/lang/Throwable;)V" modifiers="public"> + <invokespecial> + <stloc name="x" class="System.Exception" /> + <call class="ExceptionHelper" name="FilterMessage" /> + <ldloc name="x" /> + <call class="System.Exception" name=".ctor" sig="(Ljava/lang/String;Ljava/lang/Throwable;)V" /> + </invokespecial> + </constructor> + <constructor sig="(Ljava/lang/Throwable;)V" modifiers="public"> + <invokespecial> + <stloc name="x" class="System.Exception" /> + <ldloc name="x" /> + <call class="ExceptionHelper" name="GetMessageFromCause" /> + <ldloc name="x" /> + <call class="System.Exception" name=".ctor" sig="(Ljava/lang/String;Ljava/lang/Throwable;)V" /> + </invokespecial> + </constructor> + <method type="virtual" name="printStackTrace" sig="()V" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;)V" type="static" /> + </method> + <method type="virtual" name="printStackTrace" sig="(Ljava/io/PrintStream;)V" modifiers="public"> + <!-- NOTE both printStackTrace(Ljava/io/PrintStream;)V & printStackTrace(Ljava/io/PrintWriter;)V redirect + to the same method, this is not a bug --> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;Ljava/lang/Object;)V" type="static" /> + </method> + <method type="virtual" name="printStackTrace" sig="(Ljava/io/PrintWriter;)V" modifiers="public"> + <!-- NOTE both printStackTrace(Ljava/io/PrintStream;)V & printStackTrace(Ljava/io/PrintWriter;)V redirect + to the same method, this is not a bug --> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;Ljava/lang/Object;)V" type="static" /> + </method> + <method type="virtual" name="getMessage" sig="()Ljava/lang/String;" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;)Ljava/lang/String;" type="static" /> + </method> + <method type="virtual" name="getLocalizedMessage" sig="()Ljava/lang/String;" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;)Ljava/lang/String;" type="static" /> + </method> + <method type="virtual" name="fillInStackTrace" sig="()Ljava/lang/Throwable;" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;)Ljava/lang/Throwable;" type="static" /> + </method> + <method type="virtual" name="initCause" sig="(Ljava/lang/Throwable;)Ljava/lang/Throwable;" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;Ljava/lang/Throwable;)Ljava/lang/Throwable;" type="static" /> + </method> + <method type="virtual" name="getCause" sig="()Ljava/lang/Throwable;" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;)Ljava/lang/Throwable;" type="static" /> + </method> + <method type="virtual" name="getStackTrace" sig="()[Ljava/lang/StackTraceElement;" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;)[Ljava/lang/StackTraceElement;" type="static" /> + </method> + <method type="virtual" name="setStackTrace" sig="([Ljava/lang/StackTraceElement;)V" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;[Ljava/lang/StackTraceElement;)V" type="static" /> + </method> + <method type="virtual" name="toString" sig="()Ljava/lang/String;" modifiers="public"> + <redirect class="ExceptionHelper" sig="(Ljava/lang/Throwable;)Ljava/lang/String;" type="static" /> + </method> + </class> + <class name="java.lang.Comparable" type="System.IComparable" modifiers="public interface"> + <method name="compareTo" sig="(Ljava/lang/Object;)I" modifiers="public"> + <override name="CompareTo" /> + </method> + </class> + <!-- TODO java.lang.StackTraceElement shouldn't be here, but it should be represented by a different TypeWrapper (that can take + a .NET type and present it as a Java type --> + <class name="java.lang.StackTraceElement" type="java.lang.StackTraceElement" modifiers="public final"> + <!-- TODO fill out the methods --> + </class> + </remappings> + <nativeMethods> + <class name="java.lang.VMSystem"> + <method name="identityHashCode" sig="(Ljava/lang/Object;)I"> + <ldarg_0 /> + <call class="System.Object" name="GetHashCode" /> + <ret /> + </method> + </class> + </nativeMethods> +</root>
\ No newline at end of file diff --git a/IK.VM.NET/profiler.cs b/IK.VM.NET/profiler.cs new file mode 100644 index 00000000..fb6f75fc --- /dev/null +++ b/IK.VM.NET/profiler.cs @@ -0,0 +1,79 @@ +/* + 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 + +*/ +using System; +using System.Collections; +using System.Diagnostics; + +class Profiler +{ + private static Profiler instance = new Profiler(); + private static Hashtable counters = new Hashtable(); + private static Stack stack = new Stack(); + + private class Entry + { + internal long Time; + internal long Count; + } + + ~Profiler() + { + foreach(DictionaryEntry e in counters) + { + Console.WriteLine("{0} was executed {1} times for a total of {2} ms", e.Key, ((Entry)e.Value).Count, ((Entry)e.Value).Time / 10000); + } + } + + [Conditional("PROFILE")] + internal static void Enter(string name) + { + long ticks = DateTime.Now.Ticks; + if(stack.Count > 0) + { + ((Entry)stack.Peek()).Time += ticks; + } + Entry e = (Entry)counters[name]; + if(e == null) + { + e = new Entry(); + counters[name] = e; + } + e.Count++; + e.Time -= ticks; + stack.Push(e); + } + + [Conditional("PROFILE")] + internal static void Leave(string name) + { + long ticks = DateTime.Now.Ticks; + stack.Pop(); + Entry e = (Entry)counters[name]; + e.Time += ticks; + if(stack.Count > 0) + { + ((Entry)stack.Peek()).Time -= ticks; + } + } +} diff --git a/IK.VM.NET/remapper.cs b/IK.VM.NET/remapper.cs new file mode 100644 index 00000000..8156717e --- /dev/null +++ b/IK.VM.NET/remapper.cs @@ -0,0 +1,387 @@ +using System; +using System.Xml.Serialization; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +namespace MapXml +{ + public abstract class Instruction + { + internal abstract void Generate(Hashtable context, ILGenerator ilgen); + } + + [XmlType("ldstr")] + public sealed class Ldstr : Instruction + { + [XmlAttribute("value")] + public string Value; + + internal override void Generate(Hashtable context, ILGenerator ilgen) + { + ilgen.Emit(OpCodes.Ldstr, Value); + } + } + + [XmlType("call")] + public class Call : Instruction + { + public Call() : this(OpCodes.Call) + { + } + + internal Call(OpCode opcode) + { + this.opcode = opcode; + } + + [XmlAttribute("class")] + public string Class; + [XmlAttribute("name")] + public string Name; + [XmlAttribute("sig")] + public string Sig; + + private MethodBase method; + private OpCode opcode; + + internal sealed override void Generate(Hashtable context, ILGenerator ilgen) + { + if(method == null) + { + string name = Name; + if(name == ".ctor") + { + string sig = Sig; + Type[] argTypes = ClassLoaderWrapper.GetBootstrapClassLoader().ArgTypeListFromSig(sig); + method = Type.GetType(Class, true).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard, argTypes, null); + } + else + { + method = Type.GetType(Class, true).GetMethod(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + } + if(method == null) + { + throw new InvalidOperationException("method not found: " + name); + } + } + if(method.IsConstructor) + { + ilgen.Emit(opcode, (ConstructorInfo)method); + } + else + { + ilgen.Emit(opcode, (MethodInfo)method); + } + } + } + + [XmlType("callvirt")] + public sealed class Callvirt : Call + { + public Callvirt() : base(OpCodes.Callvirt) + { + } + } + + [XmlType("newobj")] + public sealed class NewObj : Call + { + public NewObj() : base(OpCodes.Newobj) + { + } + } + + public abstract class Simple : Instruction + { + private OpCode opcode; + + public Simple(OpCode opcode) + { + this.opcode = opcode; + } + + internal sealed override void Generate(Hashtable context, ILGenerator ilgen) + { + ilgen.Emit(opcode); + } + } + + [XmlType("dup")] + public sealed class Dup : Simple + { + public Dup() : base(OpCodes.Dup) + { + } + } + + [XmlType("pop")] + public sealed class Pop : Simple + { + public Pop() : base(OpCodes.Pop) + { + } + } + + [XmlType("isinst")] + public sealed class IsInst : Instruction + { + [XmlAttribute("class")] + public string Class; + + private Type type; + + internal override void Generate(Hashtable context, ILGenerator ilgen) + { + if(type == null) + { + type = Type.GetType(Class, true); + } + ilgen.Emit(OpCodes.Isinst, type); + } + } + + public abstract class Branch : Instruction + { + private OpCode opcode; + + public Branch(OpCode opcode) + { + this.opcode = opcode; + } + + internal sealed override void Generate(Hashtable context, ILGenerator ilgen) + { + Label l; + if(context[Name] == null) + { + l = ilgen.DefineLabel(); + context[Name] = l; + } + else + { + l = (Label)context[Name]; + } + ilgen.Emit(opcode, l); + } + + [XmlAttribute("name")] + public string Name; + } + + [XmlType("brfalse")] + public sealed class BrFalse : Branch + { + public BrFalse() : base(OpCodes.Brfalse) + { + } + } + + [XmlType("br")] + public sealed class Br : Branch + { + public Br() : base(OpCodes.Br) + { + } + } + + [XmlType("label")] + public sealed class BrLabel : Instruction + { + [XmlAttribute("name")] + public string Name; + + internal override void Generate(Hashtable context, ILGenerator ilgen) + { + Label l; + if(context[Name] == null) + { + l = ilgen.DefineLabel(); + context[Name] = l; + } + else + { + l = (Label)context[Name]; + } + ilgen.MarkLabel(l); + } + } + + [XmlType("stloc")] + public sealed class StLoc : Instruction + { + [XmlAttribute("name")] + public string Name; + [XmlAttribute("class")] + public string Class; + + private Type type; + + internal override void Generate(Hashtable context, ILGenerator ilgen) + { + LocalBuilder lb = (LocalBuilder)context[Name]; + if(lb == null) + { + if(type == null) + { + type = Type.GetType(Class, true); + } + lb = ilgen.DeclareLocal(type); + context[Name] = lb; + } + ilgen.Emit(OpCodes.Stloc, lb); + } + } + + [XmlType("ldloc")] + public sealed class LdLoc : Instruction + { + [XmlAttribute("name")] + public string Name; + + internal override void Generate(Hashtable context, ILGenerator ilgen) + { + ilgen.Emit(OpCodes.Ldloc, (LocalBuilder)context[Name]); + } + } + + [XmlType("ldarg_0")] + public sealed class LdArg_0 : Simple + { + public LdArg_0() : base(OpCodes.Ldarg_0) + { + } + } + + [XmlType("ret")] + public sealed class Ret : Simple + { + public Ret() : base(OpCodes.Ret) + { + } + } + + public class InstructionList : CodeEmitter + { + [XmlElement(typeof(Ldstr))] + [XmlElement(typeof(Call))] + [XmlElement(typeof(Callvirt))] + [XmlElement(typeof(Dup))] + [XmlElement(typeof(Pop))] + [XmlElement(typeof(IsInst))] + [XmlElement(typeof(BrFalse))] + [XmlElement(typeof(Br))] + [XmlElement(typeof(BrLabel))] + [XmlElement(typeof(NewObj))] + [XmlElement(typeof(StLoc))] + [XmlElement(typeof(LdLoc))] + [XmlElement(typeof(LdArg_0))] + [XmlElement(typeof(Ret))] + public Instruction[] invoke; + + internal sealed override void Emit(ILGenerator ilgen) + { + Hashtable context = new Hashtable(); + for(int i = 0; i < invoke.Length; i++) + { + invoke[i].Generate(context, ilgen); + } + } + } + + public class Constructor + { + [XmlAttribute("sig")] + public string Sig; + [XmlAttribute("modifiers")] + public MapModifiers Modifiers; + public InstructionList invokespecial; + public InstructionList newobj; + public Redirect redirect; + } + + public class Redirect + { + [XmlAttribute("class")] + public string Class; + [XmlAttribute("name")] + public string Name; + [XmlAttribute("sig")] + public string Sig; + [XmlAttribute("type")] + public string Type; + } + + public class Override + { + [XmlAttribute("name")] + public string Name; + } + + public class Method : InstructionList + { + [XmlAttribute("name")] + public string Name; + [XmlAttribute("sig")] + public string Sig; + [XmlAttribute("modifiers")] + public MapModifiers Modifiers; + [XmlAttribute("type")] + public string Type; + public InstructionList invokevirtual; + public InstructionList invokespecial; + public InstructionList invokestatic; + public Redirect redirect; + public Override @override; + } + + public class Field + { + [XmlAttribute("name")] + public string Name; + [XmlAttribute("sig")] + public string Sig; + [XmlAttribute("modifiers")] + public MapModifiers Modifiers; + public Redirect redirect; + } + + [Flags] + public enum MapModifiers + { + [XmlEnum("public")] + Public = Modifiers.Public, + [XmlEnum("protected")] + Protected = Modifiers.Protected, + [XmlEnum("final")] + Final = Modifiers.Final, + [XmlEnum("interface")] + Interface = Modifiers.Interface, + [XmlEnum("static")] + Static = Modifiers.Static + } + + [XmlType("class")] + public class Class + { + [XmlAttribute("name")] + public string Name; + [XmlAttribute("type")] + public string Type; + [XmlAttribute("modifiers")] + public MapModifiers Modifiers; + [XmlElement("constructor")] + public Constructor[] Constructors; + [XmlElement("method")] + public Method[] Methods; + [XmlElement("field")] + public Field[] Fields; + } + + [XmlRoot("root")] + public class Root + { + public Class[] remappings; + public Class[] nativeMethods; + } +} diff --git a/IK.VM.NET/verifier.cs b/IK.VM.NET/verifier.cs new file mode 100644 index 00000000..3913fc66 --- /dev/null +++ b/IK.VM.NET/verifier.cs @@ -0,0 +1,2185 @@ +/* + 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 + +*/ +using System; +using System.IO; +using System.Collections; + +class VerifyError : ApplicationException +{ + internal int ByteCodeOffset; + internal string Class; + internal string Method; + internal string Signature; + internal string Instruction; + + internal VerifyError() + { + } + + internal VerifyError(string msg) : base(msg) + { + } +} + +class Subroutine +{ + private int subroutineIndex; + private bool[] localsModified; + + private Subroutine(int subroutineIndex, bool[] localsModified) + { + this.subroutineIndex = subroutineIndex; + this.localsModified = localsModified; + } + + internal Subroutine(int subroutineIndex, int maxLocals) + { + this.subroutineIndex = subroutineIndex; + localsModified = new bool[maxLocals]; + } + + internal int SubroutineIndex + { + get + { + return subroutineIndex; + } + } + + internal bool[] LocalsModified + { + get + { + return localsModified; + } + } + + internal void SetLocalModified(int local) + { + localsModified[local] = true; + } + + internal Subroutine Copy() + { + return new Subroutine(subroutineIndex, (bool[])localsModified.Clone()); + } +} + +class InstructionState +{ + private static ArrayList empty = new ArrayList(); + private MethodAnalyzer ma; + private ArrayList stack; // each entry contains a string with a Java signature of the type + private string[] locals; // each entry contains a string with a Java signature of the type + private ArrayList subroutines; + private int callsites; + internal bool changed = true; + + private InstructionState(MethodAnalyzer ma, ArrayList stack, string[] locals, ArrayList subroutines, int callsites) + { + this.ma = ma; + this.stack = stack; + this.locals = locals; + this.subroutines = subroutines; + this.callsites = callsites; + } + + internal InstructionState(MethodAnalyzer ma, int maxLocals) + { + this.ma = ma; + this.stack = new ArrayList(); + this.locals = new string[maxLocals]; + this.subroutines = new ArrayList(); + } + + internal InstructionState Copy() + { + return new InstructionState(ma, (ArrayList)stack.Clone(), (string[])locals.Clone(), CopySubroutines(subroutines), callsites); + } + + internal InstructionState CopyLocalsAndSubroutines() + { + return new InstructionState(ma, new ArrayList(), (string[])locals.Clone(), CopySubroutines(subroutines), callsites); + } + + private ArrayList CopySubroutines(ArrayList l) + { + ArrayList n = new ArrayList(l.Count); + foreach(Subroutine s in l) + { + n.Add(s.Copy()); + } + return n; + } + + public static InstructionState operator+(InstructionState s1, InstructionState s2) + { + return Merge(s1, s2, null, null); + } + + internal static InstructionState Merge(InstructionState s1, InstructionState s2, bool[] locals_modified, InstructionState s3) + { + if(s1 == null) + { + s2 = s2.Copy(); + if(locals_modified != null) + { + for(int i = 0; i < s2.locals.Length; i++) + { + if(!locals_modified[i]) + { + s2.locals[i] = s3.locals[i]; + } + } + } + return s2; + } + if(s1.stack.Count != s2.stack.Count) + { + throw new VerifyError(string.Format("Inconsistent stack height: {0} != {1}", s1.stack.Count, s2.stack.Count)); + } + InstructionState s = s1.Copy(); + s.changed = s1.changed; + for(int i = 0; i < s.stack.Count; i++) + { + string type = (string)s.stack[i]; + if(type == (string)s2.stack[i]) + { + // perfect match, nothing to do + } + else if(type[0] == 'L' || type[0] == '[' || type[0] == 'U' || type[0] == 'N') + { + string baseType = s.FindCommonBaseType(type, (string)s2.stack[i]); + if(type != baseType) + { + s.stack[i] = baseType; + s.changed = true; + } + } + else + { + throw new VerifyError(string.Format("cannot merge {0} and {1}", type, s2.stack[i])); + } + } + for(int i = 0; i < s.locals.Length; i++) + { + string type = s.locals[i]; + string type2; + if(locals_modified == null || locals_modified[i]) + { + type2 = s2.locals[i]; + } + else + { + type2 = s3.locals[i]; + } + if(type == type2) + { + // perfect match, nothing to do + } + else if(type != null) + { + if(type[0] == 'L' || type[0] == '[' || type[0] == 'U' || type[0] == 'N') + { + string baseType = s2.FindCommonBaseType(type, type2); + if(type != baseType) + { + s.locals[i] = baseType; + s.changed = true; + } + } + else + { + // mark the slot as invalid + s.locals[i] = null; + s.changed = true; + } + } + } + foreach(Subroutine ss2 in s2.subroutines) + { + bool found = false; + foreach(Subroutine ss in s.subroutines) + { + if(ss.SubroutineIndex == ss2.SubroutineIndex) + { + for(int i = 0; i < ss.LocalsModified.Length; i++) + { + if(ss2.LocalsModified[i] && !ss.LocalsModified[i]) + { + ss.LocalsModified[i] = true; + s.changed = true; + } + } + found = true; + } + } + if(!found) + { + s.subroutines.Add(ss2.Copy()); + s.changed = true; + } + } + if(s2.callsites > s.callsites) + { + //Console.WriteLine("s2.callsites = {0}, s.callsites = {1}", s2.callsites, s.callsites); + s.callsites = s2.callsites; + s.changed = true; + } + return s; + } + + internal void AddCallSite() + { + callsites++; + changed = true; + } + + internal void SetSubroutineId(int subroutineIndex) + { + foreach(Subroutine s in subroutines) + { + if(s.SubroutineIndex == subroutineIndex) + { + // subroutines cannot recursivly call themselves + throw new VerifyError("subroutines cannot recurse"); + } + } + subroutines.Add(new Subroutine(subroutineIndex, locals.Length)); + } + + internal bool[] ClearSubroutineId(int subroutineIndex) + { + foreach(Subroutine s in subroutines) + { + if(s.SubroutineIndex == subroutineIndex) + { + subroutines.Remove(s); + return s.LocalsModified; + } + } + throw new VerifyError(); + } + + internal void CheckSubroutineActive(int subroutineIndex) + { + foreach(Subroutine s in subroutines) + { + if(s.SubroutineIndex == subroutineIndex) + { + return; + } + } + throw new VerifyError(); + } + + private bool IsSubType(string subType, string baseType) + { + if(subType == baseType) + { + return true; + } + if(subType.Length == 1 || baseType.Length == 1) + { + // primitives can never be subtypes of another type + return false; + } + if(baseType == "Ljava/lang/Object;") + { + return true; + } + if(baseType[0] == '[') + { + if(subType[0] != '[') + { + return false; + } + int subDepth = 0; + while(subType[subDepth] == '[') + { + subDepth++; + } + int baseDepth = 0; + while(baseType[baseDepth] == '[') + { + baseDepth++; + } + if(baseDepth > subDepth) + { + return false; + } + if(subType[subDepth] == baseType[baseDepth]) + { + if(subType[subDepth] != 'L') + { + return baseDepth == subDepth; + } + string baseElemType = baseType.Substring(baseDepth + 1, baseType.Length - baseDepth - 2); + if(baseElemType == "java/lang/Object") + { + return true; + } + if(baseDepth == subDepth) + { + string subElemType = subType.Substring(subDepth + 1, subType.Length - subDepth - 2); + return ma.classLoader.IsSubType(subElemType, baseElemType); + } + } + return false; + } + else if(subType[0] == '[') + { + return false; + } + return ma.classLoader.IsSubType(subType.Substring(1, subType.Length - 2), baseType.Substring(1, baseType.Length - 2)); + } + + internal string FindCommonBaseType(string type1, string type2) + { +// Console.WriteLine("FindCommonBaseType: {0} v {1}", type1, type2); + if(type1 == "Lnull") + { + return type2; + } + if(type2 == "Lnull") + { + return type1; + } + if(type1 == type2) + { + return type1; + } + if(type1 == null || type2 == null) + { + return null; + } + if(type1.Length == 1 || type2.Length == 1) + { + return null; + } + if(type1[0] == '[' || type2[0] == '[') + { + int rank1 = 0; + while(type1[rank1] == '[') + { + rank1++; + } + int rank2 = 0; + while(type2[rank2] == '[') + { + rank2++; + } + if(rank1 == 0 || rank2 == 0) + { + return "Ljava/lang/Object;"; + } + if(rank1 != rank2) + { + if(rank1 > rank2) + { + int temp = rank1; + rank1 = rank2; + rank2 = temp; + string temps = type1; + type1 = type2; + type2 = temps; + } + if(type1.EndsWith("Ljava/lang/Object;")) + { + return type1; + } + return "Ljava/lang/Object;"; + } + else + { + if(type1[rank1] != type2[rank1]) + { + // two different primitive arrays, or a primitive and a reference array + return "Ljava/lang/Object;"; + } + return new String('[', rank1) + "L" + ma.classLoader.FindCommonBaseType(type1.Substring(1 + rank1, type1.Length - (2 + rank1)), type2.Substring(1 + rank2, type2.Length - (2 + rank2))) + ";"; + } + } + if(type1.StartsWith("Lret;") || type2.StartsWith("Lret;")) + { + return null; + } + return "L" + ma.classLoader.FindCommonBaseType(type1.Substring(1, type1.Length - 2), type2.Substring(1, type2.Length - 2)) + ";"; + } + + private void SetLocal1(int index, string type) + { + if(index > 0 && (locals[index] == "D2" || locals[index] == "J2")) + { + locals[index - 1] = null; + } + locals[index] = type; + foreach(Subroutine s in subroutines) + { + s.SetLocalModified(index); + } + } + + private void SetLocal2(int index, string type) + { + if(index > 0 && (locals[index] == "D2" || locals[index] == "J2")) + { + locals[index - 1] = null; + } + locals[index] = type; + locals[index + 1] = type[0] == 'D' ? "D2" : "J2"; + foreach(Subroutine s in subroutines) + { + s.SetLocalModified(index); + s.SetLocalModified(index + 1); + } + } + + internal void GetLocalInt(int index) + { + if(locals[index] != "I") + { + throw new VerifyError("Invalid local type"); + } + } + + internal void SetLocalInt(int index) + { + SetLocal1(index, "I"); + } + + internal void SetLocalLong(int index) + { + SetLocal2(index, "J"); + } + + internal void GetLocalLong(int index) + { + if(locals[index] != "J" || locals[index + 1] != "J2") + { + throw new VerifyError(); + } + } + + internal void GetLocalFloat(int index) + { + if(locals[index] != "F") + { + throw new VerifyError(); + } + } + + internal void SetLocalFloat(int index) + { + SetLocal1(index, "F"); + } + + internal void SetLocalDouble(int index) + { + SetLocal2(index, "D"); + } + + internal void GetLocalDouble(int index) + { + if(locals[index] != "D" || locals[index + 1] != "D2") + { + throw new VerifyError(); + } + } + + internal string GetLocalType(int index) + { + return locals[index]; + } + + internal int GetLocalRet(int index) + { + string type = locals[index]; + if(!type.StartsWith("Lret;")) + { + throw new VerifyError(); + } + return int.Parse(type.Substring(5)); + } + + internal string GetLocalObject(int index) + { + string s = locals[index]; + if(s == null || (s[0] != 'L' && s[0] != '[' && s[0] != 'U' && s[0] != 'N') || s.StartsWith("Lret;")) + { + throw new VerifyError(); + } + return s; + } + + internal void SetLocalObject(int index, string type) + { + if(type[0] != 'L' && type[0] != '[' && type[0] != 'U' && type[0] != 'N') + { + throw new VerifyError(); + } + SetLocal1(index, type); + } + + internal void Push(string type) + { + if(type.Length == 1) + { + switch(type[0]) + { + case 'Z': + case 'B': + case 'C': + case 'S': + type = "I"; + break; + } + } + PushHelper(type); + } + + internal void PushObject(string type) + { + if(type == null) + { + throw new VerifyError(); + } + if(type[0] == 'L' || type[0] == '[' || type[0] == 'U' || type[0] == 'N') + { + PushHelper(type); + return; + } + throw new VerifyError(); + } + + internal void PushInt() + { + PushHelper("I"); + } + + internal void PushLong() + { + PushHelper("J"); + } + + internal void PushFloat() + { + PushHelper("F"); + } + + internal void PushDouble() + { + PushHelper("D"); + } + + internal void PopInt() + { + Pop('I'); + } + + internal void PopFloat() + { + Pop('F'); + } + + internal void PopDouble() + { + Pop('D'); + } + + internal void PopLong() + { + Pop('J'); + } + + internal string PopArray() + { + string s = PopHelper(); + if(s[0] == '[' || s == "Lnull") + { + return s; + } + throw new VerifyError("Array type expected"); + } + + internal string PopUninitializedObject(string type) + { + string s = PopHelper(); + string u = s; + if(s[0] != 'U' && s[0] != 'N') + { + throw new VerifyError("Expecting to find unitialized object on stack"); + } + s = s.Substring(s.IndexOf(';') + 1); + if(s != type) + { + if(IsSubType(s, type)) + { + // OK + } + else + { + throw new VerifyError(string.Format("popped {0} and expected {1}", s, type)); + } + } + return u; + } + + internal void Pop(string type) + { + if(type.Length == 1) + { + switch(type[0]) + { + case 'Z': + case 'B': + case 'C': + case 'S': + Pop('I'); + return; + } + } + string s = PopHelper(); + if(s != type) + { + if((type[0] == 'L' || type[0] == '[') && s == "Lnull") + { + } + else if(IsSubType(s, type)) + { + } + else + { + throw new VerifyError(string.Format("popped {0} and expected {1}", s, type)); + } + } + } + + internal string PopObject(string type) + { + string s = PopHelper(); + if(s != type) + { + if(s == "Lnull") + { + // null can be used as any type + } + else if(s[0] == 'N' || s[0] == 'U') + { + throw new VerifyError("Unexpected unitialized objref " + s); + } + else if(IsSubType(s, type)) + { + // OK + } + else + { + throw new VerifyError(string.Format("popped {0} and expected {1}", s, type)); + } + } + return s; + } + + internal string PopAny() + { + return PopHelper(); + } + + internal string Pop() + { + string type = PopHelper(); + if(type == "D" || type == "J") + { + throw new VerifyError("Attempt to split long or double on the stack"); + } + return type; + } + + internal string Pop2() + { + string type = PopHelper(); + if(type == "D" || type == "J") + { + return type; + } + type = PopHelper(); + if(type == "D" || type == "J") + { + throw new VerifyError("Attempt to split long or double on the stack"); + } + return type; + } + + internal int GetStackHeight() + { + return stack.Count; + } + + internal string GetStackSlot(int pos) + { + return (string)stack[stack.Count - 1 - pos]; + } + + internal string Peek() + { + if(stack.Count == 0) + { + // return null, if the stack is empty + return null; + } + return (string)stack[stack.Count - 1]; + } + + private string PopHelper() + { + if(stack.Count == 0) + { + throw new VerifyError("Unable to pop operand off an empty stack"); + } + string s = (string)stack[stack.Count - 1]; + stack.RemoveAt(stack.Count - 1); + return s; + } + + private void PushHelper(string s) + { + if(s.IndexOf("L[") >= 0) + { + throw new VerifyError("Internal error: L[ type found"); + } + stack.Add(s); + } + + private string Pop(char type) + { + string s = PopHelper(); + if(s[0] != type) + { + switch(type) + { + case 'I': + throw new VerifyError("Expecting to find int on stack"); + case '[': + throw new VerifyError("Expecting to find array on stack"); + case 'L': + throw new VerifyError("Expecting to find object on stack"); + case 'F': + throw new VerifyError("Expecting to find float on stack"); + case 'D': + throw new VerifyError("Expecting to find double on stack"); + case 'J': + throw new VerifyError("Expecting to find long on stack"); + default: + throw new VerifyError("Expecting to find " + type + " on stack"); + } + } + return s; + } + + internal void MarkInitialized(string type) + { + string initType = type.Substring(type.IndexOf(';') + 1); + for(int i = 0; i < locals.Length; i++) + { + if(locals[i] == type) + { + locals[i] = initType; + } + } + for(int i = 0; i < stack.Count; i++) + { + if((string)stack[i] == type) + { + stack[i] = initType; + } + } + } + + internal void DumpLocals() + { + Console.Write("// "); + string sep = ""; + for(int i = 0; i < locals.Length; i++) + { + Console.Write(sep); + Console.Write(locals[i]); + sep = ", "; + } + Console.WriteLine(); + } + + internal void DumpStack() + { + Console.Write("// "); + string sep = ""; + for(int i = 0; i < stack.Count; i++) + { + Console.Write(sep); + Console.Write(stack[i]); + sep = ", "; + } + Console.WriteLine(); + } + + // this method ensures that no uninitialized object are in the locals for the current state + internal void CheckLocalsForUninitializedObjRefs() + { + for(int i = 0; i < locals.Length; i++) + { + if(locals[i] != null && (((locals[i])[0] == 'U') || ((locals[i])[0] == 'N'))) + { + throw new VerifyError(); + } + } + } + + // this method ensures that no uninitialized object are in the current state + internal void CheckUninitializedObjRefs() + { + for(int i = 0; i < locals.Length; i++) + { + if(locals[i] != null && (((locals[i])[0] == 'U') || ((locals[i])[0] == 'N'))) + { + throw new VerifyError(); + } + } + for(int i = 0; i < stack.Count; i++) + { + if((((string)stack[i])[0] == 'U') || (((string)stack[i])[0] == 'N')) + { + throw new VerifyError(); + } + } + } + + // this method ensures that no uninitialized objects, of the specified type are in the current state + internal void CheckUninitializedObjRefs(string type) + { + for(int i = 0; i < locals.Length; i++) + { + if(locals[i] == type) + { + throw new VerifyError(); + } + } + for(int i = 0; i < stack.Count; i++) + { + if((string)stack[i] == type) + { + throw new VerifyError(); + } + } + } +} + +class SigEnumerator +{ + private string sig; + private int pos; + private int length; + + internal SigEnumerator(string sig) + { + this.sig = sig; + pos = 1; + length = 0; + } + + internal bool MoveNext() + { + pos += length; + switch(sig[pos]) + { + case 'L': + { + length = sig.IndexOf(';', pos) - pos + 1; + break; + } + case '[': + { + length = 0; + while(sig[pos + length] == '[') length++; + if(sig[pos + length] == 'L') + { + length = sig.IndexOf(';', pos) - pos; + } + length++; + break; + } + case ')': + length = 0; + return false; + default: + length = 1; + break; + } + return true; + } + + internal string Current + { + get + { + return sig.Substring(pos, length); + } + } +} + +class ReverseSigEnumerator +{ + private string[] items; + private int pos; + + internal ReverseSigEnumerator(string sig) + { + ArrayList ar = new ArrayList(); + SigEnumerator se = new SigEnumerator(sig); + while(se.MoveNext()) + { + ar.Add(se.Current); + } + items = new String[ar.Count]; + ar.CopyTo(items); + pos = items.Length; + } + + internal bool MoveNext() + { + pos--; + return pos >= 0; + } + + internal string Current + { + get + { + return items[pos]; + } + } +} + +class MethodAnalyzer +{ + internal readonly ClassLoaderWrapper classLoader; + private ClassFile.Method.Code method; + private InstructionState[] state; + private ArrayList[] callsites; + private string[] localTypes; + + internal MethodAnalyzer(ClassFile.Method.Code method, ClassLoaderWrapper classLoader) + { + this.classLoader = classLoader; + this.method = method; + state = new InstructionState[method.Instructions.Length]; + callsites = new ArrayList[method.Instructions.Length]; + localTypes = new String[method.MaxLocals]; + + // start by computing the initial state, the stack is empty and the locals contain the arguments + state[0] = new InstructionState(this, method.MaxLocals); + int arg = 0; + if(!method.Method.IsStatic) + { + // this reference. If we're a constructor, the this reference is uninitialized. + if(method.Method.Name == "<init>") + { + state[0].SetLocalObject(arg++, "U0;L" + method.Method.ClassFile.Name + ";"); + } + else + { + state[0].SetLocalObject(arg++, "L" + method.Method.ClassFile.Name + ";"); + } + } + string sig = method.Method.Signature; + for(int i = 1; sig[i] != ')'; i++) + { + switch(sig[i]) + { + case 'D': + state[0].SetLocalDouble(arg); + arg += 2; + break; + case 'J': + state[0].SetLocalLong(arg); + arg += 2; + break; + case 'L': + { + int pos = sig.IndexOf(';', i); + state[0].SetLocalObject(arg++, sig.Substring(i, pos - i + 1)); + i = pos; + break; + } + case '[': + { + int start = i; + while(sig[i] == '[') i++; + if(sig[i] == 'L') + { + i = sig.IndexOf(';', i); + } + state[0].SetLocalObject(arg++, sig.Substring(start, i - start + 1)); + break; + } + case 'F': + state[0].SetLocalFloat(arg++); + break; + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + state[0].SetLocalInt(arg++); + break; + default: + throw new NotImplementedException(); + } + } + bool done = false; + while(!done) + { + done = true; + for(int i = 0; i < method.Instructions.Length; i++) + { + if(state[i] != null && state[i].changed) + { + try + { + //Console.WriteLine(method.Instructions[i].PC + ": " + method.Instructions[i].OpCode.ToString()); + done = false; + state[i].changed = false; + // mark the exception handlers reachable from this instruction + for(int j = 0; j < method.ExceptionTable.Length; j++) + { + if(method.ExceptionTable[j].start_pc <= method.Instructions[i].PC && method.ExceptionTable[j].end_pc > method.Instructions[i].PC) + { + state[i].CheckLocalsForUninitializedObjRefs(); + InstructionState ex = state[i].CopyLocalsAndSubroutines(); + int catch_type = method.ExceptionTable[j].catch_type; + if(catch_type == 0) + { + ex.PushObject("Ljava/lang/Throwable;"); + } + else + { + ex.PushObject("L" + GetConstantPoolClass(catch_type) + ";"); + } + int idx = method.PcIndexMap[method.ExceptionTable[j].handler_pc]; + state[idx] += ex; + } + } + InstructionState s = state[i].Copy(); + ClassFile.Method.Instruction instr = method.Instructions[i]; + switch(instr.NormalizedOpCode) + { + case NormalizedByteCode.__aload: + s.PushObject(s.GetLocalObject(instr.NormalizedArg1)); + break; + case NormalizedByteCode.__astore: + { + string type = s.Pop(); + switch(type[0]) + { + case 'L': + case '[': + case 'N': + case 'U': + s.SetLocalObject(instr.NormalizedArg1, type); + break; + default: + throw new VerifyError("Object reference expected"); + } + break; + } + case NormalizedByteCode.__aconst_null: + s.PushObject("Lnull"); + break; + case NormalizedByteCode.__aaload: + { + s.PopInt(); + string type = s.PopArray(); + if(type == "Lnull") + { + // if the array is null, we have use null as the element type, because + // otherwise the rest of the code will not verify correctly + s.PushObject(type); + } + else + { + s.PushObject(type.Substring(1)); + } + break; + } + case NormalizedByteCode.__aastore: + { + string elem = s.PopObject("Ljava/lang/Object;"); + s.PopInt(); + string type = s.PopArray(); + // TODO check that elem is assignable to the array + break; + } + case NormalizedByteCode.__baload: + { + s.PopInt(); + string type = s.PopArray(); + if(type[1] != 'B' && type[1] != 'Z' && type != "Lnull") + { + throw new VerifyError(); + } + s.PushInt(); + break; + } + case NormalizedByteCode.__bastore: + { + s.PopInt(); + s.PopInt(); + string type = s.PopArray(); + if(type[1] != 'B' && type[1] != 'Z' && type != "Lnull") + { + throw new VerifyError(); + } + break; + } + case NormalizedByteCode.__caload: + s.PopInt(); + s.PopObject("[C"); + s.PushInt(); + break; + case NormalizedByteCode.__castore: + s.PopInt(); + s.PopInt(); + s.PopObject("[C"); + break; + case NormalizedByteCode.__saload: + s.PopInt(); + s.PopObject("[S"); + s.PushInt(); + break; + case NormalizedByteCode.__sastore: + s.PopInt(); + s.PopInt(); + s.PopObject("[S"); + break; + case NormalizedByteCode.__iaload: + s.PopInt(); + s.PopObject("[I"); + s.PushInt(); + break; + case NormalizedByteCode.__iastore: + s.PopInt(); + s.PopInt(); + s.PopObject("[I"); + break; + case NormalizedByteCode.__laload: + s.PopInt(); + s.PopObject("[J"); + s.PushLong(); + break; + case NormalizedByteCode.__lastore: + s.PopLong(); + s.PopInt(); + s.PopObject("[J"); + break; + case NormalizedByteCode.__daload: + s.PopInt(); + s.PopObject("[D"); + s.PushDouble(); + break; + case NormalizedByteCode.__dastore: + s.PopDouble(); + s.PopInt(); + s.PopObject("[D"); + break; + case NormalizedByteCode.__faload: + s.PopInt(); + s.PopObject("[F"); + s.PushFloat(); + break; + case NormalizedByteCode.__fastore: + s.PopFloat(); + s.PopInt(); + s.PopObject("[F"); + break; + case NormalizedByteCode.__arraylength: + s.PopArray(); + s.PushInt(); + break; + case NormalizedByteCode.__iconst: + s.PushInt(); + break; + case NormalizedByteCode.__if_icmpeq: + case NormalizedByteCode.__if_icmpne: + case NormalizedByteCode.__if_icmplt: + case NormalizedByteCode.__if_icmpge: + case NormalizedByteCode.__if_icmpgt: + case NormalizedByteCode.__if_icmple: + s.PopInt(); + s.PopInt(); + break; + case NormalizedByteCode.__ifeq: + case NormalizedByteCode.__ifge: + case NormalizedByteCode.__ifgt: + case NormalizedByteCode.__ifle: + case NormalizedByteCode.__iflt: + case NormalizedByteCode.__ifne: + s.PopInt(); + break; + case NormalizedByteCode.__ifnonnull: + case NormalizedByteCode.__ifnull: + s.PopObject("Ljava/lang/Object;"); + break; + case NormalizedByteCode.__if_acmpeq: + case NormalizedByteCode.__if_acmpne: + s.PopObject("Ljava/lang/Object;"); + s.PopObject("Ljava/lang/Object;"); + break; + case NormalizedByteCode.__getstatic: + s.Push((GetFieldref(instr.Arg1)).Signature); + break; + case NormalizedByteCode.__putstatic: + { + // HACK because of the way interface merging works, if the + // type on the stack is Object, it can be assigned to anything + // (the compiler will emit a cast) + string type = (GetFieldref(instr.Arg1)).Signature; + if(type[0] == 'L' && s.Peek() == "Ljava/lang/Object;") + { + s.Pop(); + } + else + { + s.Pop(type); + } + break; + } + case NormalizedByteCode.__getfield: + s.PopObject("L" + (GetFieldref(instr.Arg1)).Class + ";"); + s.Push((GetFieldref(instr.Arg1)).Signature); + break; + case NormalizedByteCode.__putfield: + { + // HACK because of the way interface merging works, if the + // type on the stack is Object, it can be assigned to anything + // (the compiler will emit a cast) + string type = (GetFieldref(instr.Arg1)).Signature; + if(type[0] == 'L' && s.Peek() == "Ljava/lang/Object;") + { + s.Pop(); + } + else + { + s.Pop(type); + } + s.PopObject("L" + (GetFieldref(instr.Arg1)).Class + ";"); + break; + } + case NormalizedByteCode.__ldc: + { + object o = GetConstantPoolConstant(instr.Arg1); + if(o is int) + { + s.PushInt(); + } + else if(o is string) + { + s.PushObject("Ljava/lang/String;"); + } + else if(o is long) + { + s.PushLong(); + } + else if(o is float) + { + s.PushFloat(); + } + else if(o is double) + { + s.PushDouble(); + } + else + { + throw new InvalidOperationException(o.GetType().FullName); + } + break; + } + case NormalizedByteCode.__invokevirtual: + case NormalizedByteCode.__invokespecial: + case NormalizedByteCode.__invokeinterface: + case NormalizedByteCode.__invokestatic: + { + ClassFile.ConstantPoolItemFMI cpi = GetMethodref(instr.Arg1); + if((cpi is ClassFile.ConstantPoolItemInterfaceMethodref) != (instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface)) + { + throw new VerifyError("Illegal constant pool index"); + } + if(instr.NormalizedOpCode != NormalizedByteCode.__invokespecial && cpi.Name == "<init>") + { + throw new VerifyError("Must call initializers using invokespecial"); + } + if(cpi.Name == "<clinit>") + { + throw new VerifyError("Illegal call to internal method"); + } + ReverseSigEnumerator rse = new ReverseSigEnumerator(cpi.Signature); + while(rse.MoveNext()) + { + switch(rse.Current[0]) + { + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + s.PopInt(); + break; + case 'J': + s.PopLong(); + break; + case 'D': + s.PopDouble(); + break; + case 'F': + s.PopFloat(); + break; + case 'L': + case '[': + s.PopObject(rse.Current); + break; + default: + throw new NotImplementedException(rse.Current); + } + } + if(instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface) + { + // TODO check arg (should match signature) + // error: "Inconsistent args size" + } + if(instr.NormalizedOpCode != NormalizedByteCode.__invokestatic) + { + if(cpi.Name == "<init>") + { + string type = s.PopUninitializedObject("L" + cpi.Class + ";"); + if((type[0] == 'N' && !type.EndsWith("L" + cpi.Class + ";")) || + (type[0] == 'U' && cpi.Class != method.Method.ClassFile.SuperClass && cpi.Class != method.Method.ClassFile.Name)) + { + // TODO oddly enough, Java fails verification for the class without + // even running the constructor, so maybe constructors are always + // verified... + // NOTE when a constructor isn't verifiable, the static initializer + // doesn't run either (or so I believe) + throw new VerifyError("Call to wrong initialization method"); + } + // after the constructor invocation, the uninitialized reference, is now + // suddenly initialized + s.MarkInitialized(type); + } + else + { + // NOTE previously we checked the type here, but it turns out that + // the JVM throws an IncompatibleClassChangeError at runtime instead + // of a VerifyError if this doesn't match + s.PopObject("Ljava/lang/Object;"); + /* + string type = cpi.Class; + if(type[0] != '[') + { + type = "L" + type + ";"; + } + // invokeinterface is allowed on java/lang/Object (because merging interfaces is + // complicated), this will generate a runtime cast in the compiler + if(instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface && + s.Peek() == "Ljava/lang/Object;") + { + s.PopAny(); + } + else + { + // TODO if this fails it shouldn't generate a VerifyError, but instead + // an IncompatibleClassChangeError (at run time) + s.PopObject(type); + } + */ + } + } + string ret = cpi.Signature.Substring(cpi.Signature.IndexOf(')') + 1); + switch(ret[0]) + { + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + s.PushInt(); + break; + case 'J': + s.PushLong(); + break; + case 'D': + s.PushDouble(); + break; + case 'F': + s.PushFloat(); + break; + case 'L': + case '[': + s.PushObject(ret); + break; + case 'V': + break; + default: + throw new NotImplementedException(ret); + } + break; + } + case NormalizedByteCode.__goto: + break; + case NormalizedByteCode.__istore: + s.PopInt(); + s.SetLocalInt(instr.NormalizedArg1); + break; + case NormalizedByteCode.__iload: + s.GetLocalInt(instr.NormalizedArg1); + s.PushInt(); + break; + case NormalizedByteCode.__ineg: + s.PopInt(); + s.PushInt(); + break; + case NormalizedByteCode.__iadd: + case NormalizedByteCode.__isub: + case NormalizedByteCode.__imul: + case NormalizedByteCode.__idiv: + case NormalizedByteCode.__irem: + case NormalizedByteCode.__iand: + case NormalizedByteCode.__ior: + case NormalizedByteCode.__ixor: + case NormalizedByteCode.__ishl: + case NormalizedByteCode.__ishr: + case NormalizedByteCode.__iushr: + s.PopInt(); + s.PopInt(); + s.PushInt(); + break; + case NormalizedByteCode.__lneg: + s.PopLong(); + s.PushLong(); + break; + case NormalizedByteCode.__ladd: + case NormalizedByteCode.__lsub: + case NormalizedByteCode.__lmul: + case NormalizedByteCode.__ldiv: + case NormalizedByteCode.__lrem: + case NormalizedByteCode.__land: + case NormalizedByteCode.__lor: + case NormalizedByteCode.__lxor: + s.PopLong(); + s.PopLong(); + s.PushLong(); + break; + case NormalizedByteCode.__lshl: + case NormalizedByteCode.__lshr: + case NormalizedByteCode.__lushr: + s.PopInt(); + s.PopLong(); + s.PushLong(); + break; + case NormalizedByteCode.__fneg: + s.PopFloat(); + s.PushFloat(); + break; + case NormalizedByteCode.__fadd: + case NormalizedByteCode.__fsub: + case NormalizedByteCode.__fmul: + case NormalizedByteCode.__fdiv: + case NormalizedByteCode.__frem: + s.PopFloat(); + s.PopFloat(); + s.PushFloat(); + break; + case NormalizedByteCode.__dneg: + s.PopDouble(); + s.PushDouble(); + break; + case NormalizedByteCode.__dadd: + case NormalizedByteCode.__dsub: + case NormalizedByteCode.__dmul: + case NormalizedByteCode.__ddiv: + case NormalizedByteCode.__drem: + s.PopDouble(); + s.PopDouble(); + s.PushDouble(); + break; + case NormalizedByteCode.__new: + // mark the type, so that we can ascertain that it is a "new object" + s.PushObject(string.Format("N{0};L{1};", instr.PC, GetConstantPoolClass(instr.Arg1))); + break; + case NormalizedByteCode.__multianewarray: + { + if(instr.Arg2 < 1) + { + throw new VerifyError("Illegal dimension argument"); + } + for(int j = 0; j < instr.Arg2; j++) + { + s.PopInt(); + } + string type = GetConstantPoolClass(instr.Arg1); + if(!type.StartsWith(new String('[', instr.Arg2))) + { + throw new VerifyError("Illegal dimension argument"); + } + s.PushObject(type); + break; + } + case NormalizedByteCode.__anewarray: + { + s.PopInt(); + string type = GetConstantPoolClass(instr.Arg1); + if(type[0] != '[') + { + type = "L" + type + ";"; + } + s.PushObject("[" + type); + break; + } + case NormalizedByteCode.__newarray: + s.PopInt(); + switch(instr.Arg1) + { + case 4: + s.PushObject("[Z"); + break; + case 5: + s.PushObject("[C"); + break; + case 6: + s.PushObject("[F"); + break; + case 7: + s.PushObject("[D"); + break; + case 8: + s.PushObject("[B"); + break; + case 9: + s.PushObject("[S"); + break; + case 10: + s.PushObject("[I"); + break; + case 11: + s.PushObject("[J"); + break; + default: + throw new VerifyError("Bad type"); + } + break; + case NormalizedByteCode.__swap: + { + string t1 = s.Pop(); + string t2 = s.Pop(); + s.Push(t1); + s.Push(t2); + break; + } + case NormalizedByteCode.__dup: + { + string t = s.Pop(); + s.Push(t); + s.Push(t); + break; + } + case NormalizedByteCode.__dup2: + { + string t = s.PopAny(); + if(t == "D" || t == "J") + { + s.Push(t); + s.Push(t); + } + else + { + string t2 = s.Pop(); + s.Push(t2); + s.Push(t); + s.Push(t2); + s.Push(t); + } + break; + } + case NormalizedByteCode.__dup_x1: + { + string value1 = s.Pop(); + string value2 = s.Pop(); + s.Push(value1); + s.Push(value2); + s.Push(value1); + break; + } + case NormalizedByteCode.__dup2_x1: + { + string value1 = s.PopAny(); + if(value1 == "D" || value1 == "J") + { + string value2 = s.Pop(); + s.Push(value1); + s.Push(value2); + s.Push(value1); + } + else + { + string value2 = s.Pop(); + string value3 = s.Pop(); + s.Push(value2); + s.Push(value1); + s.Push(value3); + s.Push(value2); + s.Push(value1); + } + break; + } + case NormalizedByteCode.__dup_x2: + { + string value1 = s.Pop(); + string value2 = s.Pop(); + string value3 = s.Pop(); + s.Push(value1); + s.Push(value3); + s.Push(value2); + s.Push(value1); + break; + } + case NormalizedByteCode.__dup2_x2: + { + string value1 = s.PopAny(); + if(value1 == "D" || value1 == "J") + { + string value2 = s.PopAny(); + if(value2 == "D" || value2 == "J") + { + // Form 4 + s.Push(value1); + s.Push(value2); + s.Push(value1); + } + else + { + // Form 2 + string value3 = s.Pop(); + s.Push(value1); + s.Push(value3); + s.Push(value2); + s.Push(value1); + } + } + else + { + string value2 = s.Pop(); + string value3 = s.PopAny(); + if(value3 == "D" || value3 == "J") + { + // Form 3 + s.Push(value2); + s.Push(value1); + s.Push(value3); + s.Push(value2); + s.Push(value1); + } + else + { + // Form 4 + string value4 = s.Pop(); + s.Push(value2); + s.Push(value1); + s.Push(value4); + s.Push(value3); + s.Push(value2); + s.Push(value1); + } + } + break; + } + case NormalizedByteCode.__pop: + s.Pop(); + break; + case NormalizedByteCode.__pop2: + s.Pop2(); + break; + case NormalizedByteCode.__monitorenter: + case NormalizedByteCode.__monitorexit: + s.PopObject("Ljava/lang/Object;"); + break; + case NormalizedByteCode.__return: + if(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1) != "V") + { + throw new VerifyError("Wrong return type in function"); + } + break; + case NormalizedByteCode.__areturn: + { + // HACK we always allow object, because if the return type is an interface, this is legal + if(s.Peek() == "Ljava/lang/Object;") + { + s.Pop(); + } + else + { + s.PopObject(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1)); + } + break; + } + case NormalizedByteCode.__ireturn: + s.PopInt(); + switch(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1)) + { + case "Z": + case "B": + case "S": + case "C": + case "I": + break; + default: + throw new VerifyError("Wrong return type in function"); + } + break; + case NormalizedByteCode.__lreturn: + s.PopLong(); + if(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1) != "J") + { + throw new VerifyError("Wrong return type in function"); + } + break; + case NormalizedByteCode.__freturn: + s.PopFloat(); + if(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1) != "F") + { + throw new VerifyError("Wrong return type in function"); + } + break; + case NormalizedByteCode.__dreturn: + s.PopDouble(); + if(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1) != "D") + { + throw new VerifyError("Wrong return type in function"); + } + break; + case NormalizedByteCode.__fload: + s.GetLocalFloat(instr.NormalizedArg1); + s.PushFloat(); + break; + case NormalizedByteCode.__fstore: + s.PopFloat(); + s.SetLocalFloat(instr.NormalizedArg1); + break; + case NormalizedByteCode.__dload: + s.GetLocalDouble(instr.NormalizedArg1); + s.PushDouble(); + break; + case NormalizedByteCode.__dstore: + s.PopDouble(); + s.SetLocalDouble(instr.NormalizedArg1); + break; + case NormalizedByteCode.__lload: + s.GetLocalLong(instr.NormalizedArg1); + s.PushLong(); + break; + case NormalizedByteCode.__lstore: + s.PopLong(); + s.SetLocalLong(instr.NormalizedArg1); + break; + case NormalizedByteCode.__lconst_0: + case NormalizedByteCode.__lconst_1: + s.PushLong(); + break; + case NormalizedByteCode.__fconst_0: + case NormalizedByteCode.__fconst_1: + case NormalizedByteCode.__fconst_2: + s.PushFloat(); + break; + case NormalizedByteCode.__dconst_0: + case NormalizedByteCode.__dconst_1: + s.PushDouble(); + break; + case NormalizedByteCode.__lcmp: + s.PopLong(); + s.PopLong(); + s.PushInt(); + break; + case NormalizedByteCode.__fcmpl: + case NormalizedByteCode.__fcmpg: + s.PopFloat(); + s.PopFloat(); + s.PushInt(); + break; + case NormalizedByteCode.__dcmpl: + case NormalizedByteCode.__dcmpg: + s.PopDouble(); + s.PopDouble(); + s.PushInt(); + break; + case NormalizedByteCode.__checkcast: + { + s.PopObject("Ljava/lang/Object;"); + string type = GetConstantPoolClass(instr.Arg1); + if(type[0] != '[') + { + type = "L" + type + ";"; + } + s.PushObject(type); + break; + } + case NormalizedByteCode.__instanceof: + s.PopObject("Ljava/lang/Object;"); + s.PushInt(); + break; + case NormalizedByteCode.__iinc: + s.GetLocalInt(instr.Arg1); + break; + case NormalizedByteCode.__athrow: + s.PopObject("Ljava/lang/Throwable;"); + break; + case NormalizedByteCode.__lookupswitch: + s.PopInt(); + break; + case NormalizedByteCode.__i2b: + s.PopInt(); + s.PushInt(); + break; + case NormalizedByteCode.__i2c: + s.PopInt(); + s.PushInt(); + break; + case NormalizedByteCode.__i2s: + s.PopInt(); + s.PushInt(); + break; + case NormalizedByteCode.__i2l: + s.PopInt(); + s.PushLong(); + break; + case NormalizedByteCode.__i2f: + s.PopInt(); + s.PushFloat(); + break; + case NormalizedByteCode.__i2d: + s.PopInt(); + s.PushDouble(); + break; + case NormalizedByteCode.__l2i: + s.PopLong(); + s.PushInt(); + break; + case NormalizedByteCode.__l2f: + s.PopLong(); + s.PushFloat(); + break; + case NormalizedByteCode.__l2d: + s.PopLong(); + s.PushDouble(); + break; + case NormalizedByteCode.__f2i: + s.PopFloat(); + s.PushInt(); + break; + case NormalizedByteCode.__f2l: + s.PopFloat(); + s.PushLong(); + break; + case NormalizedByteCode.__f2d: + s.PopFloat(); + s.PushDouble(); + break; + case NormalizedByteCode.__d2i: + s.PopDouble(); + s.PushInt(); + break; + case NormalizedByteCode.__d2f: + s.PopDouble(); + s.PushFloat(); + break; + case NormalizedByteCode.__d2l: + s.PopDouble(); + s.PushLong(); + break; + case NormalizedByteCode.__jsr: + // TODO make sure we're not calling a subroutine we're already in + break; + case NormalizedByteCode.__ret: + { + // TODO if we're returning from a higher level subroutine, invalidate + // all the intermediate return addresses + int subroutineIndex = s.GetLocalRet(instr.Arg1); + s.CheckSubroutineActive(subroutineIndex); + break; + } + case NormalizedByteCode.__nop: + break; + default: + throw new NotImplementedException(instr.NormalizedOpCode.ToString()); + } + if(s.GetStackHeight() > method.MaxStack) + { + throw new VerifyError("Stack size too large"); + } + try + { + // another big switch to handle the opcode targets + switch(instr.NormalizedOpCode) + { + case NormalizedByteCode.__lookupswitch: + for(int j = 0; j < instr.Values.Length; j++) + { + state[method.PcIndexMap[instr.PC + instr.TargetOffsets[j]]] += s; + } + state[method.PcIndexMap[instr.PC + instr.DefaultOffset]] += s; + break; + case NormalizedByteCode.__ifeq: + case NormalizedByteCode.__ifne: + case NormalizedByteCode.__iflt: + case NormalizedByteCode.__ifge: + case NormalizedByteCode.__ifgt: + case NormalizedByteCode.__ifle: + case NormalizedByteCode.__if_icmpeq: + case NormalizedByteCode.__if_icmpne: + case NormalizedByteCode.__if_icmplt: + case NormalizedByteCode.__if_icmpge: + case NormalizedByteCode.__if_icmpgt: + case NormalizedByteCode.__if_icmple: + case NormalizedByteCode.__if_acmpeq: + case NormalizedByteCode.__if_acmpne: + case NormalizedByteCode.__ifnull: + case NormalizedByteCode.__ifnonnull: + if(instr.Arg1 < 0) + { + // backward branches cannot have uninitialized objects on + // the stack or in local variables + s.CheckUninitializedObjRefs(); + } + state[i + 1] += s; + state[method.PcIndexMap[instr.PC + instr.Arg1]] += s; + break; + case NormalizedByteCode.__goto: + if(instr.Arg1 < 0) + { + // backward branches cannot have uninitialized objects on + // the stack or in local variables + s.CheckUninitializedObjRefs(); + } + state[method.PcIndexMap[instr.PC + instr.Arg1]] += s; + break; + case NormalizedByteCode.__jsr: + { + int index = method.PcIndexMap[instr.PC + instr.Arg1]; + s.Push("Lret;" + index); + s.SetSubroutineId(index); + state[index] += s; + AddCallSite(index, i); + break; + } + case NormalizedByteCode.__ret: + { + // HACK if the ret is processed before all of the jsr instructions to this subroutine + // we wouldn't be able to properly merge, so that is why we track the number of callsites + // for each subroutine instruction (see Instruction.AddCallSite()) + int subroutineIndex = s.GetLocalRet(instr.Arg1); + int[] cs = GetCallSites(subroutineIndex); + bool[] locals_modified = s.ClearSubroutineId(subroutineIndex); + for(int j = 0; j < cs.Length; j++) + { + state[cs[j] + 1] = InstructionState.Merge(state[cs[j] + 1], s, locals_modified, state[cs[j]]); + } + break; + } + case NormalizedByteCode.__ireturn: + case NormalizedByteCode.__lreturn: + case NormalizedByteCode.__freturn: + case NormalizedByteCode.__dreturn: + case NormalizedByteCode.__areturn: + case NormalizedByteCode.__return: + case NormalizedByteCode.__athrow: + break; + default: + state[i + 1] += s; + break; + } + } + catch(IndexOutOfRangeException) + { + // we're going to assume that this always means that we have an invalid branch target + // NOTE because PcIndexMap returns -1 for illegal PCs (in the middle of an instruction) and + // we always use that value as an index into the state array, any invalid PC will result + // in an IndexOutOfRangeException + throw new VerifyError("Invalid branch target"); + } + // HACK track the local types (but only for object references) + for(int j = 0; j < localTypes.Length ; j++) + { + string l = s.GetLocalType(j); + if(l != null && (l[0] == 'U' || l[0] == 'N' || l[0] == 'L' || l[0] == '[') && !l.StartsWith("Lret;")) + { + if(l[0] == 'U' || l[0] == 'N') + { + l = l.Substring(l.IndexOf(';') + 1); + } + if(localTypes[j] == null) + { + localTypes[j] = l; + } + else + { + localTypes[j] = s.FindCommonBaseType(localTypes[j], l); + } + } + } + } + catch(VerifyError x) + { + x.Class = method.Method.ClassFile.Name; + x.Method = method.Method.Name; + x.Signature = method.Method.Signature; + x.ByteCodeOffset = method.Instructions[i].PC; + string opcode = method.Instructions[i].OpCode.ToString(); + if(opcode.StartsWith("__")) + { + opcode = opcode.Substring(2); + } + x.Instruction = opcode; + throw; + } + } + } + } +/* + for(int i = 0; i < method.Instructions.Length; i++) + { + state[i].DumpLocals(); + state[i].DumpStack(); + Console.WriteLine("{0}: {1}", method.Instructions[i].PC, method.Instructions[i].OpCode.ToString()); + } +*/ + } + + private ClassFile.ConstantPoolItemFMI GetMethodref(int index) + { + try + { + ClassFile.ConstantPoolItemFMI item = method.Method.ClassFile.GetMethodref(index); + if(item != null && !(item is ClassFile.ConstantPoolItemFieldref)) + { + return item; + } + } + catch(InvalidCastException) + { + } + catch(IndexOutOfRangeException) + { + } + throw new VerifyError("Illegal constant pool index"); + } + + private ClassFile.ConstantPoolItemFieldref GetFieldref(int index) + { + try + { + ClassFile.ConstantPoolItemFieldref item = method.Method.ClassFile.GetFieldref(index); + if(item != null) + { + return item; + } + } + catch(InvalidCastException) + { + } + catch(IndexOutOfRangeException) + { + } + throw new VerifyError("Illegal constant pool index"); + } + + private object GetConstantPoolConstant(int index) + { + try + { + return method.Method.ClassFile.GetConstantPoolConstant(index); + } + catch(IndexOutOfRangeException) + { + } + throw new VerifyError("Illegal constant pool index"); + } + + private string GetConstantPoolClass(int index) + { + try + { + return method.Method.ClassFile.GetConstantPoolClass(index); + } + catch(InvalidCastException) + { + } + catch(IndexOutOfRangeException) + { + } + throw new VerifyError("Illegal constant pool index"); + } + + private void AddCallSite(int subroutineIndex, int callSiteIndex) + { + if(callsites[subroutineIndex] == null) + { + callsites[subroutineIndex] = new ArrayList(); + } + ArrayList l = (ArrayList)callsites[subroutineIndex]; + if(l.IndexOf(callSiteIndex) == -1) + { + l.Add(callSiteIndex); + state[subroutineIndex].AddCallSite(); + } + } + + internal int[] GetCallSites(int subroutineIndex) + { + return (int[])((ArrayList)callsites[subroutineIndex]).ToArray(typeof(int)); + } + + internal int GetStackHeight(int index) + { + return state[index].GetStackHeight(); + } + + internal string GetRawStackType(int index, int pos) + { + return state[index].GetStackSlot(pos); + } + + internal string GetLocalType(int index, int local) + { + return state[index].GetLocalType(local); + } + + internal string GetDeclaredLocalType(int local) + { + return localTypes[local]; + } +} diff --git a/IK.VM.NET/vm.cs b/IK.VM.NET/vm.cs new file mode 100644 index 00000000..dd8da8c9 --- /dev/null +++ b/IK.VM.NET/vm.cs @@ -0,0 +1,195 @@ +/* + 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 + +*/ +using System; +using System.Reflection.Emit; +using System.Reflection; +using System.IO; +using System.Collections; +using System.Xml; +using System.Diagnostics; + +public class JVM +{ + private static bool debug = false; + private static bool noJniStubs = false; + private static bool isStaticCompiler = false; + + public static bool Debug + { + get + { + return debug; + } + set + { + debug = value; + } + } + + public static bool NoJniStubs + { + get + { + return noJniStubs; + } + } + + public static bool IsStaticCompiler + { + get + { + return isStaticCompiler; + } + } + + public static bool CleanStackTraces + { + get + { + return true; + } + } + + private class CompilerClassLoader : ClassLoaderWrapper + { + private Hashtable classes; + private string assembly; + private string path; + private AssemblyBuilder assemblyBuilder; + private ModuleBuilder moduleBuilder; + + internal CompilerClassLoader(string path, string assembly, Hashtable classes) + : base(null) + { + this.classes = classes; + this.assembly = assembly; + this.path = path; + } + + protected override ModuleBuilder CreateModuleBuilder() + { + AssemblyName name = new AssemblyName(); + name.Name = assembly; + assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Save); + CustomAttributeBuilder ikvmAssemblyAttr = new CustomAttributeBuilder(typeof(IKVMAssemblyAttribute).GetConstructor(Type.EmptyTypes), new object[0]); + assemblyBuilder.SetCustomAttribute(ikvmAssemblyAttr); + moduleBuilder = assemblyBuilder.DefineDynamicModule(path, JVM.Debug); + if(JVM.Debug) + { + CustomAttributeBuilder debugAttr = new CustomAttributeBuilder(typeof(DebuggableAttribute).GetConstructor(new Type[] { typeof(bool), typeof(bool) }), new object[] { true, true }); + moduleBuilder.SetCustomAttribute(debugAttr); + } + return moduleBuilder; + } + + internal override TypeWrapper GetBootstrapType(string name) + { + TypeWrapper type = base.GetBootstrapType(name); + if(type == null) + { + ClassFile f = (ClassFile)classes[name]; + if(f != null) + { + type = DefineClass(f); + } + } + return type; + } + + internal void SetMain(MethodInfo m, PEFileKinds target) + { + assemblyBuilder.SetEntryPoint(m, target); + } + + internal void Save() + { + FinishAll(); + assemblyBuilder.Save(path); + } + + internal void AddResources(Hashtable resources) + { + foreach(DictionaryEntry d in resources) + { + byte[] buf = (byte[])d.Value; + if(buf.Length > 0) + { + moduleBuilder.DefineInitializedData((string)d.Key, buf, FieldAttributes.Public | FieldAttributes.Static); + } + } + moduleBuilder.CreateGlobalFunctions(); + } + } + + public static void Compile(string path, string assembly, string mainClass, PEFileKinds target, byte[][] classes, string[] references, bool nojni, Hashtable resources) + { + isStaticCompiler = true; + noJniStubs = nojni; + Hashtable h = new Hashtable(); + Console.WriteLine("Parsing class files"); + for(int i = 0; i < classes.Length; i++) + { + ClassFile f = new ClassFile(classes[i], 0, classes[i].Length, null); + h[f.Name.Replace('/', '.')] = f; + } + Console.WriteLine("Constructing compiler"); + CompilerClassLoader loader = new CompilerClassLoader(path, assembly, h); + ClassLoaderWrapper.SetBootstrapClassLoader(loader); + foreach(string r in references) + { + Assembly.LoadFrom(r); + } + Console.WriteLine("Loading remapped types"); + loader.LoadRemappedTypes(); + Console.WriteLine("Compiling class files (1)"); + foreach(string s in h.Keys) + { + TypeWrapper wrapper = loader.LoadClassByDottedName(s); + if(s == mainClass) + { + MethodWrapper mw = wrapper.GetMethodWrapper(new MethodDescriptor(loader, "main", "([Ljava/lang/String;)V"), false); + if(mw == null) + { + Console.Error.WriteLine("Error: main method not found"); + return; + } + MethodInfo method = mw.GetMethod() as MethodInfo; + if(method == null) + { + Console.Error.WriteLine("Error: redirected main method not supported"); + return; + } + loader.SetMain(method, target); + } + } + Console.WriteLine("Compiling class files (2)"); + loader.AddResources(resources); + loader.Save(); + } + + public static void SaveDebugImage(object mainClass) + { + ClassLoaderWrapper.SaveDebugImage(mainClass); + } +} diff --git a/awt/AssemblyInfo.cs b/awt/AssemblyInfo.cs new file mode 100644 index 00000000..9f89a328 --- /dev/null +++ b/awt/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\<configuration>. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/awt/awt.build b/awt/awt.build new file mode 100644 index 00000000..e33fc2fb --- /dev/null +++ b/awt/awt.build @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<project name="awt" default="awt"> + <target name="awt"> + <csc target="library" output="../bin/awt.dll"> + <sources> + <includes name="*.cs" /> + </sources> + <references> + <includes name="../bin/classpath.dll" asis="true" /> + <includes name="../bin/ik.vm.net.dll" asis="true" /> + </references> + </csc> + </target> +</project> diff --git a/awt/awt.csproj b/awt/awt.csproj new file mode 100644 index 00000000..eee1e04a --- /dev/null +++ b/awt/awt.csproj @@ -0,0 +1,113 @@ +<VisualStudioProject> + <CSHARP + ProjectType = "Local" + ProductVersion = "7.0.9466" + SchemaVersion = "1.0" + ProjectGuid = "{E00A0FA2-1FD7-4DD9-8C06-7202CE366102}" + > + <Build> + <Settings + ApplicationIcon = "" + AssemblyKeyContainerName = "" + AssemblyName = "awt" + AssemblyOriginatorKeyFile = "" + DefaultClientScript = "JScript" + DefaultHTMLPageLayout = "Grid" + DefaultTargetSchema = "IE50" + DelaySign = "false" + OutputType = "Library" + RootNamespace = "awt" + StartupObject = "" + > + <Config + Name = "Debug" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "DEBUG;TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "true" + Optimize = "false" + OutputPath = "bin\Debug\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + <Config + Name = "Release" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "TRACE" + DocumentationFile = "" + DebugSymbols = "false" + FileAlignment = "4096" + IncrementalBuild = "false" + Optimize = "true" + OutputPath = "bin\Release\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + </Settings> + <References> + <Reference + Name = "System" + AssemblyName = "System" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.dll" + /> + <Reference + Name = "System.Data" + AssemblyName = "System.Data" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Data.dll" + /> + <Reference + Name = "System.XML" + AssemblyName = "System.Xml" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll" + /> + <Reference + Name = "System.Windows.Forms" + AssemblyName = "System.Windows.Forms" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Windows.Forms.dll" + /> + <Reference + Name = "System.Drawing" + AssemblyName = "System.Drawing" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Drawing.dll" + /> + <Reference + Name = "IK.VM.NET" + Project = "{F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> + <Reference + Name = "classpath" + AssemblyName = "classpath" + HintPath = "..\classpath\classpath.dll" + /> + </References> + </Build> + <Files> + <Include> + <File + RelPath = "AssemblyInfo.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "toolkit.cs" + SubType = "Code" + BuildAction = "Compile" + /> + </Include> + </Files> + </CSHARP> +</VisualStudioProject> + diff --git a/awt/toolkit.cs b/awt/toolkit.cs new file mode 100644 index 00000000..b0daf846 --- /dev/null +++ b/awt/toolkit.cs @@ -0,0 +1,850 @@ +/* + 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 + +*/ + +using System; +using System.Threading; +using System.Windows.Forms; +using System.Drawing; +using System.ComponentModel; +using java.awt.datatransfer; +using java.awt.image; +using java.awt.peer; +using java.net; +using java.util; + +namespace ikvm.awt +{ + delegate void SetVoid(); + delegate void SetBool(bool b); + delegate void SetXYWH(int x, int y, int w, int h); + delegate void SetString(string s); + delegate string GetString(); + delegate void SetStringInt(string s, int i); + + public class NetToolkit : java.awt.Toolkit + { + private static java.awt.EventQueue eventQueue = new java.awt.EventQueue(); + private static Form bogusForm; + private static Delegate createControlInstance; + + private delegate Control CreateControlInstanceDelegate(Type type); + + private static void MessageLoop() + { + createControlInstance = new CreateControlInstanceDelegate(CreateControlImpl); + Form form = new Form(); + form.CreateControl(); + // HACK I have no idea why this line is necessary... + IntPtr p = form.Handle; + bogusForm = form; + Application.Run(); + } + + private static Control CreateControlImpl(Type type) + { + Control control = (Control)Activator.CreateInstance(type); + control.CreateControl(); + // HACK here we go again... + IntPtr p = control.Handle; + return control; + } + + internal static Control CreateControl(Type type) + { + return (Control)bogusForm.Invoke(createControlInstance, new object[] { type }); + } + + public NetToolkit() + { + lock(typeof(NetToolkit)) + { + if(bogusForm != null) + { + throw new InvalidOperationException(); + } + Thread thread = new Thread(new ThreadStart(MessageLoop)); + thread.Start(); + // TODO don't use polling... + while(bogusForm == null) + { + Thread.Sleep(1); + } + } + } + + protected override java.awt.peer.ButtonPeer createButton(java.awt.Button target) + { + return new NetButtonPeer(target, (Button)CreateControl(typeof(Button))); + } + + protected override java.awt.peer.TextFieldPeer createTextField(java.awt.TextField target) + { + return new NetTextFieldPeer(target, (TextBox)CreateControl(typeof(TextBox))); + } + + protected override java.awt.peer.LabelPeer createLabel(java.awt.Label target) + { + throw new NotImplementedException(); + } + + protected override java.awt.peer.ListPeer createList(java.awt.List target) + { + throw new NotImplementedException(); + } + + protected override java.awt.peer.CheckboxPeer createCheckbox(java.awt.Checkbox target) + { + throw new NotImplementedException(); + } + + protected override java.awt.peer.ScrollbarPeer createScrollbar(java.awt.Scrollbar target) + { + throw new NotImplementedException(); + } + + protected override java.awt.peer.ScrollPanePeer createScrollPane(java.awt.ScrollPane target) + { + throw new NotImplementedException(); + } + + protected override java.awt.peer.TextAreaPeer createTextArea(java.awt.TextArea target) + { + return new NetTextAreaPeer(target, (TextBox)CreateControl(typeof(TextBox))); + } + + protected override java.awt.peer.ChoicePeer createChoice(java.awt.Choice target) + { + throw new NotImplementedException(); + } + + protected override java.awt.peer.FramePeer createFrame(java.awt.Frame target) + { + return new NetFramePeer(target, (Form)CreateControl(typeof(Form))); + } + + protected override java.awt.peer.CanvasPeer createCanvas(java.awt.Canvas target) + { + throw new NotImplementedException(); + } + + protected override java.awt.peer.PanelPeer createPanel(java.awt.Panel target) + { + return new NetPanelPeer(target, (ContainerControl)CreateControl(typeof(ContainerControl))); + } + + protected override java.awt.peer.WindowPeer createWindow(java.awt.Window target) + { + throw new NotImplementedException(); + } + protected override java.awt.peer.DialogPeer createDialog(java.awt.Dialog target) + { + throw new NotImplementedException(); + } + protected override java.awt.peer.MenuBarPeer createMenuBar(java.awt.MenuBar target) + { + throw new NotImplementedException(); + } + protected override java.awt.peer.MenuPeer createMenu(java.awt.Menu target) + { + throw new NotImplementedException(); + } + protected override java.awt.peer.PopupMenuPeer createPopupMenu(java.awt.PopupMenu target) + { + throw new NotImplementedException(); + } + protected override java.awt.peer.MenuItemPeer createMenuItem(java.awt.MenuItem target) + { + throw new NotImplementedException(); + } + protected override java.awt.peer.FileDialogPeer createFileDialog(java.awt.FileDialog target) + { + throw new NotImplementedException(); + } + protected override java.awt.peer.CheckboxMenuItemPeer createCheckboxMenuItem(java.awt.CheckboxMenuItem target) + { + throw new NotImplementedException(); + } + protected override java.awt.peer.FontPeer getFontPeer(string name, int style) + { + throw new NotImplementedException(); + } + public override java.awt.Dimension getScreenSize() + { + throw new NotImplementedException(); + } + public override int getScreenResolution() + { + throw new NotImplementedException(); + } + public override ColorModel getColorModel() + { + throw new NotImplementedException(); + } + public override string[] getFontList() + { + throw new NotImplementedException(); + } + public override java.awt.FontMetrics getFontMetrics(java.awt.Font font) + { + throw new NotImplementedException(); + } + public override void sync() + { + throw new NotImplementedException(); + } + public override java.awt.Image getImage(string filename) + { + throw new NotImplementedException(); + } + public override java.awt.Image getImage(URL url) + { + throw new NotImplementedException(); + } + public override java.awt.Image createImage(string filename) + { + throw new NotImplementedException(); + } + public override java.awt.Image createImage(URL url) + { + throw new NotImplementedException(); + } + public override bool prepareImage(java.awt.Image image, + int width, + int height, + java.awt.image.ImageObserver observer) + { + throw new NotImplementedException(); + } + public override int checkImage(java.awt.Image image, + int width, + int height, + java.awt.image.ImageObserver observer) + { + throw new NotImplementedException(); + } + public override java.awt.Image createImage(java.awt.image.ImageProducer producer) + { + throw new NotImplementedException(); + } + public override java.awt.Image createImage(sbyte[] imagedata, + int imageoffset, + int imagelength) + { + throw new NotImplementedException(); + } + public override java.awt.PrintJob getPrintJob(java.awt.Frame frame, + string jobtitle, + Properties props) + { + throw new NotImplementedException(); + } + public override void beep() + { + throw new NotImplementedException(); + } + public override java.awt.datatransfer.Clipboard getSystemClipboard() + { + throw new NotImplementedException(); + } + + protected override java.awt.EventQueue getSystemEventQueueImpl() + { + return eventQueue; + } + + public override java.awt.dnd.peer.DragSourceContextPeer createDragSourceContextPeer(java.awt.dnd.DragGestureEvent dge) + { + throw new NotImplementedException(); + } + + public override Map mapInputMethodHighlight(java.awt.im.InputMethodHighlight highlight) + { + throw new NotImplementedException(); + } + } + + class NetComponentPeer : ComponentPeer + { + internal readonly java.awt.Component component; + internal readonly Control control; + + public NetComponentPeer(java.awt.Component component, Control control) + { + this.control = control; + this.component = component; + java.awt.Container parent = component.getParent(); + if(parent != null) + { + control.Parent = ((NetComponentPeer)parent.getPeer()).control; + } + setEnabled(component.isEnabled()); + component.invalidate(); + //setBounds(component.getX(), component.getY(), component.getWidth(), component.getHeight()); + control.Invoke(new SetVoid(Setup)); + } + + private void Setup() + { + // TODO we really only should hook these events when they are needed... + control.KeyDown += new KeyEventHandler(OnKeyDown); + control.KeyUp += new KeyEventHandler(OnKeyUp); + control.KeyPress += new KeyPressEventHandler(OnKeyPress); + } + + private static int MapKeyCode(Keys key) + { + switch(key) + { + default: + return (int)key; + } + } + + protected virtual void OnKeyDown(object sender, KeyEventArgs e) + { + // TODO set all this stuff... + long when = 0; + int modifiers = 0; + int keyCode = MapKeyCode(e.KeyCode); + char keyChar = ' '; + int keyLocation = 0; + postEvent(new java.awt.@event.KeyEvent(component, java.awt.@event.KeyEvent.KEY_PRESSED, when, modifiers, keyCode, keyChar, keyLocation)); + } + + protected virtual void OnKeyUp(object sender, KeyEventArgs e) + { + // TODO set all this stuff... + long when = 0; + int modifiers = 0; + int keyCode = MapKeyCode(e.KeyCode); + char keyChar = ' '; + int keyLocation = 0; + postEvent(new java.awt.@event.KeyEvent(component, java.awt.@event.KeyEvent.KEY_RELEASED, when, modifiers, keyCode, keyChar, keyLocation)); + } + + protected virtual void OnKeyPress(object sender, KeyPressEventArgs e) + { + // TODO set all this stuff... + long when = 0; + int modifiers = 0; + int keyCode = 0; + char keyChar = e.KeyChar; + int keyLocation = 0; + postEvent(new java.awt.@event.KeyEvent(component, java.awt.@event.KeyEvent.KEY_TYPED, when, modifiers, keyCode, keyChar, keyLocation)); + } + + protected void postEvent(java.awt.AWTEvent evt) + { + getToolkit().getSystemEventQueue().postEvent(evt); + } + + public int checkImage(java.awt.Image img, int width, int height, java.awt.image.ImageObserver ob) + { + throw new NotImplementedException(); + } + public java.awt.Image createImage(java.awt.image.ImageProducer prod) + { + throw new NotImplementedException(); + } + public java.awt.Image createImage(int width, int height) + { + throw new NotImplementedException(); + } + public void disable() + { + throw new NotImplementedException(); + } + public void dispose() + { + throw new NotImplementedException(); + } + public void enable() + { + throw new NotImplementedException(); + } + public ColorModel getColorModel() + { + throw new NotImplementedException(); + } + public java.awt.FontMetrics getFontMetrics(java.awt.Font f) + { + throw new NotImplementedException(); + } + public java.awt.Graphics getGraphics() + { + throw new NotImplementedException(); + } + public java.awt.Point getLocationOnScreen() + { + throw new NotImplementedException(); + } + public java.awt.Dimension getMinimumSize() + { + throw new NotImplementedException(); + } + + public virtual java.awt.Dimension getPreferredSize() + { + Console.WriteLine("NOTE: NetComponentPeer.getPreferredSize not implemented"); + return new java.awt.Dimension(0, 0); + } + + public java.awt.Toolkit getToolkit() + { + return java.awt.Toolkit.getDefaultToolkit(); + } + + public void handleEvent(java.awt.AWTEvent e) + { + Console.WriteLine("NOTE: NetComponentPeer.handleEvent not implemented"); + } + public void hide() + { + throw new NotImplementedException(); + } + public bool isFocusTraversable() + { + throw new NotImplementedException(); + } + public java.awt.Dimension minimumSize() + { + throw new NotImplementedException(); + } + + public java.awt.Dimension preferredSize() + { + Console.WriteLine("NOTE: NetComponentPeer.preferredSize not implemented"); + return new java.awt.Dimension(0, 0); + } + + public void paint(java.awt.Graphics graphics) + { + throw new NotImplementedException(); + } + public bool prepareImage(java.awt.Image img, int width, int height, ImageObserver ob) + { + throw new NotImplementedException(); + } + public void print(java.awt.Graphics graphics) + { + throw new NotImplementedException(); + } + public void repaint(long tm, int x, int y, int width, int height) + { + throw new NotImplementedException(); + } + public void requestFocus() + { + throw new NotImplementedException(); + } + public void reshape(int x, int y, int width, int height) + { + throw new NotImplementedException(); + } + public void setBackground(java.awt.Color color) + { + throw new NotImplementedException(); + } + + private void setBoundsImpl(int x, int y, int width, int height) + { + control.SetBounds(x, y, width, height); + } + + public void setBounds(int x, int y, int width, int height) + { + control.Invoke(new SetXYWH(setBoundsImpl), new object[] { x, y, width, height }); + } + + public void setCursor(java.awt.Cursor cursor) + { + throw new NotImplementedException(); + } + + private void setEnabledImpl(bool enabled) + { + control.Enabled = enabled; + } + + public void setEnabled(bool enabled) + { + control.Invoke(new SetBool(setEnabledImpl), new object[] { enabled }); + } + + public void setFont(java.awt.Font font) + { + throw new NotImplementedException(); + } + public void setForeground(java.awt.Color color) + { + throw new NotImplementedException(); + } + + private void setVisibleImpl(bool visible) + { + control.Visible = visible; + } + + public void setVisible(bool visible) + { + control.Invoke(new SetBool(setVisibleImpl), new object[] { visible }); + } + + public void show() + { + throw new NotImplementedException(); + } + public java.awt.GraphicsConfiguration getGraphicsConfiguration() + { + throw new NotImplementedException(); + } + + public void setEventMask (long mask) + { + Console.WriteLine("NOTE: NetComponentPeer.setEventMask not implemented"); + } + } + + class NetButtonPeer : NetComponentPeer, ButtonPeer + { + public NetButtonPeer(java.awt.Button awtbutton, Button button) + : base(awtbutton, button) + { + setLabel(awtbutton.getLabel()); + control.Invoke(new SetVoid(Setup)); + } + + private void Setup() + { + ((Button)control).Click += new EventHandler(OnClick); + } + + private void OnClick(object sender, EventArgs e) + { + // TODO set all these properties correctly + string cmd = ""; + long when = 0; + int modifiers = 0; + postEvent(new java.awt.@event.ActionEvent(component, java.awt.@event.ActionEvent.ACTION_PERFORMED, cmd, when, modifiers)); + } + + private void setLabelImpl(string label) + { + control.Text = label; + } + + public void setLabel(string label) + { + control.Invoke(new SetString(setLabelImpl), new object[] { label }); + } + + public override java.awt.Dimension getPreferredSize() + { + // TODO get the size from somewhere... + return new java.awt.Dimension(80, 15); + } + } + + class NetTextComponentPeer : NetComponentPeer, TextComponentPeer + { + public NetTextComponentPeer(java.awt.TextComponent textComponent, TextBox textBox) + : base(textComponent, textBox) + { + } + + protected override void OnKeyPress(object sender, KeyPressEventArgs e) + { + base.OnKeyPress(sender, e); + // TODO for TextAreas this probably isn't the right behaviour + if(e.KeyChar == '\r') + { + // TODO set all these properties correctly + string cmd = ""; + long when = 0; + int modifiers = 0; + postEvent(new java.awt.@event.ActionEvent(component, java.awt.@event.ActionEvent.ACTION_PERFORMED, cmd, when, modifiers)); + } + } + + public int getSelectionEnd() + { + throw new NotImplementedException(); + } + public int getSelectionStart() + { + throw new NotImplementedException(); + } + + private string getTextImpl() + { + return control.Text; + } + + public string getText() + { + return (string)control.Invoke(new GetString(getTextImpl)); + } + + private void setTextImpl(string text) + { + control.Text = text; + } + + public void setText(string text) + { + control.Invoke(new SetString(setTextImpl), new object[] { text }); + } + + public void select(int start_pos, int end_pos) + { + throw new NotImplementedException(); + } + public void setEditable(bool editable) + { + throw new NotImplementedException(); + } + public int getCaretPosition() + { + throw new NotImplementedException(); + } + public void setCaretPosition(int pos) + { + throw new NotImplementedException(); + } + } + + class NetTextFieldPeer : NetTextComponentPeer, TextFieldPeer + { + public NetTextFieldPeer(java.awt.TextField textField, TextBox textBox) + : base(textField, textBox) + { + } + + public java.awt.Dimension minimumSize(int len) + { + throw new NotImplementedException(); + } + public java.awt.Dimension preferredSize(int len) + { + throw new NotImplementedException(); + } + public java.awt.Dimension getMinimumSize(int len) + { + throw new NotImplementedException(); + } + + public java.awt.Dimension getPreferredSize(int len) + { + TextBox b = (TextBox)control; + // TODO use control.Invoke + return new java.awt.Dimension(200, b.PreferredHeight); + } + + public void setEchoChar(char echo_char) + { + throw new NotImplementedException(); + } + public void setEchoCharacter(char echo_char) + { + throw new NotImplementedException(); + } + } + + class NetTextAreaPeer : NetTextComponentPeer, TextAreaPeer + { + public NetTextAreaPeer(java.awt.TextArea textArea, TextBox textBox) + : base(textArea, textBox) + { + control.Invoke(new SetVoid(Setup)); + } + + private void Setup() + { + ((TextBox)control).ReadOnly = !((java.awt.TextArea)component).isEditable(); + ((TextBox)control).WordWrap = false; + ((TextBox)control).ScrollBars = ScrollBars.Both; + ((TextBox)control).Multiline = true; + } + + private void insertImpl(string text, int pos) + { + ((TextBox)control).Text = ((TextBox)control).Text.Insert(pos, text); + } + + public void insert(string text, int pos) + { + control.Invoke(new SetStringInt(insertImpl), new Object[] { text, pos }); + } + + public void insertText(string text, int pos) + { + throw new NotImplementedException(); + } + public java.awt.Dimension minimumSize(int rows, int cols) + { + throw new NotImplementedException(); + } + public java.awt.Dimension getMinimumSize(int rows, int cols) + { + throw new NotImplementedException(); + } + public java.awt.Dimension preferredSize(int rows, int cols) + { + throw new NotImplementedException(); + } + + public java.awt.Dimension getPreferredSize(int rows, int cols) + { + Console.WriteLine("NOTE: NetTextAreaPeer.getPreferredSize not implemented"); + return new java.awt.Dimension(10 * cols, 15 * rows); + } + + public void replaceRange(string text, int start_pos, int end_pos) + { + throw new NotImplementedException(); + } + public void replaceText(string text, int start_pos, int end_pos) + { + throw new NotImplementedException(); + } + } + + class NetContainerPeer : NetComponentPeer, ContainerPeer + { + public NetContainerPeer(java.awt.Container awtcontainer, ContainerControl container) + : base(awtcontainer, container) + { + } + + public java.awt.Insets insets() + { + throw new NotImplementedException(); + } + + public virtual java.awt.Insets getInsets() + { + Console.WriteLine("NOTE: NetContainerPeer.getInsets not implemented"); + return new java.awt.Insets(0, 0, 0, 0); + } + + public void beginValidate() + { + Console.WriteLine("NOTE: NetContainerPeer.beginValidate not implemented"); + } + + public void endValidate() + { + Console.WriteLine("NOTE: NetContainerPeer.endValidate not implemented"); + } + } + + class NetPanelPeer : NetContainerPeer, PanelPeer + { + public NetPanelPeer(java.awt.Panel panel, ContainerControl container) + : base(panel, container) + { + } + } + + class NetWindowPeer : NetContainerPeer, WindowPeer + { + public NetWindowPeer(java.awt.Window window, Form form) + : base(window, form) + { + } + + public void toBack() + { + throw new NotImplementedException(); + } + + public void toFront() + { + Console.WriteLine("NOTE: NetWindowPeer.toFront not implemented"); + } + } + + class NetFramePeer : NetWindowPeer, FramePeer + { + public NetFramePeer(java.awt.Frame frame, Form form) + : base(frame, form) + { + setTitle(frame.getTitle()); + control.Invoke(new SetVoid(Setup)); + } + + private void Setup() + { + Form form = (Form)control; + form.Resize += new EventHandler(Resize); + form.Closing += new CancelEventHandler(Closing); + } + + private void Closing(object sender, CancelEventArgs e) + { + e.Cancel = true; + postEvent(new java.awt.@event.WindowEvent((java.awt.Window)component, java.awt.@event.WindowEvent.WINDOW_CLOSING)); + } + + private void Resize(object sender, EventArgs e) + { + // TODO I have no clue what I should do here... + Rectangle r = control.Bounds; + component.setBounds(r.X, r.Y, r.Width, r.Height); + component.invalidate(); + component.validate(); + postEvent(new java.awt.@event.ComponentEvent(component, java.awt.@event.ComponentEvent.COMPONENT_RESIZED)); + } + + public void setIconImage(java.awt.Image image) + { + throw new NotImplementedException(); + } + public void setMenuBar(java.awt.MenuBar mb) + { + throw new NotImplementedException(); + } + public void setResizable(bool resizable) + { + throw new NotImplementedException(); + } + private void setTitleImpl(string title) + { + control.Text = title; + } + public void setTitle(string title) + { + control.Invoke(new SetString(setTitleImpl), new object[] { title }); + } + + public override java.awt.Insets getInsets() + { + // NOTE that we're not returning the "real" insets, but the result is equivalent (I think) + // and it doesn't require me to remap the client coordinates + Rectangle client = control.ClientRectangle; + // TODO use control.Invoke + return new java.awt.Insets(0, 0, control.Height - client.Height, control.Width - client.Width); + } + } +} diff --git a/bin/lib/security/classpath.security b/bin/lib/security/classpath.security new file mode 100644 index 00000000..cd1223be --- /dev/null +++ b/bin/lib/security/classpath.security @@ -0,0 +1,39 @@ +# classpath.security +# Copyright (C) 2002 Free Software Foundation, Inc. +# +# This file is part of GNU Classpath. +# +# GNU Classpath is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Classpath is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Classpath; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307 USA. +# +# Linking this library statically or dynamically with other modules is +# making a combined work based on this library. Thus, the terms and +# conditions of the GNU General Public License cover the whole +# combination. +# +# As a special exception, the copyright holders of this library give you +# permission to link this library with independent modules to produce an +# executable, regardless of the license terms of these independent +# modules, and to copy and distribute the resulting executable under +# terms of your choice, provided that you also meet, for each linked +# independent module, the terms and conditions of the license of that +# module. An independent module is a module which is not derived from +# or based on this library. If you modify this library, you may extend +# this exception to your version of the library, but you are not +# obligated to do so. If you do not wish to do so, delete this +# exception statement from your version. + + +security.provider.1=gnu.java.security.provider.Gnu diff --git a/classpath/System.Xml.jar b/classpath/System.Xml.jar Binary files differnew file mode 100644 index 00000000..4c7e5df4 --- /dev/null +++ b/classpath/System.Xml.jar diff --git a/classpath/System.jar b/classpath/System.jar Binary files differnew file mode 100644 index 00000000..f4161828 --- /dev/null +++ b/classpath/System.jar diff --git a/classpath/allsources.lst b/classpath/allsources.lst new file mode 100644 index 00000000..539d9b40 --- /dev/null +++ b/classpath/allsources.lst @@ -0,0 +1,1488 @@ +C:\vsp\ikvm\classpath\gnu\classpath\Configuration.java +C:\vsp\ikvm\classpath\gnu\java\net\protocol\ikvmres\Handler.java +C:\vsp\ikvm\classpath\java\io\FileDescriptor.java +C:\vsp\ikvm\classpath\java\io\FileInputStream.java +C:\vsp\ikvm\classpath\java\io\FileOutputStream.java +C:\vsp\ikvm\classpath\java\io\RandomAccessFile.java +C:\vsp\ikvm\classpath\java\lang\Thread.java +C:\vsp\ikvm\classpath\java\lang\Class.java +C:\vsp\ikvm\classpath\java\lang\VMClassLoader.java +C:\vsp\ikvm\classpath\java\lang\reflect\Constructor.java +C:\vsp\ikvm\classpath\java\lang\reflect\Method.java +C:\vsp\ikvm\classpath\java\lang\reflect\Field.java +C:\vsp\ikvm\classpath\java\net\PlainSocketImpl.java +C:\vsp\ikvm\classpath\java\net\PlainDatagramSocketImpl.java +C:\vsp\ikvm\classpath\sun\misc\Ref.java +C:\classpath-0.04\com\sun\javadoc\ClassDoc.java +C:\classpath-0.04\com\sun\javadoc\ConstructorDoc.java +C:\classpath-0.04\com\sun\javadoc\Doc.java +C:\classpath-0.04\com\sun\javadoc\DocErrorReporter.java +C:\classpath-0.04\com\sun\javadoc\Doclet.java +C:\classpath-0.04\com\sun\javadoc\ExecutableMemberDoc.java +C:\classpath-0.04\com\sun\javadoc\FieldDoc.java +C:\classpath-0.04\com\sun\javadoc\MemberDoc.java +C:\classpath-0.04\com\sun\javadoc\MethodDoc.java +C:\classpath-0.04\com\sun\javadoc\PackageDoc.java +C:\classpath-0.04\com\sun\javadoc\Parameter.java +C:\classpath-0.04\com\sun\javadoc\ParamTag.java +C:\classpath-0.04\com\sun\javadoc\ProgramElementDoc.java +C:\classpath-0.04\com\sun\javadoc\RootDoc.java +C:\classpath-0.04\com\sun\javadoc\SeeTag.java +C:\classpath-0.04\com\sun\javadoc\SerialFieldTag.java +C:\classpath-0.04\com\sun\javadoc\Tag.java +C:\classpath-0.04\com\sun\javadoc\ThrowsTag.java +C:\classpath-0.04\com\sun\javadoc\Type.java +C:\classpath-0.04\gnu\java\awt\BitMaskExtent.java +C:\classpath-0.04\gnu\java\awt\Buffers.java +C:\classpath-0.04\gnu\java\awt\ComponentDataBlitOp.java +C:\classpath-0.04\gnu\java\awt\GLightweightPeer.java +C:\classpath-0.04\gnu\java\awt\image\GdkPixbufDecoder.java +C:\classpath-0.04\gnu\java\awt\image\GtkOffScreenDecoder.java +C:\classpath-0.04\gnu\java\awt\image\ImageDecoder.java +C:\classpath-0.04\gnu\java\awt\image\XBMDecoder.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GdkFontMetrics.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GdkGraphics.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkArg.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkArgList.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkButtonPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkCanvasPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkCheckboxMenuItemPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkCheckboxPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkChoicePeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkClipboard.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkComponentPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkContainerPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkDialogPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkFileDialogPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkFontPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkFramePeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkGenericPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkImage.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkImagePainter.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkLabelPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkListPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkMainThread.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkMenuBarPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkMenuComponentPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkMenuItemPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkMenuPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkOffScreenImage.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkPanelPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkPopupMenuPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkScrollbarPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkScrollPanePeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkTextAreaPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkTextComponentPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkTextFieldPeer.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkToolkit.java +C:\classpath-0.04\gnu\java\awt\peer\gtk\GtkWindowPeer.java +C:\classpath-0.04\gnu\java\beans\BeanInfoEmbryo.java +C:\classpath-0.04\gnu\java\beans\EmptyBeanInfo.java +C:\classpath-0.04\gnu\java\beans\ExplicitBeanInfo.java +C:\classpath-0.04\gnu\java\beans\IntrospectionIncubator.java +C:\classpath-0.04\gnu\java\beans\editors\ColorEditor.java +C:\classpath-0.04\gnu\java\beans\editors\FontEditor.java +C:\classpath-0.04\gnu\java\beans\editors\NativeBooleanEditor.java +C:\classpath-0.04\gnu\java\beans\editors\NativeByteEditor.java +C:\classpath-0.04\gnu\java\beans\editors\NativeDoubleEditor.java +C:\classpath-0.04\gnu\java\beans\editors\NativeFloatEditor.java +C:\classpath-0.04\gnu\java\beans\editors\NativeIntEditor.java +C:\classpath-0.04\gnu\java\beans\editors\NativeLongEditor.java +C:\classpath-0.04\gnu\java\beans\editors\NativeShortEditor.java +C:\classpath-0.04\gnu\java\beans\editors\StringEditor.java +C:\classpath-0.04\gnu\java\beans\info\ComponentBeanInfo.java +C:\classpath-0.04\gnu\java\io\ClassLoaderObjectInputStream.java +C:\classpath-0.04\gnu\java\io\EncodingManager.java +C:\classpath-0.04\gnu\java\io\NullOutputStream.java +C:\classpath-0.04\gnu\java\io\ObjectIdentityWrapper.java +C:\classpath-0.04\gnu\java\io\PlatformHelper.java +C:\classpath-0.04\gnu\java\io\decode\Decoder.java +C:\classpath-0.04\gnu\java\io\decode\Decoder8859_1.java +C:\classpath-0.04\gnu\java\io\decode\Decoder8859_2.java +C:\classpath-0.04\gnu\java\io\decode\Decoder8859_3.java +C:\classpath-0.04\gnu\java\io\decode\Decoder8859_4.java +C:\classpath-0.04\gnu\java\io\decode\Decoder8859_5.java +C:\classpath-0.04\gnu\java\io\decode\DecoderEightBitLookup.java +C:\classpath-0.04\gnu\java\io\decode\DecoderUTF8.java +C:\classpath-0.04\gnu\java\io\encode\Encoder.java +C:\classpath-0.04\gnu\java\io\encode\Encoder8859_1.java +C:\classpath-0.04\gnu\java\io\encode\Encoder8859_2.java +C:\classpath-0.04\gnu\java\io\encode\Encoder8859_3.java +C:\classpath-0.04\gnu\java\io\encode\Encoder8859_4.java +C:\classpath-0.04\gnu\java\io\encode\Encoder8859_5.java +C:\classpath-0.04\gnu\java\io\encode\EncoderEightBitLookup.java +C:\classpath-0.04\gnu\java\io\encode\EncoderUTF8.java +C:\classpath-0.04\gnu\java\lang\ArrayHelper.java +C:\classpath-0.04\gnu\java\lang\CharData.java +C:\classpath-0.04\gnu\java\lang\ClassHelper.java +C:\classpath-0.04\gnu\java\lang\ClassLoaderHelper.java +C:\classpath-0.04\gnu\java\lang\ExecutionStack.java +C:\classpath-0.04\gnu\java\lang\MainThread.java +C:\classpath-0.04\gnu\java\lang\StackFrame.java +C:\classpath-0.04\gnu\java\lang\SystemClassLoader.java +C:\classpath-0.04\gnu\java\lang\reflect\TypeSignature.java +C:\classpath-0.04\gnu\java\locale\Calendar.java +C:\classpath-0.04\gnu\java\locale\Calendar_de.java +C:\classpath-0.04\gnu\java\locale\Calendar_en.java +C:\classpath-0.04\gnu\java\locale\Calendar_nl.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_af_ZA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_AE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_BH.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_DZ.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_EG.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_IN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_IQ.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_JO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_KW.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_LB.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_LY.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_MA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_OM.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_QA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_SD.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_SY.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_TN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ar_YE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_be_BY.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_bn_IN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_br_FR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_bs_BA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ca_ES.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_cs_CZ.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_cy_GB.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_da_DK.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_de.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_de_AT.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_de_BE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_de_CH.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_de_DE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_de_LU.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_el_GR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_AU.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_BW.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_CA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_DK.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_GB.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_HK.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_IE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_IN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_NZ.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_PH.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_SG.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_US.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_ZA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_en_ZW.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_AR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_BO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_CL.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_CO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_CR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_DO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_EC.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_ES.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_GT.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_HN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_MX.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_NI.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_PA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_PE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_PR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_PY.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_SV.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_US.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_UY.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_es_VE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_et_EE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_eu_ES.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_fa_IR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_fi_FI.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_fo_FO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_fr_BE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_fr_CA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_fr_CH.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_fr_FR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_fr_LU.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ga_IE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_gd_GB.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_gl_ES.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_gv_GB.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_he_IL.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_hi_IN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_hr_HR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_hu_HU.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_id_ID.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_it_CH.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_it_IT.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_iw_IL.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ja_JP.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ka_GE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_kl_GL.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ko_KR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_kw_GB.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_lt_LT.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_lv_LV.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_mi_NZ.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_mk_MK.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_mr_IN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_mt_MT.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_nl.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_nl_BE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_nl_NL.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_nn_NO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_no_NO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_oc_FR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_pl_PL.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_pt_BR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_pt_PT.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ro_RO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ru_RU.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ru_UA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_se_NO.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_sk_SK.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_sl_SI.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_sq_AL.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_sr_YU.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_sv_FI.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_sv_SE.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ta_IN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_te_IN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_tg_TJ.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_tl_PH.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_tr_TR.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_uk_UA.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_ur_PK.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_uz_UZ.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_vi_VN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_yi_US.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_zh_CN.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_zh_HK.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_zh_SG.java +C:\classpath-0.04\gnu\java\locale\LocaleInformation_zh_TW.java +C:\classpath-0.04\gnu\java\math\MPN.java +C:\classpath-0.04\gnu\java\net\HeaderFieldHelper.java +C:\classpath-0.04\gnu\java\net\content\text\plain.java +C:\classpath-0.04\gnu\java\net\protocol\file\FileURLConnection.java +C:\classpath-0.04\gnu\java\net\protocol\file\Handler.java +C:\classpath-0.04\gnu\java\net\protocol\http\Handler.java +C:\classpath-0.04\gnu\java\net\protocol\http\HttpURLConnection.java +C:\classpath-0.04\gnu\java\net\protocol\jar\Handler.java +C:\classpath-0.04\gnu\java\net\protocol\jar\JarURLConnection.java +C:\classpath-0.04\gnu\java\nio\ByteBufferImpl.java +C:\classpath-0.04\gnu\java\nio\CharBufferImpl.java +C:\classpath-0.04\gnu\java\nio\DatagramChannelImpl.java +C:\classpath-0.04\gnu\java\nio\DoubleBufferImpl.java +C:\classpath-0.04\gnu\java\nio\FileChannelImpl.java +C:\classpath-0.04\gnu\java\nio\FloatBufferImpl.java +C:\classpath-0.04\gnu\java\nio\IntBufferImpl.java +C:\classpath-0.04\gnu\java\nio\LongBufferImpl.java +C:\classpath-0.04\gnu\java\nio\MappedByteFileBuffer.java +C:\classpath-0.04\gnu\java\nio\MappedCharFileBuffer.java +C:\classpath-0.04\gnu\java\nio\MappedDoubleFileBuffer.java +C:\classpath-0.04\gnu\java\nio\MappedFloatFileBuffer.java +C:\classpath-0.04\gnu\java\nio\MappedIntFileBuffer.java +C:\classpath-0.04\gnu\java\nio\MappedLongFileBuffer.java +C:\classpath-0.04\gnu\java\nio\MappedShortFileBuffer.java +C:\classpath-0.04\gnu\java\nio\PipeImpl.java +C:\classpath-0.04\gnu\java\nio\SelectionKeyImpl.java +C:\classpath-0.04\gnu\java\nio\SelectorImpl.java +C:\classpath-0.04\gnu\java\nio\SelectorProviderImpl.java +C:\classpath-0.04\gnu\java\nio\ServerSocketChannelImpl.java +C:\classpath-0.04\gnu\java\nio\ShortBufferImpl.java +C:\classpath-0.04\gnu\java\nio\SocketChannelImpl.java +C:\classpath-0.04\gnu\java\rmi\RMIMarshalledObjectInputStream.java +C:\classpath-0.04\gnu\java\rmi\RMIMarshalledObjectOutputStream.java +C:\classpath-0.04\gnu\java\rmi\dgc\DGCImpl.java +C:\classpath-0.04\gnu\java\rmi\dgc\DGCImpl_Skel.java +C:\classpath-0.04\gnu\java\rmi\dgc\DGCImpl_Stub.java +C:\classpath-0.04\gnu\java\rmi\registry\RegistryImpl.java +C:\classpath-0.04\gnu\java\rmi\registry\RegistryImpl_Skel.java +C:\classpath-0.04\gnu\java\rmi\registry\RegistryImpl_Stub.java +C:\classpath-0.04\gnu\java\rmi\rmic\Compiler.java +C:\classpath-0.04\gnu\java\rmi\rmic\CompilerProcess.java +C:\classpath-0.04\gnu\java\rmi\rmic\Compile_gcj.java +C:\classpath-0.04\gnu\java\rmi\rmic\RMIC.java +C:\classpath-0.04\gnu\java\rmi\rmic\TabbedWriter.java +C:\classpath-0.04\gnu\java\rmi\server\ConnectionRunnerPool.java +C:\classpath-0.04\gnu\java\rmi\server\ProtocolConstants.java +C:\classpath-0.04\gnu\java\rmi\server\RMIDefaultSocketFactory.java +C:\classpath-0.04\gnu\java\rmi\server\RMIHashes.java +C:\classpath-0.04\gnu\java\rmi\server\RMIObjectInputStream.java +C:\classpath-0.04\gnu\java\rmi\server\RMIObjectOutputStream.java +C:\classpath-0.04\gnu\java\rmi\server\UnicastConnection.java +C:\classpath-0.04\gnu\java\rmi\server\UnicastConnectionManager.java +C:\classpath-0.04\gnu\java\rmi\server\UnicastRef.java +C:\classpath-0.04\gnu\java\rmi\server\UnicastRemoteCall.java +C:\classpath-0.04\gnu\java\rmi\server\UnicastRemoteStub.java +C:\classpath-0.04\gnu\java\rmi\server\UnicastServer.java +C:\classpath-0.04\gnu\java\rmi\server\UnicastServerRef.java +C:\classpath-0.04\gnu\java\security\der\DEREncodingException.java +C:\classpath-0.04\gnu\java\security\provider\DefaultPolicy.java +C:\classpath-0.04\gnu\java\security\provider\DERReader.java +C:\classpath-0.04\gnu\java\security\provider\DERWriter.java +C:\classpath-0.04\gnu\java\security\provider\DSAKeyPairGenerator.java +C:\classpath-0.04\gnu\java\security\provider\DSAParameterGenerator.java +C:\classpath-0.04\gnu\java\security\provider\DSAParameters.java +C:\classpath-0.04\gnu\java\security\provider\DSASignature.java +C:\classpath-0.04\gnu\java\security\provider\Gnu.java +C:\classpath-0.04\gnu\java\security\provider\GnuDSAPrivateKey.java +C:\classpath-0.04\gnu\java\security\provider\GnuDSAPublicKey.java +C:\classpath-0.04\gnu\java\security\provider\MD5.java +C:\classpath-0.04\gnu\java\security\provider\SHA.java +C:\classpath-0.04\gnu\java\security\provider\SHA1PRNG.java +C:\classpath-0.04\gnu\java\security\util\Prime.java +C:\classpath-0.04\gnu\java\text\BaseBreakIterator.java +C:\classpath-0.04\gnu\java\text\CharacterBreakIterator.java +C:\classpath-0.04\gnu\java\text\LineBreakIterator.java +C:\classpath-0.04\gnu\java\text\SentenceBreakIterator.java +C:\classpath-0.04\gnu\java\text\WordBreakIterator.java +C:\classpath-0.04\gnu\java\util\DoubleEnumeration.java +C:\classpath-0.04\gnu\java\util\EmptyEnumeration.java +C:\classpath-0.04\gnu\java\util\prefs\FileBasedFactory.java +C:\classpath-0.04\gnu\java\util\prefs\MemoryBasedFactory.java +C:\classpath-0.04\gnu\java\util\prefs\MemoryBasedPreferences.java +C:\classpath-0.04\gnu\java\util\prefs\NodeReader.java +C:\classpath-0.04\gnu\java\util\prefs\NodeWriter.java +C:\classpath-0.04\java\applet\Applet.java +C:\classpath-0.04\java\applet\AppletContext.java +C:\classpath-0.04\java\applet\AppletStub.java +C:\classpath-0.04\java\applet\AudioClip.java +C:\classpath-0.04\java\awt\ActiveEvent.java +C:\classpath-0.04\java\awt\Adjustable.java +C:\classpath-0.04\java\awt\AlphaComposite.java +C:\classpath-0.04\java\awt\AttributeValue.java +C:\classpath-0.04\java\awt\AWTError.java +C:\classpath-0.04\java\awt\AWTEvent.java +C:\classpath-0.04\java\awt\AWTEventMulticaster.java +C:\classpath-0.04\java\awt\AWTException.java +C:\classpath-0.04\java\awt\AWTKeyStroke.java +C:\classpath-0.04\java\awt\AWTPermission.java +C:\classpath-0.04\java\awt\BasicStroke.java +C:\classpath-0.04\java\awt\BorderLayout.java +C:\classpath-0.04\java\awt\BufferCapabilities.java +C:\classpath-0.04\java\awt\Button.java +C:\classpath-0.04\java\awt\Canvas.java +C:\classpath-0.04\java\awt\CardLayout.java +C:\classpath-0.04\java\awt\Checkbox.java +C:\classpath-0.04\java\awt\CheckboxGroup.java +C:\classpath-0.04\java\awt\CheckboxMenuItem.java +C:\classpath-0.04\java\awt\Choice.java +C:\classpath-0.04\java\awt\Color.java +C:\classpath-0.04\java\awt\Component.java +C:\classpath-0.04\java\awt\ComponentOrientation.java +C:\classpath-0.04\java\awt\Composite.java +C:\classpath-0.04\java\awt\CompositeContext.java +C:\classpath-0.04\java\awt\Container.java +C:\classpath-0.04\java\awt\ContainerOrderFocusTraversalPolicy.java +C:\classpath-0.04\java\awt\Cursor.java +C:\classpath-0.04\java\awt\DefaultFocusTraversalPolicy.java +C:\classpath-0.04\java\awt\DefaultKeyboardFocusManager.java +C:\classpath-0.04\java\awt\Dialog.java +C:\classpath-0.04\java\awt\Dimension.java +C:\classpath-0.04\java\awt\DisplayMode.java +C:\classpath-0.04\java\awt\Event.java +C:\classpath-0.04\java\awt\EventDispatchThread.java +C:\classpath-0.04\java\awt\EventQueue.java +C:\classpath-0.04\java\awt\FileDialog.java +C:\classpath-0.04\java\awt\FlowLayout.java +C:\classpath-0.04\java\awt\FocusTraversalPolicy.java +C:\classpath-0.04\java\awt\Font.java +C:\classpath-0.04\java\awt\FontFormatException.java +C:\classpath-0.04\java\awt\FontMetrics.java +C:\classpath-0.04\java\awt\Frame.java +C:\classpath-0.04\java\awt\GradientPaint.java +C:\classpath-0.04\java\awt\Graphics.java +C:\classpath-0.04\java\awt\Graphics2D.java +C:\classpath-0.04\java\awt\GraphicsConfigTemplate.java +C:\classpath-0.04\java\awt\GraphicsConfiguration.java +C:\classpath-0.04\java\awt\GraphicsDevice.java +C:\classpath-0.04\java\awt\GraphicsEnvironment.java +C:\classpath-0.04\java\awt\GridBagConstraints.java +C:\classpath-0.04\java\awt\GridLayout.java +C:\classpath-0.04\java\awt\HeadlessException.java +C:\classpath-0.04\java\awt\IllegalComponentStateException.java +C:\classpath-0.04\java\awt\Image.java +C:\classpath-0.04\java\awt\ImageCapabilities.java +C:\classpath-0.04\java\awt\Insets.java +C:\classpath-0.04\java\awt\ItemSelectable.java +C:\classpath-0.04\java\awt\JobAttributes.java +C:\classpath-0.04\java\awt\KeyboardFocusManager.java +C:\classpath-0.04\java\awt\KeyEventDispatcher.java +C:\classpath-0.04\java\awt\KeyEventPostProcessor.java +C:\classpath-0.04\java\awt\Label.java +C:\classpath-0.04\java\awt\LayoutManager.java +C:\classpath-0.04\java\awt\LayoutManager2.java +C:\classpath-0.04\java\awt\List.java +C:\classpath-0.04\java\awt\MediaTracker.java +C:\classpath-0.04\java\awt\Menu.java +C:\classpath-0.04\java\awt\MenuBar.java +C:\classpath-0.04\java\awt\MenuComponent.java +C:\classpath-0.04\java\awt\MenuContainer.java +C:\classpath-0.04\java\awt\MenuItem.java +C:\classpath-0.04\java\awt\MenuShortcut.java +C:\classpath-0.04\java\awt\PageAttributes.java +C:\classpath-0.04\java\awt\Paint.java +C:\classpath-0.04\java\awt\PaintContext.java +C:\classpath-0.04\java\awt\Panel.java +C:\classpath-0.04\java\awt\Point.java +C:\classpath-0.04\java\awt\Polygon.java +C:\classpath-0.04\java\awt\PopupMenu.java +C:\classpath-0.04\java\awt\PrintGraphics.java +C:\classpath-0.04\java\awt\PrintJob.java +C:\classpath-0.04\java\awt\Rectangle.java +C:\classpath-0.04\java\awt\RenderingHints.java +C:\classpath-0.04\java\awt\Robot.java +C:\classpath-0.04\java\awt\Scrollbar.java +C:\classpath-0.04\java\awt\ScrollPane.java +C:\classpath-0.04\java\awt\ScrollPaneAdjustable.java +C:\classpath-0.04\java\awt\Shape.java +C:\classpath-0.04\java\awt\Stroke.java +C:\classpath-0.04\java\awt\SystemColor.java +C:\classpath-0.04\java\awt\TextArea.java +C:\classpath-0.04\java\awt\TextComponent.java +C:\classpath-0.04\java\awt\TextField.java +C:\classpath-0.04\java\awt\TexturePaint.java +C:\classpath-0.04\java\awt\Toolkit.java +C:\classpath-0.04\java\awt\Transparency.java +C:\classpath-0.04\java\awt\Window.java +C:\classpath-0.04\java\awt\color\CMMException.java +C:\classpath-0.04\java\awt\color\ColorSpace.java +C:\classpath-0.04\java\awt\color\ICC_ColorSpace.java +C:\classpath-0.04\java\awt\color\ICC_Profile.java +C:\classpath-0.04\java\awt\color\ProfileDataException.java +C:\classpath-0.04\java\awt\datatransfer\Clipboard.java +C:\classpath-0.04\java\awt\datatransfer\ClipboardOwner.java +C:\classpath-0.04\java\awt\datatransfer\DataFlavor.java +C:\classpath-0.04\java\awt\datatransfer\FlavorMap.java +C:\classpath-0.04\java\awt\datatransfer\MimeTypeParseException.java +C:\classpath-0.04\java\awt\datatransfer\StringSelection.java +C:\classpath-0.04\java\awt\datatransfer\SystemFlavorMap.java +C:\classpath-0.04\java\awt\datatransfer\Transferable.java +C:\classpath-0.04\java\awt\datatransfer\UnsupportedFlavorException.java +C:\classpath-0.04\java\awt\dnd\DropTarget.java +C:\classpath-0.04\java\awt\event\ActionEvent.java +C:\classpath-0.04\java\awt\event\ActionListener.java +C:\classpath-0.04\java\awt\event\AdjustmentEvent.java +C:\classpath-0.04\java\awt\event\AdjustmentListener.java +C:\classpath-0.04\java\awt\event\AWTEventListener.java +C:\classpath-0.04\java\awt\event\AWTEventListenerProxy.java +C:\classpath-0.04\java\awt\event\ComponentAdapter.java +C:\classpath-0.04\java\awt\event\ComponentEvent.java +C:\classpath-0.04\java\awt\event\ComponentListener.java +C:\classpath-0.04\java\awt\event\ContainerAdapter.java +C:\classpath-0.04\java\awt\event\ContainerEvent.java +C:\classpath-0.04\java\awt\event\ContainerListener.java +C:\classpath-0.04\java\awt\event\FocusAdapter.java +C:\classpath-0.04\java\awt\event\FocusEvent.java +C:\classpath-0.04\java\awt\event\FocusListener.java +C:\classpath-0.04\java\awt\event\HierarchyBoundsAdapter.java +C:\classpath-0.04\java\awt\event\HierarchyBoundsListener.java +C:\classpath-0.04\java\awt\event\HierarchyEvent.java +C:\classpath-0.04\java\awt\event\HierarchyListener.java +C:\classpath-0.04\java\awt\event\InputEvent.java +C:\classpath-0.04\java\awt\event\InputMethodEvent.java +C:\classpath-0.04\java\awt\event\InputMethodListener.java +C:\classpath-0.04\java\awt\event\InvocationEvent.java +C:\classpath-0.04\java\awt\event\ItemEvent.java +C:\classpath-0.04\java\awt\event\ItemListener.java +C:\classpath-0.04\java\awt\event\KeyAdapter.java +C:\classpath-0.04\java\awt\event\KeyEvent.java +C:\classpath-0.04\java\awt\event\KeyListener.java +C:\classpath-0.04\java\awt\event\MouseAdapter.java +C:\classpath-0.04\java\awt\event\MouseEvent.java +C:\classpath-0.04\java\awt\event\MouseListener.java +C:\classpath-0.04\java\awt\event\MouseMotionAdapter.java +C:\classpath-0.04\java\awt\event\MouseMotionListener.java +C:\classpath-0.04\java\awt\event\MouseWheelEvent.java +C:\classpath-0.04\java\awt\event\MouseWheelListener.java +C:\classpath-0.04\java\awt\event\PaintEvent.java +C:\classpath-0.04\java\awt\event\TextEvent.java +C:\classpath-0.04\java\awt\event\TextListener.java +C:\classpath-0.04\java\awt\event\WindowAdapter.java +C:\classpath-0.04\java\awt\event\WindowEvent.java +C:\classpath-0.04\java\awt\event\WindowFocusListener.java +C:\classpath-0.04\java\awt\event\WindowListener.java +C:\classpath-0.04\java\awt\event\WindowStateListener.java +C:\classpath-0.04\java\awt\font\TextHitInfo.java +C:\classpath-0.04\java\awt\geom\AffineTransform.java +C:\classpath-0.04\java\awt\geom\Arc2D.java +C:\classpath-0.04\java\awt\geom\Area.java +C:\classpath-0.04\java\awt\geom\CubicCurve2D.java +C:\classpath-0.04\java\awt\geom\Dimension2D.java +C:\classpath-0.04\java\awt\geom\Ellipse2D.java +C:\classpath-0.04\java\awt\geom\FlatteningPathIterator.java +C:\classpath-0.04\java\awt\geom\GeneralPath.java +C:\classpath-0.04\java\awt\geom\IllegalPathStateException.java +C:\classpath-0.04\java\awt\geom\Line2D.java +C:\classpath-0.04\java\awt\geom\NoninvertibleTransformException.java +C:\classpath-0.04\java\awt\geom\PathIterator.java +C:\classpath-0.04\java\awt\geom\Point2D.java +C:\classpath-0.04\java\awt\geom\QuadCurve2D.java +C:\classpath-0.04\java\awt\geom\Rectangle2D.java +C:\classpath-0.04\java\awt\geom\RectangularShape.java +C:\classpath-0.04\java\awt\geom\RoundRectangle2D.java +C:\classpath-0.04\java\awt\im\InputContext.java +C:\classpath-0.04\java\awt\im\InputMethodHighlight.java +C:\classpath-0.04\java\awt\im\InputMethodRequests.java +C:\classpath-0.04\java\awt\im\InputSubset.java +C:\classpath-0.04\java\awt\im\spi\InputMethod.java +C:\classpath-0.04\java\awt\im\spi\InputMethodContext.java +C:\classpath-0.04\java\awt\im\spi\InputMethodDescriptor.java +C:\classpath-0.04\java\awt\image\AreaAveragingScaleFilter.java +C:\classpath-0.04\java\awt\image\BufferedImage.java +C:\classpath-0.04\java\awt\image\BufferedImageOp.java +C:\classpath-0.04\java\awt\image\BufferStrategy.java +C:\classpath-0.04\java\awt\image\ColorModel.java +C:\classpath-0.04\java\awt\image\ComponentColorModel.java +C:\classpath-0.04\java\awt\image\ComponentSampleModel.java +C:\classpath-0.04\java\awt\image\CropImageFilter.java +C:\classpath-0.04\java\awt\image\DataBuffer.java +C:\classpath-0.04\java\awt\image\DataBufferByte.java +C:\classpath-0.04\java\awt\image\DataBufferInt.java +C:\classpath-0.04\java\awt\image\DataBufferUShort.java +C:\classpath-0.04\java\awt\image\DirectColorModel.java +C:\classpath-0.04\java\awt\image\FilteredImageSource.java +C:\classpath-0.04\java\awt\image\ImageConsumer.java +C:\classpath-0.04\java\awt\image\ImageFilter.java +C:\classpath-0.04\java\awt\image\ImageObserver.java +C:\classpath-0.04\java\awt\image\ImageProducer.java +C:\classpath-0.04\java\awt\image\ImagingOpException.java +C:\classpath-0.04\java\awt\image\IndexColorModel.java +C:\classpath-0.04\java\awt\image\MemoryImageSource.java +C:\classpath-0.04\java\awt\image\PackedColorModel.java +C:\classpath-0.04\java\awt\image\PixelGrabber.java +C:\classpath-0.04\java\awt\image\Raster.java +C:\classpath-0.04\java\awt\image\RasterFormatException.java +C:\classpath-0.04\java\awt\image\RasterOp.java +C:\classpath-0.04\java\awt\image\RenderedImage.java +C:\classpath-0.04\java\awt\image\ReplicateScaleFilter.java +C:\classpath-0.04\java\awt\image\RGBImageFilter.java +C:\classpath-0.04\java\awt\image\SampleModel.java +C:\classpath-0.04\java\awt\image\SinglePixelPackedSampleModel.java +C:\classpath-0.04\java\awt\image\TileObserver.java +C:\classpath-0.04\java\awt\image\VolatileImage.java +C:\classpath-0.04\java\awt\image\WritableRaster.java +C:\classpath-0.04\java\awt\image\WritableRenderedImage.java +C:\classpath-0.04\java\awt\image\renderable\ContextualRenderedImageFactory.java +C:\classpath-0.04\java\awt\image\renderable\ParameterBlock.java +C:\classpath-0.04\java\awt\image\renderable\RenderableImage.java +C:\classpath-0.04\java\awt\image\renderable\RenderableImageOp.java +C:\classpath-0.04\java\awt\image\renderable\RenderableImageProducer.java +C:\classpath-0.04\java\awt\image\renderable\RenderContext.java +C:\classpath-0.04\java\awt\image\renderable\RenderedImageFactory.java +C:\classpath-0.04\java\awt\peer\ButtonPeer.java +C:\classpath-0.04\java\awt\peer\CanvasPeer.java +C:\classpath-0.04\java\awt\peer\CheckboxMenuItemPeer.java +C:\classpath-0.04\java\awt\peer\CheckboxPeer.java +C:\classpath-0.04\java\awt\peer\ChoicePeer.java +C:\classpath-0.04\java\awt\peer\ComponentPeer.java +C:\classpath-0.04\java\awt\peer\ContainerPeer.java +C:\classpath-0.04\java\awt\peer\DialogPeer.java +C:\classpath-0.04\java\awt\peer\FileDialogPeer.java +C:\classpath-0.04\java\awt\peer\FontPeer.java +C:\classpath-0.04\java\awt\peer\FramePeer.java +C:\classpath-0.04\java\awt\peer\LabelPeer.java +C:\classpath-0.04\java\awt\peer\LightweightPeer.java +C:\classpath-0.04\java\awt\peer\ListPeer.java +C:\classpath-0.04\java\awt\peer\MenuBarPeer.java +C:\classpath-0.04\java\awt\peer\MenuComponentPeer.java +C:\classpath-0.04\java\awt\peer\MenuItemPeer.java +C:\classpath-0.04\java\awt\peer\MenuPeer.java +C:\classpath-0.04\java\awt\peer\PanelPeer.java +C:\classpath-0.04\java\awt\peer\PopupMenuPeer.java +C:\classpath-0.04\java\awt\peer\ScrollbarPeer.java +C:\classpath-0.04\java\awt\peer\ScrollPanePeer.java +C:\classpath-0.04\java\awt\peer\TextAreaPeer.java +C:\classpath-0.04\java\awt\peer\TextComponentPeer.java +C:\classpath-0.04\java\awt\peer\TextFieldPeer.java +C:\classpath-0.04\java\awt\peer\WindowPeer.java +C:\classpath-0.04\java\awt\print\Book.java +C:\classpath-0.04\java\awt\print\Pageable.java +C:\classpath-0.04\java\awt\print\PageFormat.java +C:\classpath-0.04\java\awt\print\Paper.java +C:\classpath-0.04\java\awt\print\Printable.java +C:\classpath-0.04\java\awt\print\PrinterAbortException.java +C:\classpath-0.04\java\awt\print\PrinterException.java +C:\classpath-0.04\java\awt\print\PrinterGraphics.java +C:\classpath-0.04\java\awt\print\PrinterIOException.java +C:\classpath-0.04\java\awt\print\PrinterJob.java +C:\classpath-0.04\java\beans\AppletInitializer.java +C:\classpath-0.04\java\beans\BeanDescriptor.java +C:\classpath-0.04\java\beans\BeanInfo.java +C:\classpath-0.04\java\beans\Beans.java +C:\classpath-0.04\java\beans\Customizer.java +C:\classpath-0.04\java\beans\DesignMode.java +C:\classpath-0.04\java\beans\EventSetDescriptor.java +C:\classpath-0.04\java\beans\ExceptionListener.java +C:\classpath-0.04\java\beans\FeatureDescriptor.java +C:\classpath-0.04\java\beans\IndexedPropertyDescriptor.java +C:\classpath-0.04\java\beans\IntrospectionException.java +C:\classpath-0.04\java\beans\Introspector.java +C:\classpath-0.04\java\beans\MethodDescriptor.java +C:\classpath-0.04\java\beans\ParameterDescriptor.java +C:\classpath-0.04\java\beans\PropertyChangeEvent.java +C:\classpath-0.04\java\beans\PropertyChangeListener.java +C:\classpath-0.04\java\beans\PropertyChangeListenerProxy.java +C:\classpath-0.04\java\beans\PropertyChangeSupport.java +C:\classpath-0.04\java\beans\PropertyDescriptor.java +C:\classpath-0.04\java\beans\PropertyEditor.java +C:\classpath-0.04\java\beans\PropertyEditorManager.java +C:\classpath-0.04\java\beans\PropertyEditorSupport.java +C:\classpath-0.04\java\beans\PropertyVetoException.java +C:\classpath-0.04\java\beans\SimpleBeanInfo.java +C:\classpath-0.04\java\beans\VetoableChangeListener.java +C:\classpath-0.04\java\beans\VetoableChangeListenerProxy.java +C:\classpath-0.04\java\beans\VetoableChangeSupport.java +C:\classpath-0.04\java\beans\Visibility.java +C:\classpath-0.04\java\beans\beancontext\BeanContext.java +C:\classpath-0.04\java\beans\beancontext\BeanContextChild.java +C:\classpath-0.04\java\beans\beancontext\BeanContextChildComponentProxy.java +C:\classpath-0.04\java\beans\beancontext\BeanContextChildSupport.java +C:\classpath-0.04\java\beans\beancontext\BeanContextContainerProxy.java +C:\classpath-0.04\java\beans\beancontext\BeanContextEvent.java +C:\classpath-0.04\java\beans\beancontext\BeanContextMembershipEvent.java +C:\classpath-0.04\java\beans\beancontext\BeanContextMembershipListener.java +C:\classpath-0.04\java\beans\beancontext\BeanContextProxy.java +C:\classpath-0.04\java\beans\beancontext\BeanContextServiceAvailableEvent.java +C:\classpath-0.04\java\beans\beancontext\BeanContextServiceProvider.java +C:\classpath-0.04\java\beans\beancontext\BeanContextServiceProviderBeanInfo.java +C:\classpath-0.04\java\beans\beancontext\BeanContextServiceRevokedEvent.java +C:\classpath-0.04\java\beans\beancontext\BeanContextServiceRevokedListener.java +C:\classpath-0.04\java\beans\beancontext\BeanContextServices.java +C:\classpath-0.04\java\beans\beancontext\BeanContextServicesListener.java +C:\classpath-0.04\java\io\BufferedInputStream.java +C:\classpath-0.04\java\io\BufferedOutputStream.java +C:\classpath-0.04\java\io\BufferedReader.java +C:\classpath-0.04\java\io\BufferedWriter.java +C:\classpath-0.04\java\io\ByteArrayInputStream.java +C:\classpath-0.04\java\io\ByteArrayOutputStream.java +C:\classpath-0.04\java\io\CharArrayReader.java +C:\classpath-0.04\java\io\CharArrayWriter.java +C:\classpath-0.04\java\io\CharConversionException.java +C:\classpath-0.04\java\io\DataInput.java +C:\classpath-0.04\java\io\DataInputStream.java +C:\classpath-0.04\java\io\DataOutput.java +C:\classpath-0.04\java\io\DataOutputStream.java +C:\classpath-0.04\java\io\EOFException.java +C:\classpath-0.04\java\io\Externalizable.java +C:\classpath-0.04\java\io\File.java +C:\classpath-0.04\java\io\FileFilter.java +C:\classpath-0.04\java\io\FilenameFilter.java +C:\classpath-0.04\java\io\FileNotFoundException.java +C:\classpath-0.04\java\io\FilePermission.java +C:\classpath-0.04\java\io\FileReader.java +C:\classpath-0.04\java\io\FileWriter.java +C:\classpath-0.04\java\io\FilterInputStream.java +C:\classpath-0.04\java\io\FilterOutputStream.java +C:\classpath-0.04\java\io\FilterReader.java +C:\classpath-0.04\java\io\FilterWriter.java +C:\classpath-0.04\java\io\InputStream.java +C:\classpath-0.04\java\io\InputStreamReader.java +C:\classpath-0.04\java\io\InterruptedIOException.java +C:\classpath-0.04\java\io\InvalidClassException.java +C:\classpath-0.04\java\io\InvalidObjectException.java +C:\classpath-0.04\java\io\IOException.java +C:\classpath-0.04\java\io\LineNumberInputStream.java +C:\classpath-0.04\java\io\LineNumberReader.java +C:\classpath-0.04\java\io\NotActiveException.java +C:\classpath-0.04\java\io\NotSerializableException.java +C:\classpath-0.04\java\io\ObjectInput.java +C:\classpath-0.04\java\io\ObjectInputStream.java +C:\classpath-0.04\java\io\ObjectInputValidation.java +C:\classpath-0.04\java\io\ObjectOutput.java +C:\classpath-0.04\java\io\ObjectOutputStream.java +C:\classpath-0.04\java\io\ObjectStreamClass.java +C:\classpath-0.04\java\io\ObjectStreamConstants.java +C:\classpath-0.04\java\io\ObjectStreamException.java +C:\classpath-0.04\java\io\ObjectStreamField.java +C:\classpath-0.04\java\io\OptionalDataException.java +C:\classpath-0.04\java\io\OutputStream.java +C:\classpath-0.04\java\io\OutputStreamWriter.java +C:\classpath-0.04\java\io\PipedInputStream.java +C:\classpath-0.04\java\io\PipedOutputStream.java +C:\classpath-0.04\java\io\PipedReader.java +C:\classpath-0.04\java\io\PipedWriter.java +C:\classpath-0.04\java\io\PrintStream.java +C:\classpath-0.04\java\io\PrintWriter.java +C:\classpath-0.04\java\io\PushbackInputStream.java +C:\classpath-0.04\java\io\PushbackReader.java +C:\classpath-0.04\java\io\Reader.java +C:\classpath-0.04\java\io\SequenceInputStream.java +C:\classpath-0.04\java\io\Serializable.java +C:\classpath-0.04\java\io\SerializablePermission.java +C:\classpath-0.04\java\io\StreamCorruptedException.java +C:\classpath-0.04\java\io\StreamTokenizer.java +C:\classpath-0.04\java\io\StringBufferInputStream.java +C:\classpath-0.04\java\io\StringReader.java +C:\classpath-0.04\java\io\StringWriter.java +C:\classpath-0.04\java\io\SyncFailedException.java +C:\classpath-0.04\java\io\UnsupportedEncodingException.java +C:\classpath-0.04\java\io\UTFDataFormatException.java +C:\classpath-0.04\java\io\WriteAbortedException.java +C:\classpath-0.04\java\io\Writer.java +C:\classpath-0.04\java\lang\AbstractMethodError.java +C:\classpath-0.04\java\lang\ArithmeticException.java +C:\classpath-0.04\java\lang\ArrayIndexOutOfBoundsException.java +C:\classpath-0.04\java\lang\ArrayStoreException.java +C:\classpath-0.04\java\lang\AssertionError.java +C:\classpath-0.04\java\lang\Boolean.java +C:\classpath-0.04\java\lang\Byte.java +C:\classpath-0.04\java\lang\Character.java +C:\classpath-0.04\java\lang\CharSequence.java +C:\classpath-0.04\java\lang\ClassCastException.java +C:\classpath-0.04\java\lang\ClassCircularityError.java +C:\classpath-0.04\java\lang\ClassFormatError.java +C:\classpath-0.04\java\lang\ClassLoader.java +C:\classpath-0.04\java\lang\ClassNotFoundException.java +C:\classpath-0.04\java\lang\Cloneable.java +C:\classpath-0.04\java\lang\CloneNotSupportedException.java +C:\classpath-0.04\java\lang\Comparable.java +C:\classpath-0.04\java\lang\Compiler.java +C:\classpath-0.04\java\lang\Double.java +C:\classpath-0.04\java\lang\Error.java +C:\classpath-0.04\java\lang\Exception.java +C:\classpath-0.04\java\lang\ExceptionInInitializerError.java +C:\classpath-0.04\java\lang\Float.java +C:\classpath-0.04\java\lang\IllegalAccessError.java +C:\classpath-0.04\java\lang\IllegalAccessException.java +C:\classpath-0.04\java\lang\IllegalArgumentException.java +C:\classpath-0.04\java\lang\IllegalMonitorStateException.java +C:\classpath-0.04\java\lang\IllegalStateException.java +C:\classpath-0.04\java\lang\IllegalThreadStateException.java +C:\classpath-0.04\java\lang\IncompatibleClassChangeError.java +C:\classpath-0.04\java\lang\IndexOutOfBoundsException.java +C:\classpath-0.04\java\lang\InheritableThreadLocal.java +C:\classpath-0.04\java\lang\InstantiationError.java +C:\classpath-0.04\java\lang\InstantiationException.java +C:\classpath-0.04\java\lang\Integer.java +C:\classpath-0.04\java\lang\InternalError.java +C:\classpath-0.04\java\lang\InterruptedException.java +C:\classpath-0.04\java\lang\LinkageError.java +C:\classpath-0.04\java\lang\Long.java +C:\classpath-0.04\java\lang\Math.java +C:\classpath-0.04\java\lang\NegativeArraySizeException.java +C:\classpath-0.04\java\lang\NoClassDefFoundError.java +C:\classpath-0.04\java\lang\NoSuchFieldError.java +C:\classpath-0.04\java\lang\NoSuchFieldException.java +C:\classpath-0.04\java\lang\NoSuchMethodError.java +C:\classpath-0.04\java\lang\NoSuchMethodException.java +C:\classpath-0.04\java\lang\NullPointerException.java +C:\classpath-0.04\java\lang\Number.java +C:\classpath-0.04\java\lang\NumberFormatException.java +C:\classpath-0.04\java\lang\Object.java +C:\classpath-0.04\java\lang\OutOfMemoryError.java +C:\classpath-0.04\java\lang\Package.java +C:\classpath-0.04\java\lang\Process.java +C:\classpath-0.04\java\lang\Runnable.java +C:\classpath-0.04\java\lang\RuntimeException.java +C:\classpath-0.04\java\lang\RuntimePermission.java +C:\classpath-0.04\java\lang\SecurityException.java +C:\classpath-0.04\java\lang\SecurityManager.java +C:\classpath-0.04\java\lang\Short.java +C:\classpath-0.04\java\lang\StackOverflowError.java +C:\classpath-0.04\java\lang\StackTraceElement.java +C:\classpath-0.04\java\lang\StrictMath.java +C:\classpath-0.04\java\lang\String.java +C:\classpath-0.04\java\lang\StringBuffer.java +C:\classpath-0.04\java\lang\StringIndexOutOfBoundsException.java +C:\classpath-0.04\java\lang\System.java +C:\classpath-0.04\java\lang\ThreadDeath.java +C:\classpath-0.04\java\lang\ThreadGroup.java +C:\classpath-0.04\java\lang\ThreadLocal.java +C:\classpath-0.04\java\lang\UnknownError.java +C:\classpath-0.04\java\lang\UnsatisfiedLinkError.java +C:\classpath-0.04\java\lang\UnsupportedClassVersionError.java +C:\classpath-0.04\java\lang\UnsupportedOperationException.java +C:\classpath-0.04\java\lang\VerifyError.java +C:\classpath-0.04\java\lang\VirtualMachineError.java +C:\classpath-0.04\java\lang\Void.java +C:\classpath-0.04\java\lang\ref\PhantomReference.java +C:\classpath-0.04\java\lang\ref\Reference.java +C:\classpath-0.04\java\lang\ref\ReferenceQueue.java +C:\classpath-0.04\java\lang\ref\SoftReference.java +C:\classpath-0.04\java\lang\ref\WeakReference.java +C:\classpath-0.04\java\lang\reflect\AccessibleObject.java +C:\classpath-0.04\java\lang\reflect\Array.java +C:\classpath-0.04\java\lang\reflect\InvocationHandler.java +C:\classpath-0.04\java\lang\reflect\InvocationTargetException.java +C:\classpath-0.04\java\lang\reflect\Member.java +C:\classpath-0.04\java\lang\reflect\Modifier.java +C:\classpath-0.04\java\lang\reflect\Proxy.java +C:\classpath-0.04\java\lang\reflect\ReflectPermission.java +C:\classpath-0.04\java\lang\reflect\UndeclaredThrowableException.java +C:\classpath-0.04\java\math\BigDecimal.java +C:\classpath-0.04\java\math\BigInteger.java +C:\classpath-0.04\java\net\Authenticator.java +C:\classpath-0.04\java\net\BindException.java +C:\classpath-0.04\java\net\ConnectException.java +C:\classpath-0.04\java\net\ContentHandler.java +C:\classpath-0.04\java\net\ContentHandlerFactory.java +C:\classpath-0.04\java\net\DatagramPacket.java +C:\classpath-0.04\java\net\DatagramSocket.java +C:\classpath-0.04\java\net\DatagramSocketImpl.java +C:\classpath-0.04\java\net\FileNameMap.java +C:\classpath-0.04\java\net\HttpURLConnection.java +C:\classpath-0.04\java\net\InetAddress.java +C:\classpath-0.04\java\net\InetSocketAddress.java +C:\classpath-0.04\java\net\JarURLConnection.java +C:\classpath-0.04\java\net\MalformedURLException.java +C:\classpath-0.04\java\net\MimeTypeMapper.java +C:\classpath-0.04\java\net\MulticastSocket.java +C:\classpath-0.04\java\net\NetPermission.java +C:\classpath-0.04\java\net\NoRouteToHostException.java +C:\classpath-0.04\java\net\PasswordAuthentication.java +C:\classpath-0.04\java\net\PortUnreachableException.java +C:\classpath-0.04\java\net\ProtocolException.java +C:\classpath-0.04\java\net\ServerSocket.java +C:\classpath-0.04\java\net\Socket.java +C:\classpath-0.04\java\net\SocketAddress.java +C:\classpath-0.04\java\net\SocketException.java +C:\classpath-0.04\java\net\SocketImpl.java +C:\classpath-0.04\java\net\SocketImplFactory.java +C:\classpath-0.04\java\net\SocketInputStream.java +C:\classpath-0.04\java\net\SocketOptions.java +C:\classpath-0.04\java\net\SocketOutputStream.java +C:\classpath-0.04\java\net\SocketPermission.java +C:\classpath-0.04\java\net\SocketTimeoutException.java +C:\classpath-0.04\java\net\UnknownHostException.java +C:\classpath-0.04\java\net\UnknownServiceException.java +C:\classpath-0.04\java\net\URISyntaxException.java +C:\classpath-0.04\java\net\URL.java +C:\classpath-0.04\java\net\URLClassLoader.java +C:\classpath-0.04\java\net\URLConnection.java +C:\classpath-0.04\java\net\URLDecoder.java +C:\classpath-0.04\java\net\URLEncoder.java +C:\classpath-0.04\java\net\URLStreamHandler.java +C:\classpath-0.04\java\net\URLStreamHandlerFactory.java +C:\classpath-0.04\java\nio\Buffer.java +C:\classpath-0.04\java\nio\ByteBuffer.java +C:\classpath-0.04\java\nio\ByteOrder.java +C:\classpath-0.04\java\nio\CharBuffer.java +C:\classpath-0.04\java\nio\DoubleBuffer.java +C:\classpath-0.04\java\nio\FloatBuffer.java +C:\classpath-0.04\java\nio\IntBuffer.java +C:\classpath-0.04\java\nio\LongBuffer.java +C:\classpath-0.04\java\nio\MappedByteBuffer.java +C:\classpath-0.04\java\nio\ShortBuffer.java +C:\classpath-0.04\java\nio\channels\AlreadyConnectedException.java +C:\classpath-0.04\java\nio\channels\ByteChannel.java +C:\classpath-0.04\java\nio\channels\Channel.java +C:\classpath-0.04\java\nio\channels\Channels.java +C:\classpath-0.04\java\nio\channels\ClosedChannelException.java +C:\classpath-0.04\java\nio\channels\DatagramChannel.java +C:\classpath-0.04\java\nio\channels\FileChannel.java +C:\classpath-0.04\java\nio\channels\FileLock.java +C:\classpath-0.04\java\nio\channels\GatheringByteChannel.java +C:\classpath-0.04\java\nio\channels\InterruptibleChannel.java +C:\classpath-0.04\java\nio\channels\Pipe.java +C:\classpath-0.04\java\nio\channels\ReadableByteChannel.java +C:\classpath-0.04\java\nio\channels\ScatteringByteChannel.java +C:\classpath-0.04\java\nio\channels\SelectableChannel.java +C:\classpath-0.04\java\nio\channels\SelectionKey.java +C:\classpath-0.04\java\nio\channels\Selector.java +C:\classpath-0.04\java\nio\channels\ServerSocketChannel.java +C:\classpath-0.04\java\nio\channels\SocketChannel.java +C:\classpath-0.04\java\nio\channels\WritableByteChannel.java +C:\classpath-0.04\java\nio\channels\spi\AbstractInterruptibleChannel.java +C:\classpath-0.04\java\nio\channels\spi\AbstractSelectableChannel.java +C:\classpath-0.04\java\nio\channels\spi\AbstractSelectionKey.java +C:\classpath-0.04\java\nio\channels\spi\AbstractSelector.java +C:\classpath-0.04\java\nio\channels\spi\SelectorProvider.java +C:\classpath-0.04\java\nio\charset\CharacterCodingException.java +C:\classpath-0.04\java\nio\charset\Charset.java +C:\classpath-0.04\java\nio\charset\CharsetDecoder.java +C:\classpath-0.04\java\nio\charset\CharsetEncoder.java +C:\classpath-0.04\java\nio\charset\CoderResult.java +C:\classpath-0.04\java\nio\charset\CodingErrorAction.java +C:\classpath-0.04\java\rmi\AccessException.java +C:\classpath-0.04\java\rmi\AlreadyBoundException.java +C:\classpath-0.04\java\rmi\ConnectException.java +C:\classpath-0.04\java\rmi\ConnectIOException.java +C:\classpath-0.04\java\rmi\MarshalException.java +C:\classpath-0.04\java\rmi\MarshalledObject.java +C:\classpath-0.04\java\rmi\Naming.java +C:\classpath-0.04\java\rmi\NoSuchObjectException.java +C:\classpath-0.04\java\rmi\NotBoundException.java +C:\classpath-0.04\java\rmi\Remote.java +C:\classpath-0.04\java\rmi\RemoteException.java +C:\classpath-0.04\java\rmi\RMISecurityException.java +C:\classpath-0.04\java\rmi\RMISecurityManager.java +C:\classpath-0.04\java\rmi\ServerError.java +C:\classpath-0.04\java\rmi\ServerException.java +C:\classpath-0.04\java\rmi\ServerRuntimeException.java +C:\classpath-0.04\java\rmi\StubNotFoundException.java +C:\classpath-0.04\java\rmi\UnexpectedException.java +C:\classpath-0.04\java\rmi\UnknownHostException.java +C:\classpath-0.04\java\rmi\UnmarshalException.java +C:\classpath-0.04\java\rmi\activation\Activatable.java +C:\classpath-0.04\java\rmi\activation\ActivateFailedException.java +C:\classpath-0.04\java\rmi\activation\ActivationDesc.java +C:\classpath-0.04\java\rmi\activation\ActivationException.java +C:\classpath-0.04\java\rmi\activation\ActivationGroup.java +C:\classpath-0.04\java\rmi\activation\ActivationGroupDesc.java +C:\classpath-0.04\java\rmi\activation\ActivationGroupID.java +C:\classpath-0.04\java\rmi\activation\ActivationID.java +C:\classpath-0.04\java\rmi\activation\ActivationInstantiator.java +C:\classpath-0.04\java\rmi\activation\ActivationMonitor.java +C:\classpath-0.04\java\rmi\activation\ActivationSystem.java +C:\classpath-0.04\java\rmi\activation\Activator.java +C:\classpath-0.04\java\rmi\activation\UnknownGroupException.java +C:\classpath-0.04\java\rmi\activation\UnknownObjectException.java +C:\classpath-0.04\java\rmi\dgc\DGC.java +C:\classpath-0.04\java\rmi\dgc\Lease.java +C:\classpath-0.04\java\rmi\dgc\VMID.java +C:\classpath-0.04\java\rmi\registry\LocateRegistry.java +C:\classpath-0.04\java\rmi\registry\Registry.java +C:\classpath-0.04\java\rmi\registry\RegistryHandler.java +C:\classpath-0.04\java\rmi\server\ExportException.java +C:\classpath-0.04\java\rmi\server\LoaderHandler.java +C:\classpath-0.04\java\rmi\server\LogStream.java +C:\classpath-0.04\java\rmi\server\ObjID.java +C:\classpath-0.04\java\rmi\server\Operation.java +C:\classpath-0.04\java\rmi\server\RemoteCall.java +C:\classpath-0.04\java\rmi\server\RemoteObject.java +C:\classpath-0.04\java\rmi\server\RemoteRef.java +C:\classpath-0.04\java\rmi\server\RemoteServer.java +C:\classpath-0.04\java\rmi\server\RemoteStub.java +C:\classpath-0.04\java\rmi\server\RMIClassLoader.java +C:\classpath-0.04\java\rmi\server\RMIClientSocketFactory.java +C:\classpath-0.04\java\rmi\server\RMIFailureHandler.java +C:\classpath-0.04\java\rmi\server\RMIServerSocketFactory.java +C:\classpath-0.04\java\rmi\server\RMISocketFactory.java +C:\classpath-0.04\java\rmi\server\ServerCloneException.java +C:\classpath-0.04\java\rmi\server\ServerNotActiveException.java +C:\classpath-0.04\java\rmi\server\ServerRef.java +C:\classpath-0.04\java\rmi\server\Skeleton.java +C:\classpath-0.04\java\rmi\server\SkeletonMismatchException.java +C:\classpath-0.04\java\rmi\server\SkeletonNotFoundException.java +C:\classpath-0.04\java\rmi\server\SocketSecurityException.java +C:\classpath-0.04\java\rmi\server\UID.java +C:\classpath-0.04\java\rmi\server\UnicastRemoteObject.java +C:\classpath-0.04\java\rmi\server\Unreferenced.java +C:\classpath-0.04\java\security\AccessControlContext.java +C:\classpath-0.04\java\security\AccessControlException.java +C:\classpath-0.04\java\security\AccessController.java +C:\classpath-0.04\java\security\AlgorithmParameterGenerator.java +C:\classpath-0.04\java\security\AlgorithmParameterGeneratorSpi.java +C:\classpath-0.04\java\security\AlgorithmParameters.java +C:\classpath-0.04\java\security\AlgorithmParametersSpi.java +C:\classpath-0.04\java\security\AllPermission.java +C:\classpath-0.04\java\security\BasicPermission.java +C:\classpath-0.04\java\security\Certificate.java +C:\classpath-0.04\java\security\CodeSource.java +C:\classpath-0.04\java\security\DigestException.java +C:\classpath-0.04\java\security\DigestInputStream.java +C:\classpath-0.04\java\security\DigestOutputStream.java +C:\classpath-0.04\java\security\DomainCombiner.java +C:\classpath-0.04\java\security\DummyKeyPairGenerator.java +C:\classpath-0.04\java\security\DummyMessageDigest.java +C:\classpath-0.04\java\security\DummySignature.java +C:\classpath-0.04\java\security\GeneralSecurityException.java +C:\classpath-0.04\java\security\Guard.java +C:\classpath-0.04\java\security\GuardedObject.java +C:\classpath-0.04\java\security\Identity.java +C:\classpath-0.04\java\security\IdentityScope.java +C:\classpath-0.04\java\security\InvalidAlgorithmParameterException.java +C:\classpath-0.04\java\security\InvalidKeyException.java +C:\classpath-0.04\java\security\InvalidParameterException.java +C:\classpath-0.04\java\security\Key.java +C:\classpath-0.04\java\security\KeyException.java +C:\classpath-0.04\java\security\KeyFactory.java +C:\classpath-0.04\java\security\KeyFactorySpi.java +C:\classpath-0.04\java\security\KeyManagementException.java +C:\classpath-0.04\java\security\KeyPair.java +C:\classpath-0.04\java\security\KeyPairGenerator.java +C:\classpath-0.04\java\security\KeyPairGeneratorSpi.java +C:\classpath-0.04\java\security\KeyStore.java +C:\classpath-0.04\java\security\KeyStoreException.java +C:\classpath-0.04\java\security\KeyStoreSpi.java +C:\classpath-0.04\java\security\MessageDigest.java +C:\classpath-0.04\java\security\MessageDigestSpi.java +C:\classpath-0.04\java\security\NoSuchAlgorithmException.java +C:\classpath-0.04\java\security\NoSuchProviderException.java +C:\classpath-0.04\java\security\Permission.java +C:\classpath-0.04\java\security\PermissionCollection.java +C:\classpath-0.04\java\security\Permissions.java +C:\classpath-0.04\java\security\Policy.java +C:\classpath-0.04\java\security\Principal.java +C:\classpath-0.04\java\security\PrivateKey.java +C:\classpath-0.04\java\security\PrivilegedAction.java +C:\classpath-0.04\java\security\PrivilegedActionException.java +C:\classpath-0.04\java\security\PrivilegedExceptionAction.java +C:\classpath-0.04\java\security\ProtectionDomain.java +C:\classpath-0.04\java\security\Provider.java +C:\classpath-0.04\java\security\ProviderException.java +C:\classpath-0.04\java\security\PublicKey.java +C:\classpath-0.04\java\security\SecureClassLoader.java +C:\classpath-0.04\java\security\SecureRandom.java +C:\classpath-0.04\java\security\SecureRandomSpi.java +C:\classpath-0.04\java\security\Security.java +C:\classpath-0.04\java\security\SecurityPermission.java +C:\classpath-0.04\java\security\Signature.java +C:\classpath-0.04\java\security\SignatureException.java +C:\classpath-0.04\java\security\SignatureSpi.java +C:\classpath-0.04\java\security\SignedObject.java +C:\classpath-0.04\java\security\Signer.java +C:\classpath-0.04\java\security\UnrecoverableKeyException.java +C:\classpath-0.04\java\security\UnresolvedPermission.java +C:\classpath-0.04\java\security\acl\Acl.java +C:\classpath-0.04\java\security\acl\AclEntry.java +C:\classpath-0.04\java\security\acl\AclNotFoundException.java +C:\classpath-0.04\java\security\acl\Group.java +C:\classpath-0.04\java\security\acl\LastOwnerException.java +C:\classpath-0.04\java\security\acl\NotOwnerException.java +C:\classpath-0.04\java\security\acl\Owner.java +C:\classpath-0.04\java\security\acl\Permission.java +C:\classpath-0.04\java\security\cert\Certificate.java +C:\classpath-0.04\java\security\cert\CertificateEncodingException.java +C:\classpath-0.04\java\security\cert\CertificateException.java +C:\classpath-0.04\java\security\cert\CertificateExpiredException.java +C:\classpath-0.04\java\security\cert\CertificateFactory.java +C:\classpath-0.04\java\security\cert\CertificateFactorySpi.java +C:\classpath-0.04\java\security\cert\CertificateNotYetValidException.java +C:\classpath-0.04\java\security\cert\CertificateParsingException.java +C:\classpath-0.04\java\security\cert\CertPath.java +C:\classpath-0.04\java\security\cert\CertPathBuilderException.java +C:\classpath-0.04\java\security\cert\CertPathValidatorException.java +C:\classpath-0.04\java\security\cert\CertStoreException.java +C:\classpath-0.04\java\security\cert\CRL.java +C:\classpath-0.04\java\security\cert\CRLException.java +C:\classpath-0.04\java\security\cert\X509Certificate.java +C:\classpath-0.04\java\security\cert\X509CRL.java +C:\classpath-0.04\java\security\cert\X509CRLEntry.java +C:\classpath-0.04\java\security\cert\X509Extension.java +C:\classpath-0.04\java\security\interfaces\DSAKey.java +C:\classpath-0.04\java\security\interfaces\DSAKeyPairGenerator.java +C:\classpath-0.04\java\security\interfaces\DSAParams.java +C:\classpath-0.04\java\security\interfaces\DSAPrivateKey.java +C:\classpath-0.04\java\security\interfaces\DSAPublicKey.java +C:\classpath-0.04\java\security\interfaces\RSAKey.java +C:\classpath-0.04\java\security\interfaces\RSAPrivateCrtKey.java +C:\classpath-0.04\java\security\interfaces\RSAPrivateKey.java +C:\classpath-0.04\java\security\interfaces\RSAPublicKey.java +C:\classpath-0.04\java\security\spec\AlgorithmParameterSpec.java +C:\classpath-0.04\java\security\spec\DSAParameterSpec.java +C:\classpath-0.04\java\security\spec\DSAPrivateKeySpec.java +C:\classpath-0.04\java\security\spec\DSAPublicKeySpec.java +C:\classpath-0.04\java\security\spec\EncodedKeySpec.java +C:\classpath-0.04\java\security\spec\InvalidKeySpecException.java +C:\classpath-0.04\java\security\spec\InvalidParameterSpecException.java +C:\classpath-0.04\java\security\spec\KeySpec.java +C:\classpath-0.04\java\security\spec\PKCS8EncodedKeySpec.java +C:\classpath-0.04\java\security\spec\RSAKeyGenParameterSpec.java +C:\classpath-0.04\java\security\spec\RSAPrivateCrtKeySpec.java +C:\classpath-0.04\java\security\spec\RSAPrivateKeySpec.java +C:\classpath-0.04\java\security\spec\RSAPublicKeySpec.java +C:\classpath-0.04\java\security\spec\X509EncodedKeySpec.java +C:\classpath-0.04\java\sql\Array.java +C:\classpath-0.04\java\sql\BatchUpdateException.java +C:\classpath-0.04\java\sql\Blob.java +C:\classpath-0.04\java\sql\CallableStatement.java +C:\classpath-0.04\java\sql\Clob.java +C:\classpath-0.04\java\sql\Connection.java +C:\classpath-0.04\java\sql\DatabaseMetaData.java +C:\classpath-0.04\java\sql\DataTruncation.java +C:\classpath-0.04\java\sql\Date.java +C:\classpath-0.04\java\sql\Driver.java +C:\classpath-0.04\java\sql\DriverManager.java +C:\classpath-0.04\java\sql\DriverPropertyInfo.java +C:\classpath-0.04\java\sql\PreparedStatement.java +C:\classpath-0.04\java\sql\Ref.java +C:\classpath-0.04\java\sql\ResultSet.java +C:\classpath-0.04\java\sql\ResultSetMetaData.java +C:\classpath-0.04\java\sql\SQLData.java +C:\classpath-0.04\java\sql\SQLException.java +C:\classpath-0.04\java\sql\SQLInput.java +C:\classpath-0.04\java\sql\SQLOutput.java +C:\classpath-0.04\java\sql\SQLWarning.java +C:\classpath-0.04\java\sql\Statement.java +C:\classpath-0.04\java\sql\Struct.java +C:\classpath-0.04\java\sql\Time.java +C:\classpath-0.04\java\sql\Timestamp.java +C:\classpath-0.04\java\sql\Types.java +C:\classpath-0.04\java\text\Annotation.java +C:\classpath-0.04\java\text\AttributedCharacterIterator.java +C:\classpath-0.04\java\text\AttributedString.java +C:\classpath-0.04\java\text\AttributedStringIterator.java +C:\classpath-0.04\java\text\BreakIterator.java +C:\classpath-0.04\java\text\CharacterIterator.java +C:\classpath-0.04\java\text\ChoiceFormat.java +C:\classpath-0.04\java\text\CollationElementIterator.java +C:\classpath-0.04\java\text\CollationKey.java +C:\classpath-0.04\java\text\Collator.java +C:\classpath-0.04\java\text\DateFormat.java +C:\classpath-0.04\java\text\DateFormatSymbols.java +C:\classpath-0.04\java\text\DecimalFormat.java +C:\classpath-0.04\java\text\DecimalFormatSymbols.java +C:\classpath-0.04\java\text\FieldPosition.java +C:\classpath-0.04\java\text\Format.java +C:\classpath-0.04\java\text\MessageFormat.java +C:\classpath-0.04\java\text\NumberFormat.java +C:\classpath-0.04\java\text\ParseException.java +C:\classpath-0.04\java\text\ParsePosition.java +C:\classpath-0.04\java\text\RuleBasedCollator.java +C:\classpath-0.04\java\text\SimpleDateFormat.java +C:\classpath-0.04\java\text\StringCharacterIterator.java +C:\classpath-0.04\java\util\AbstractCollection.java +C:\classpath-0.04\java\util\AbstractList.java +C:\classpath-0.04\java\util\AbstractMap.java +C:\classpath-0.04\java\util\AbstractSequentialList.java +C:\classpath-0.04\java\util\AbstractSet.java +C:\classpath-0.04\java\util\ArrayList.java +C:\classpath-0.04\java\util\Arrays.java +C:\classpath-0.04\java\util\BitSet.java +C:\classpath-0.04\java\util\Calendar.java +C:\classpath-0.04\java\util\Collection.java +C:\classpath-0.04\java\util\Collections.java +C:\classpath-0.04\java\util\Comparator.java +C:\classpath-0.04\java\util\ConcurrentModificationException.java +C:\classpath-0.04\java\util\Date.java +C:\classpath-0.04\java\util\Dictionary.java +C:\classpath-0.04\java\util\EmptyStackException.java +C:\classpath-0.04\java\util\Enumeration.java +C:\classpath-0.04\java\util\EventListener.java +C:\classpath-0.04\java\util\EventListenerProxy.java +C:\classpath-0.04\java\util\EventObject.java +C:\classpath-0.04\java\util\GregorianCalendar.java +C:\classpath-0.04\java\util\HashMap.java +C:\classpath-0.04\java\util\HashSet.java +C:\classpath-0.04\java\util\Hashtable.java +C:\classpath-0.04\java\util\IdentityHashMap.java +C:\classpath-0.04\java\util\Iterator.java +C:\classpath-0.04\java\util\LinkedHashMap.java +C:\classpath-0.04\java\util\LinkedHashSet.java +C:\classpath-0.04\java\util\LinkedList.java +C:\classpath-0.04\java\util\List.java +C:\classpath-0.04\java\util\ListIterator.java +C:\classpath-0.04\java\util\ListResourceBundle.java +C:\classpath-0.04\java\util\Locale.java +C:\classpath-0.04\java\util\Map.java +C:\classpath-0.04\java\util\MissingResourceException.java +C:\classpath-0.04\java\util\NoSuchElementException.java +C:\classpath-0.04\java\util\Observable.java +C:\classpath-0.04\java\util\Observer.java +C:\classpath-0.04\java\util\Properties.java +C:\classpath-0.04\java\util\PropertyPermission.java +C:\classpath-0.04\java\util\PropertyPermissionCollection.java +C:\classpath-0.04\java\util\PropertyResourceBundle.java +C:\classpath-0.04\java\util\Random.java +C:\classpath-0.04\java\util\RandomAccess.java +C:\classpath-0.04\java\util\ResourceBundle.java +C:\classpath-0.04\java\util\Set.java +C:\classpath-0.04\java\util\SimpleTimeZone.java +C:\classpath-0.04\java\util\SortedMap.java +C:\classpath-0.04\java\util\SortedSet.java +C:\classpath-0.04\java\util\Stack.java +C:\classpath-0.04\java\util\StringTokenizer.java +C:\classpath-0.04\java\util\Timer.java +C:\classpath-0.04\java\util\TimerTask.java +C:\classpath-0.04\java\util\TimeZone.java +C:\classpath-0.04\java\util\TooManyListenersException.java +C:\classpath-0.04\java\util\TreeMap.java +C:\classpath-0.04\java\util\TreeSet.java +C:\classpath-0.04\java\util\Vector.java +C:\classpath-0.04\java\util\WeakHashMap.java +C:\classpath-0.04\java\util\jar\Attributes.java +C:\classpath-0.04\java\util\jar\JarEntry.java +C:\classpath-0.04\java\util\jar\JarException.java +C:\classpath-0.04\java\util\jar\JarFile.java +C:\classpath-0.04\java\util\jar\JarInputStream.java +C:\classpath-0.04\java\util\jar\JarOutputStream.java +C:\classpath-0.04\java\util\jar\Manifest.java +C:\classpath-0.04\java\util\logging\ConsoleHandler.java +C:\classpath-0.04\java\util\logging\ErrorManager.java +C:\classpath-0.04\java\util\logging\FileHandler.java +C:\classpath-0.04\java\util\logging\Filter.java +C:\classpath-0.04\java\util\logging\Formatter.java +C:\classpath-0.04\java\util\logging\Handler.java +C:\classpath-0.04\java\util\logging\Level.java +C:\classpath-0.04\java\util\logging\Logger.java +C:\classpath-0.04\java\util\logging\LoggingPermission.java +C:\classpath-0.04\java\util\logging\LogManager.java +C:\classpath-0.04\java\util\logging\LogRecord.java +C:\classpath-0.04\java\util\logging\MemoryHandler.java +C:\classpath-0.04\java\util\logging\SimpleFormatter.java +C:\classpath-0.04\java\util\logging\SocketHandler.java +C:\classpath-0.04\java\util\logging\StreamHandler.java +C:\classpath-0.04\java\util\logging\XMLFormatter.java +C:\classpath-0.04\java\util\prefs\AbstractPreferences.java +C:\classpath-0.04\java\util\prefs\BackingStoreException.java +C:\classpath-0.04\java\util\prefs\InvalidPreferencesFormatException.java +C:\classpath-0.04\java\util\prefs\NodeChangeEvent.java +C:\classpath-0.04\java\util\prefs\NodeChangeListener.java +C:\classpath-0.04\java\util\prefs\PreferenceChangeEvent.java +C:\classpath-0.04\java\util\prefs\PreferenceChangeListener.java +C:\classpath-0.04\java\util\prefs\Preferences.java +C:\classpath-0.04\java\util\prefs\PreferencesFactory.java +C:\classpath-0.04\java\util\regex\Matcher.java +C:\classpath-0.04\java\util\regex\Pattern.java +C:\classpath-0.04\java\util\zip\Adler32.java +C:\classpath-0.04\java\util\zip\CheckedInputStream.java +C:\classpath-0.04\java\util\zip\CheckedOutputStream.java +C:\classpath-0.04\java\util\zip\Checksum.java +C:\classpath-0.04\java\util\zip\CRC32.java +C:\classpath-0.04\java\util\zip\DataFormatException.java +C:\classpath-0.04\java\util\zip\Deflater.java +C:\classpath-0.04\java\util\zip\DeflaterConstants.java +C:\classpath-0.04\java\util\zip\DeflaterEngine.java +C:\classpath-0.04\java\util\zip\DeflaterHuffman.java +C:\classpath-0.04\java\util\zip\DeflaterOutputStream.java +C:\classpath-0.04\java\util\zip\DeflaterPending.java +C:\classpath-0.04\java\util\zip\GZIPInputStream.java +C:\classpath-0.04\java\util\zip\GZIPOutputStream.java +C:\classpath-0.04\java\util\zip\Inflater.java +C:\classpath-0.04\java\util\zip\InflaterDynHeader.java +C:\classpath-0.04\java\util\zip\InflaterHuffmanTree.java +C:\classpath-0.04\java\util\zip\InflaterInputStream.java +C:\classpath-0.04\java\util\zip\OutputWindow.java +C:\classpath-0.04\java\util\zip\PendingBuffer.java +C:\classpath-0.04\java\util\zip\StreamManipulator.java +C:\classpath-0.04\java\util\zip\ZipConstants.java +C:\classpath-0.04\java\util\zip\ZipEntry.java +C:\classpath-0.04\java\util\zip\ZipException.java +C:\classpath-0.04\java\util\zip\ZipFile.java +C:\classpath-0.04\java\util\zip\ZipInputStream.java +C:\classpath-0.04\java\util\zip\ZipOutputStream.java +C:\classpath-0.04\javax\accessibility\Accessible.java +C:\classpath-0.04\javax\accessibility\AccessibleAction.java +C:\classpath-0.04\javax\accessibility\AccessibleBundle.java +C:\classpath-0.04\javax\accessibility\AccessibleComponent.java +C:\classpath-0.04\javax\accessibility\AccessibleContext.java +C:\classpath-0.04\javax\accessibility\AccessibleEditableText.java +C:\classpath-0.04\javax\accessibility\AccessibleExtendedComponent.java +C:\classpath-0.04\javax\accessibility\AccessibleExtendedTable.java +C:\classpath-0.04\javax\accessibility\AccessibleHyperlink.java +C:\classpath-0.04\javax\accessibility\AccessibleHypertext.java +C:\classpath-0.04\javax\accessibility\AccessibleIcon.java +C:\classpath-0.04\javax\accessibility\AccessibleKeyBinding.java +C:\classpath-0.04\javax\accessibility\AccessibleRelation.java +C:\classpath-0.04\javax\accessibility\AccessibleRelationSet.java +C:\classpath-0.04\javax\accessibility\AccessibleResourceBundle.java +C:\classpath-0.04\javax\accessibility\AccessibleRole.java +C:\classpath-0.04\javax\accessibility\AccessibleSelection.java +C:\classpath-0.04\javax\accessibility\AccessibleState.java +C:\classpath-0.04\javax\accessibility\AccessibleStateSet.java +C:\classpath-0.04\javax\accessibility\AccessibleTable.java +C:\classpath-0.04\javax\accessibility\AccessibleTableModelChange.java +C:\classpath-0.04\javax\accessibility\AccessibleText.java +C:\classpath-0.04\javax\accessibility\AccessibleValue.java +C:\classpath-0.04\javax\naming\BinaryRefAddr.java +C:\classpath-0.04\javax\naming\InvalidNameException.java +C:\classpath-0.04\javax\naming\Name.java +C:\classpath-0.04\javax\naming\NamingException.java +C:\classpath-0.04\javax\naming\RefAddr.java +C:\classpath-0.04\javax\naming\StringRefAddr.java +C:\classpath-0.04\javax\swing\AbstractAction.java +C:\classpath-0.04\javax\swing\AbstractButton.java +C:\classpath-0.04\javax\swing\AbstractListModel.java +C:\classpath-0.04\javax\swing\AbstractSet.java +C:\classpath-0.04\javax\swing\Action.java +C:\classpath-0.04\javax\swing\BorderFactory.java +C:\classpath-0.04\javax\swing\Box.java +C:\classpath-0.04\javax\swing\BoxLayout.java +C:\classpath-0.04\javax\swing\ButtonGroup.java +C:\classpath-0.04\javax\swing\ButtonModel.java +C:\classpath-0.04\javax\swing\CellEditor.java +C:\classpath-0.04\javax\swing\ComponentInputMap.java +C:\classpath-0.04\javax\swing\DefaultButtonModel.java +C:\classpath-0.04\javax\swing\DefaultCellRenderer.java +C:\classpath-0.04\javax\swing\DefaultListModel.java +C:\classpath-0.04\javax\swing\DefaultListSelectionModel.java +C:\classpath-0.04\javax\swing\GrayFilter.java +C:\classpath-0.04\javax\swing\Icon.java +C:\classpath-0.04\javax\swing\ImageIcon.java +C:\classpath-0.04\javax\swing\InputMap.java +C:\classpath-0.04\javax\swing\JApplet.java +C:\classpath-0.04\javax\swing\JButton.java +C:\classpath-0.04\javax\swing\JCheckBox.java +C:\classpath-0.04\javax\swing\JComponent.java +C:\classpath-0.04\javax\swing\JDialog.java +C:\classpath-0.04\javax\swing\JEditorPane.java +C:\classpath-0.04\javax\swing\JFrame.java +C:\classpath-0.04\javax\swing\JInternalFrame.java +C:\classpath-0.04\javax\swing\JLabel.java +C:\classpath-0.04\javax\swing\JLayeredPane.java +C:\classpath-0.04\javax\swing\JList.java +C:\classpath-0.04\javax\swing\JMenuBar.java +C:\classpath-0.04\javax\swing\JOptionPane.java +C:\classpath-0.04\javax\swing\JPanel.java +C:\classpath-0.04\javax\swing\JRadioButton.java +C:\classpath-0.04\javax\swing\JRootPane.java +C:\classpath-0.04\javax\swing\JScrollBar.java +C:\classpath-0.04\javax\swing\JScrollPane.java +C:\classpath-0.04\javax\swing\JTabbedPane.java +C:\classpath-0.04\javax\swing\JTable.java +C:\classpath-0.04\javax\swing\JTextField.java +C:\classpath-0.04\javax\swing\JToggleButton.java +C:\classpath-0.04\javax\swing\JToolTip.java +C:\classpath-0.04\javax\swing\JTree.java +C:\classpath-0.04\javax\swing\JViewport.java +C:\classpath-0.04\javax\swing\JWindow.java +C:\classpath-0.04\javax\swing\KeyStroke.java +C:\classpath-0.04\javax\swing\ListCellRenderer.java +C:\classpath-0.04\javax\swing\ListModel.java +C:\classpath-0.04\javax\swing\ListSelectionModel.java +C:\classpath-0.04\javax\swing\LookAndFeel.java +C:\classpath-0.04\javax\swing\MenuElement.java +C:\classpath-0.04\javax\swing\MenuSelectionManager.java +C:\classpath-0.04\javax\swing\Scrollable.java +C:\classpath-0.04\javax\swing\SwingConstants.java +C:\classpath-0.04\javax\swing\SwingUtilities.java +C:\classpath-0.04\javax\swing\Timer.java +C:\classpath-0.04\javax\swing\ToggleButtonModel.java +C:\classpath-0.04\javax\swing\UIDefaults.java +C:\classpath-0.04\javax\swing\UIManager.java +C:\classpath-0.04\javax\swing\UnsupportedLookAndFeelException.java +C:\classpath-0.04\javax\swing\border\AbstractBorder.java +C:\classpath-0.04\javax\swing\border\BevelBorder.java +C:\classpath-0.04\javax\swing\border\Border.java +C:\classpath-0.04\javax\swing\border\CompoundBorder.java +C:\classpath-0.04\javax\swing\border\EmptyBorder.java +C:\classpath-0.04\javax\swing\border\EtchedBorder.java +C:\classpath-0.04\javax\swing\border\LineBorder.java +C:\classpath-0.04\javax\swing\border\MatteBorder.java +C:\classpath-0.04\javax\swing\border\TitledBorder.java +C:\classpath-0.04\javax\swing\event\AncestorEvent.java +C:\classpath-0.04\javax\swing\event\AncestorListener.java +C:\classpath-0.04\javax\swing\event\CaretEvent.java +C:\classpath-0.04\javax\swing\event\CaretListener.java +C:\classpath-0.04\javax\swing\event\CellEditorListener.java +C:\classpath-0.04\javax\swing\event\ChangeEvent.java +C:\classpath-0.04\javax\swing\event\ChangeListener.java +C:\classpath-0.04\javax\swing\event\DocumentEvent.java +C:\classpath-0.04\javax\swing\event\DocumentListener.java +C:\classpath-0.04\javax\swing\event\EventListenerList.java +C:\classpath-0.04\javax\swing\event\HyperlinkEvent.java +C:\classpath-0.04\javax\swing\event\HyperlinkListener.java +C:\classpath-0.04\javax\swing\event\InternalFrameAdapter.java +C:\classpath-0.04\javax\swing\event\InternalFrameEvent.java +C:\classpath-0.04\javax\swing\event\InternalFrameListener.java +C:\classpath-0.04\javax\swing\event\ListDataEvent.java +C:\classpath-0.04\javax\swing\event\ListDataListener.java +C:\classpath-0.04\javax\swing\event\ListSelectionEvent.java +C:\classpath-0.04\javax\swing\event\ListSelectionListener.java +C:\classpath-0.04\javax\swing\event\MenuDragMouseEvent.java +C:\classpath-0.04\javax\swing\event\MenuDragMouseListener.java +C:\classpath-0.04\javax\swing\event\MenuEvent.java +C:\classpath-0.04\javax\swing\event\MenuKeyEvent.java +C:\classpath-0.04\javax\swing\event\MenuKeyListener.java +C:\classpath-0.04\javax\swing\event\MenuListener.java +C:\classpath-0.04\javax\swing\event\MouseInputAdapter.java +C:\classpath-0.04\javax\swing\event\MouseInputListener.java +C:\classpath-0.04\javax\swing\event\PopupMenuEvent.java +C:\classpath-0.04\javax\swing\event\PopupMenuListener.java +C:\classpath-0.04\javax\swing\event\SwingPropertyChangeSupport.java +C:\classpath-0.04\javax\swing\event\TableColumnModelEvent.java +C:\classpath-0.04\javax\swing\event\TableColumnModelListener.java +C:\classpath-0.04\javax\swing\event\TableModelEvent.java +C:\classpath-0.04\javax\swing\event\TableModelListener.java +C:\classpath-0.04\javax\swing\event\TreeExpansionEvent.java +C:\classpath-0.04\javax\swing\event\TreeExpansionListener.java +C:\classpath-0.04\javax\swing\event\TreeModelEvent.java +C:\classpath-0.04\javax\swing\event\TreeModelListener.java +C:\classpath-0.04\javax\swing\event\TreeSelectionEvent.java +C:\classpath-0.04\javax\swing\event\TreeSelectionListener.java +C:\classpath-0.04\javax\swing\event\TreeWillExpandListener.java +C:\classpath-0.04\javax\swing\event\UndoableEditEvent.java +C:\classpath-0.04\javax\swing\event\UndoableEditListener.java +C:\classpath-0.04\javax\swing\plaf\BorderUIResource.java +C:\classpath-0.04\javax\swing\plaf\ButtonUI.java +C:\classpath-0.04\javax\swing\plaf\ColorUIResource.java +C:\classpath-0.04\javax\swing\plaf\ComponentUI.java +C:\classpath-0.04\javax\swing\plaf\DimensionUIResource.java +C:\classpath-0.04\javax\swing\plaf\FontUIResource.java +C:\classpath-0.04\javax\swing\plaf\IconUIResource.java +C:\classpath-0.04\javax\swing\plaf\InsetsUIResource.java +C:\classpath-0.04\javax\swing\plaf\LabelUI.java +C:\classpath-0.04\javax\swing\plaf\ListUI.java +C:\classpath-0.04\javax\swing\plaf\OptionPaneUI.java +C:\classpath-0.04\javax\swing\plaf\PanelUI.java +C:\classpath-0.04\javax\swing\plaf\ScrollPaneUI.java +C:\classpath-0.04\javax\swing\plaf\TabbedPaneUI.java +C:\classpath-0.04\javax\swing\plaf\TextUI.java +C:\classpath-0.04\javax\swing\plaf\TreeUI.java +C:\classpath-0.04\javax\swing\plaf\UIResource.java +C:\classpath-0.04\javax\swing\plaf\ViewportUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicBorders.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicButtonUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicCheckBoxUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicDefaults.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicGraphicsUtils.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicIconFactory.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicLabelUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicListUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicLookAndFeel.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicOptionPaneUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicPanelUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicRadioButtonUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicScrollPaneUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicTabbedPaneUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicTextUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicToggleButtonUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicTreeUI.java +C:\classpath-0.04\javax\swing\plaf\basic\BasicViewportUI.java +C:\classpath-0.04\javax\swing\plaf\metal\MetalLookAndFeel.java +C:\classpath-0.04\javax\swing\table\AbstractTableModel.java +C:\classpath-0.04\javax\swing\table\DefaultTableCellRenderer.java +C:\classpath-0.04\javax\swing\table\DefaultTableColumnModel.java +C:\classpath-0.04\javax\swing\table\DefaultTableModel.java +C:\classpath-0.04\javax\swing\table\TableCellEditor.java +C:\classpath-0.04\javax\swing\table\TableCellRenderer.java +C:\classpath-0.04\javax\swing\table\TableColumn.java +C:\classpath-0.04\javax\swing\table\TableColumnModel.java +C:\classpath-0.04\javax\swing\table\TableModel.java +C:\classpath-0.04\javax\swing\text\AbstractDocument.java +C:\classpath-0.04\javax\swing\text\AttributeSet.java +C:\classpath-0.04\javax\swing\text\BadLocationException.java +C:\classpath-0.04\javax\swing\text\Caret.java +C:\classpath-0.04\javax\swing\text\CharacterIterator.java +C:\classpath-0.04\javax\swing\text\ComponentView.java +C:\classpath-0.04\javax\swing\text\DefaultCaret.java +C:\classpath-0.04\javax\swing\text\DefaultEditorKit.java +C:\classpath-0.04\javax\swing\text\Document.java +C:\classpath-0.04\javax\swing\text\EditorKit.java +C:\classpath-0.04\javax\swing\text\Element.java +C:\classpath-0.04\javax\swing\text\GapContent.java +C:\classpath-0.04\javax\swing\text\JTextComponent.java +C:\classpath-0.04\javax\swing\text\Keymap.java +C:\classpath-0.04\javax\swing\text\PlainDocument.java +C:\classpath-0.04\javax\swing\text\PlainEditorKit.java +C:\classpath-0.04\javax\swing\text\Position.java +C:\classpath-0.04\javax\swing\text\Segment.java +C:\classpath-0.04\javax\swing\text\Style.java +C:\classpath-0.04\javax\swing\text\View.java +C:\classpath-0.04\javax\swing\text\ViewFactory.java +C:\classpath-0.04\javax\swing\tree\AbstractLayoutCache.java +C:\classpath-0.04\javax\swing\tree\DefaultMutableTreeNode.java +C:\classpath-0.04\javax\swing\tree\DefaultTreeCellEditor.java +C:\classpath-0.04\javax\swing\tree\DefaultTreeCellRenderer.java +C:\classpath-0.04\javax\swing\tree\DefaultTreeModel.java +C:\classpath-0.04\javax\swing\tree\DefaultTreeSelectionModel.java +C:\classpath-0.04\javax\swing\tree\ExpandVetoException.java +C:\classpath-0.04\javax\swing\tree\FixedHeightLayoutCache.java +C:\classpath-0.04\javax\swing\tree\MutableTreeNode.java +C:\classpath-0.04\javax\swing\tree\RowMapper.java +C:\classpath-0.04\javax\swing\tree\TreeCellEditor.java +C:\classpath-0.04\javax\swing\tree\TreeCellRenderer.java +C:\classpath-0.04\javax\swing\tree\TreeModel.java +C:\classpath-0.04\javax\swing\tree\TreeNode.java +C:\classpath-0.04\javax\swing\tree\TreePath.java +C:\classpath-0.04\javax\swing\tree\TreeSelectionModel.java +C:\classpath-0.04\javax\swing\tree\VariableHeightLayoutCache.java +C:\classpath-0.04\javax\swing\undo\AbstractUndoableEdit.java +C:\classpath-0.04\javax\swing\undo\CannotRedoException.java +C:\classpath-0.04\javax\swing\undo\CannotUndoException.java +C:\classpath-0.04\javax\swing\undo\CompoundEdit.java +C:\classpath-0.04\javax\swing\undo\StateEdit.java +C:\classpath-0.04\javax\swing\undo\StateEditable.java +C:\classpath-0.04\javax\swing\undo\UndoableEdit.java +C:\classpath-0.04\javax\swing\undo\UndoableEditSupport.java +C:\classpath-0.04\javax\swing\undo\UndoManager.java +C:\classpath-0.04\vm\reference\gnu\vm\stack\StackFrame.java +C:\classpath-0.04\vm\reference\gnu\vm\stack\StackTrace.java +C:\classpath-0.04\vm\reference\java\lang\Runtime.java +C:\classpath-0.04\vm\reference\java\lang\Throwable.java +C:\classpath-0.04\vm\reference\java\lang\VMObject.java +C:\classpath-0.04\vm\reference\java\lang\VMSecurityManager.java +C:\classpath-0.04\vm\reference\java\lang\VMSystem.java diff --git a/classpath/classpath.build b/classpath/classpath.build new file mode 100644 index 00000000..22d9318d --- /dev/null +++ b/classpath/classpath.build @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<project name="classpath.dll" default="classpath.dll"> + <property name="Classpath.dir" value="/classpath-0.04" /> + + <target name="jars"> + <!-- TODO + <exec program="${nant.project.basedir}/../bin/netexp.exe" commandline="" /> + --> + </target> + + <target name="classes" depends="jars" description="Classpath + IK.VM.NET specifica Java classes"> + <delete> + <fileset basedir="${Classpath.dir}"> + <includes name="**.class"/> + </fileset> + </delete> + <delete> + <fileset basedir="."> + <includes name="**.class"/> + </fileset> + </delete> + <property name="classpath" value=".;${Classpath.dir};${Classpath.dir}/vm/reference;mscorlib.jar;System.jar" /> + <exec program="jikes.exe" commandline="-classpath ${classpath} @allsources.lst"/> + </target> + + <target name="classpath.dll" depends="classes"> + <exec program="${nant.project.basedir}/../bin/ikvmc.exe" commandline="-nojni -out:classpath.dll -target:library -recurse:./*.class -recurse:${Classpath.dir}/*.class mscorlib.jar System.jar System.Xml.jar" /> + <copy file="classpath.dll" tofile="../bin/classpath.dll.new" /> + <exec program="peverify" commandline="../bin/classpath.dll.new" /> + <copy file="classpath.dll" todir="../bin" /> + <delete file="../bin/classpath.dll.new" /> + </target> +</project> diff --git a/classpath/gnu/classpath/Configuration.java b/classpath/gnu/classpath/Configuration.java new file mode 100644 index 00000000..3e7b6558 --- /dev/null +++ b/classpath/gnu/classpath/Configuration.java @@ -0,0 +1,98 @@ +/* gnu.classpath.Configuration + Copyright (C) 1998, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath; + +/** + * This file defines compile-time constants that can be accessed by + * java code. It is pre-processed by configure. + */ +public interface Configuration +{ + // TODO + String CLASSPATH_HOME = ""; + String CLASSPATH_VERSION = ""; + + + /** + * The value of DEBUG is substituted according to whether the + * "--enable-debug" argument was passed to configure. Code + * which is made conditional based on the value of this flag - typically + * code that generates debugging output - will be removed by the optimizer + * in a non-debug build. + */ + boolean DEBUG = false; + + /** + * The value of LOAD_LIBRARY is substituted according to whether the + * "--enable-load-library" or "--disable-load-library" argument was passed + * to configure. By default, configure should define this is as true. + * If set to false, loadLibrary() calls to load native function + * implementations, typically found in static initializers of classes + * which contain native functions, will be omitted. This is useful for + * runtimes which pre-link their native function implementations and do + * not require additional shared libraries to be loaded. + */ + boolean INIT_LOAD_LIBRARY = false; + + /** + * Set to true if the VM provides a native method to implement + * Proxy.getProxyClass completely, including argument verification. + * If this is true, HAVE_NATIVE_GET_PROXY_DATA and + * HAVE_NATIVE_GENERATE_PROXY_CLASS should be false. + * @see java.lang.reflect.Proxy + */ + boolean HAVE_NATIVE_GET_PROXY_CLASS = false; + + /** + * Set to true if the VM provides a native method to implement + * the first part of Proxy.getProxyClass: generation of the array + * of methods to convert, and verification of the arguments. + * If this is true, HAVE_NATIVE_GET_PROXY_CLASS should be false. + * @see java.lang.reflect.Proxy + */ + boolean HAVE_NATIVE_GET_PROXY_DATA = false; + + /** + * Set to true if the VM provides a native method to implement + * the second part of Proxy.getProxyClass: conversion of an array of + * methods into an actual proxy class. + * If this is true, HAVE_NATIVE_GET_PROXY_CLASS should be false. + * @see java.lang.reflect.Proxy + */ + boolean HAVE_NATIVE_GENERATE_PROXY_CLASS = false; +} diff --git a/classpath/gnu/java/net/protocol/ikvmres/Handler.java b/classpath/gnu/java/net/protocol/ikvmres/Handler.java new file mode 100644 index 00000000..7d5c8061 --- /dev/null +++ b/classpath/gnu/java/net/protocol/ikvmres/Handler.java @@ -0,0 +1,103 @@ +/* + 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 + +*/ + +package gnu.java.net.protocol.ikvmres; + +import system.io.*; +import system.reflection.*; +import java.net.*; +import java.io.*; +import java.io.IOException; + +class IkvmresURLConnection extends URLConnection +{ + private InputStream inputStream; + + IkvmresURLConnection(URL url) + { + super(url); + doOutput = false; + } + + public void connect() throws IOException + { + if(!connected) + { + String assembly = url.getHost(); + String resource = url.getFile(); + // TODO error handling + FieldInfo fi = Assembly.Load(assembly).GetLoadedModules()[0].GetField(resource); + byte[] b = new byte[system.runtime.interopservices.Marshal.SizeOf(fi.get_FieldType())]; + InitArray(b, fi); + inputStream = new ByteArrayInputStream(b); + connected = true; + } + } + private static native void InitArray(byte[] buf, FieldInfo field); + + public InputStream getInputStream() throws IOException + { + if(!connected) + { + connect(); + } + return inputStream; + } + + public OutputStream getOutputStream() throws IOException + { + throw new IOException("resource URLs are read only"); + } + + public long getLastModified() + { + return -1; + } + + public int getContentLength() + { + return -1; + } +} + +public class Handler extends URLStreamHandler +{ + protected URLConnection openConnection(URL url) throws IOException + { + return new IkvmresURLConnection(url); + } + + protected void parseURL(URL url, String url_string, int start, int end) + { + int colon = url_string.indexOf(':', start); + String file = url_string.substring(start, colon); + String assembly = url_string.substring(colon + 1); + setURL(url, "ikvmres", assembly, 0, file, null); + } + + protected String toExternalForm(URL url) + { + return "ikvmres:" + url.getFile() + ":" + url.getHost(); + } +} diff --git a/classpath/ikvm/awt/NetToolkit.java b/classpath/ikvm/awt/NetToolkit.java new file mode 100644 index 00000000..dde619c3 --- /dev/null +++ b/classpath/ikvm/awt/NetToolkit.java @@ -0,0 +1,316 @@ +/* + 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 + +*/ + +package ikvm.awt; + +import java.awt.*; +import java.awt.datatransfer.*; +import java.awt.image.*; +import java.awt.peer.*; +import java.net.*; +import java.util.*; +import java.awt.List; + +public class NetToolkit extends Toolkit +{ + private static EventQueue eventQueue = new EventQueue(); + + // NOTE "native" is just an easy way to say I haven't implemented it yet + + protected java.awt.peer.ButtonPeer createButton(Button target) throws HeadlessException + { + return new NetButtonPeer(); + } + + protected java.awt.peer.TextFieldPeer createTextField(TextField target) throws HeadlessException + { + return new NetTextFieldPeer(); + } + + protected native java.awt.peer.LabelPeer createLabel(Label target) throws HeadlessException; + protected native java.awt.peer.ListPeer createList(List target) throws HeadlessException; + protected native java.awt.peer.CheckboxPeer createCheckbox(Checkbox target) + throws HeadlessException; + protected native java.awt.peer.ScrollbarPeer createScrollbar(Scrollbar target) + throws HeadlessException; + protected native java.awt.peer.ScrollPanePeer createScrollPane(ScrollPane target) + throws HeadlessException; + + protected java.awt.peer.TextAreaPeer createTextArea(TextArea target) throws HeadlessException + { + return new NetTextAreaPeer(); + } + + protected native java.awt.peer.ChoicePeer createChoice(Choice target) + throws HeadlessException; + + protected java.awt.peer.FramePeer createFrame(Frame target) throws HeadlessException + { + return new NetFramePeer(); + } + + protected native java.awt.peer.CanvasPeer createCanvas(Canvas target); + + protected java.awt.peer.PanelPeer createPanel(Panel target) + { + return new NetPanelPeer(); + } + + protected native java.awt.peer.WindowPeer createWindow(Window target) + throws HeadlessException; + protected native java.awt.peer.DialogPeer createDialog(Dialog target) + throws HeadlessException; + protected native java.awt.peer.MenuBarPeer createMenuBar(MenuBar target) + throws HeadlessException; + protected native java.awt.peer.MenuPeer createMenu(Menu target) + throws HeadlessException; + protected native java.awt.peer.PopupMenuPeer createPopupMenu(PopupMenu target) + throws HeadlessException; + protected native java.awt.peer.MenuItemPeer createMenuItem(MenuItem target) + throws HeadlessException; + protected native java.awt.peer.FileDialogPeer createFileDialog(FileDialog target) + throws HeadlessException; + protected native java.awt.peer.CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) + throws HeadlessException; + protected native java.awt.peer.FontPeer getFontPeer(String name, int style); + public native Dimension getScreenSize() throws HeadlessException; + public native int getScreenResolution() throws HeadlessException; + public native ColorModel getColorModel() + throws HeadlessException; + public native String[] getFontList(); + public native FontMetrics getFontMetrics(Font font); + public native void sync(); + public native Image getImage(String filename); + public native Image getImage(URL url); + public native Image createImage(String filename); + public native Image createImage(URL url); + public native boolean prepareImage(Image image, + int width, + int height, + ImageObserver observer); + public native int checkImage(Image image, + int width, + int height, + ImageObserver observer); + public native Image createImage(ImageProducer producer); + public native Image createImage(byte[] imagedata, + int imageoffset, + int imagelength); + public native PrintJob getPrintJob(Frame frame, + String jobtitle, + Properties props); + public native void beep(); + public native Clipboard getSystemClipboard() + throws HeadlessException; + + protected EventQueue getSystemEventQueueImpl() + { + return eventQueue; + } + +// public native java.awt.dnd.peer.DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) +// throws InvalidDnDOperationException; +// public native Map mapInputMethodHighlight(InputMethodHighlight highlight) +// throws HeadlessException; +} + +class NetComponentPeer implements ComponentPeer +{ + public native int checkImage(Image img, int width, int height, ImageObserver ob); + public native Image createImage(ImageProducer prod); + public native Image createImage(int width, int height); + public native void disable(); + public native void dispose(); + public native void enable(); + public native ColorModel getColorModel(); + public native FontMetrics getFontMetrics(Font f); + public native Graphics getGraphics(); + public native Point getLocationOnScreen(); + public native Dimension getMinimumSize(); + + public Dimension getPreferredSize() + { + System.out.println("NOTE: NetComponentPeer.getPreferredSize not implemented"); + return new Dimension(0, 0); + } + + public Toolkit getToolkit() + { + return Toolkit.getDefaultToolkit(); + } + + public native void handleEvent(AWTEvent e); + public native void hide(); + public native boolean isFocusTraversable(); + public native Dimension minimumSize(); + + public Dimension preferredSize() + { + System.out.println("NOTE: NetComponentPeer.preferredSize not implemented"); + return new Dimension(0, 0); + } + + public native void paint(Graphics graphics); + public native boolean prepareImage(Image img, int width, int height, ImageObserver ob); + public native void print(Graphics graphics); + public native void repaint(long tm, int x, int y, int width, int height); + public native void requestFocus(); + public native void reshape(int x, int y, int width, int height); + public native void setBackground(Color color); + public native void setBounds(int x, int y, int width, int height); + public native void setCursor(Cursor cursor); + + public void setEnabled(boolean enabled) + { + System.out.println("NOTE: NetComponentPeer.setEnabled not implemented"); + } + + public native void setFont(Font font); + public native void setForeground(Color color); + + public void setVisible(boolean visible) + { + System.out.println("NOTE: NetComponentPeer.setVisible not implemented"); + } + + public native void show(); + public native GraphicsConfiguration getGraphicsConfiguration(); + + public void setEventMask (long mask) + { + System.out.println("NOTE: NetComponentPeer.setEventMask not implemented"); + } +} + +class NetButtonPeer extends NetComponentPeer implements ButtonPeer +{ + public native void setLabel(String label); +} + +class NetTextComponentPeer extends NetComponentPeer implements TextComponentPeer +{ + public native int getSelectionEnd(); + public native int getSelectionStart(); + + public String getText() + { + System.out.println("NOTE: NetTextComponentPeer.getText not implemented"); + return ""; + } + + public void setText(String text) + { + System.out.println("NOTE: NetTextComponentPeer.setText not implemented"); + } + + public native void select(int start_pos, int end_pos); + public native void setEditable(boolean editable); + public native int getCaretPosition(); + public native void setCaretPosition(int pos); +} + +class NetTextFieldPeer extends NetTextComponentPeer implements TextFieldPeer +{ + public native Dimension minimumSize(int len); + public native Dimension preferredSize(int len); + public native Dimension getMinimumSize(int len); + + public Dimension getPreferredSize(int len) + { + System.out.println("NOTE: NetTextFieldPeer.getPreferredSize not implemented"); + return new Dimension(0, 0); + } + + public native void setEchoChar(char echo_char); + public native void setEchoCharacter(char echo_char); +} + +class NetTextAreaPeer extends NetTextComponentPeer implements TextAreaPeer +{ + public void insert(String text, int pos) + { + System.out.println("NOTE: NetTextAreaPeer.insert not implemented"); + } + + public native void insertText(String text, int pos); + public native Dimension minimumSize(int rows, int cols); + public native Dimension getMinimumSize(int rows, int cols); + public native Dimension preferredSize(int rows, int cols); + + public Dimension getPreferredSize(int rows, int cols) + { + System.out.println("NOTE: NetTextAreaPeer.getPreferredSize not implemented"); + return new Dimension(0, 0); + } + + public native void replaceRange(String text, int start_pos, int end_pos); + public native void replaceText(String text, int start_pos, int end_pos); +} + +class NetContainerPeer extends NetComponentPeer implements ContainerPeer +{ + public native Insets insets(); + + public Insets getInsets() + { + System.out.println("NOTE: NetContainerPeer.getInsets not implemented"); + return new Insets(0, 0, 0, 0); + } + + public void beginValidate() + { + System.out.println("NOTE: NetContainerPeer.beginValidate not implemented"); + } + + public void endValidate() + { + System.out.println("NOTE: NetContainerPeer.endValidate not implemented"); + } +} + +class NetPanelPeer extends NetContainerPeer implements PanelPeer +{ +} + +class NetWindowPeer extends NetContainerPeer implements WindowPeer +{ + public native void toBack(); + + public void toFront() + { + System.out.println("NOTE: NetWindowPeer.toFront not implemented"); + } +} + +class NetFramePeer extends NetWindowPeer implements FramePeer +{ + NetFramePeer() + { + } + + public native void setIconImage(Image image); + public native void setMenuBar(MenuBar mb); + public native void setResizable(boolean resizable); + public native void setTitle(String title); +} diff --git a/classpath/java/io/FileDescriptor.java b/classpath/java/io/FileDescriptor.java new file mode 100644 index 00000000..54ca461a --- /dev/null +++ b/classpath/java/io/FileDescriptor.java @@ -0,0 +1,249 @@ +/* + 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 + +*/ +package java.io; + +import system.Console; +import system.io.*; + +public final class FileDescriptor +{ + public static final FileDescriptor in = new FileDescriptor(Console.OpenStandardInput()); + public static final FileDescriptor out = new FileDescriptor(Console.OpenStandardOutput()); + public static final FileDescriptor err = new FileDescriptor(Console.OpenStandardError()); + private Stream stream; + + public FileDescriptor() + { + } + + private FileDescriptor(Stream stream) + { + this.stream = stream; + } + + public synchronized void sync() throws SyncFailedException + { + if(stream == null) + { + throw new SyncFailedException("The handle is invalid"); + } + try + { + if(false) throw new system.io.IOException(); + stream.Flush(); + } + catch(system.io.IOException x) + { + throw new SyncFailedException(x.get_Message()); + } + } + + public synchronized boolean valid() + { + return stream != null; + } + + synchronized void close() throws IOException + { + if(stream != null) + { + // HACK don't close stdin because that throws a NotSupportedException (bug in System.IO.__ConsoleStream) + if(stream != in.stream) + { + try + { + stream.Close(); + if(false) throw new system.io.IOException(); + } + catch(system.io.IOException x) + { + throw new IOException(x.get_Message()); + } + } + stream = null; + } + } + + private static String demanglePath(String path) + { + // HACK for some reason Java accepts: \c:\foo.txt + // I don't know what else, but for now lets just support this + if(path.length() > 3 && path.charAt(0) == '\\' && path.charAt(2) == ':') + { + path = path.substring(1); + } + return path; + } + + static FileDescriptor open(String name, boolean append, boolean create, boolean read, boolean write) throws FileNotFoundException + { + try + { + if(false) throw new system.io.IOException(); + if(false) throw new system.security.SecurityException(); + FileStream fs = system.io.File.Open(demanglePath(name), + append ? FileMode.Append : (create ? FileMode.OpenOrCreate : FileMode.Open), + write ? (read ? FileAccess.ReadWrite : FileAccess.Write) : FileAccess.Read, FileShare.ReadWrite); + return new FileDescriptor(fs); + } + catch(system.security.SecurityException x1) + { + throw new SecurityException(x1.get_Message()); + } + catch(system.io.IOException x2) + { + throw new FileNotFoundException(x2.get_Message()); + } + // TODO map al the other exceptions as well... + } + + synchronized long getFilePointer() throws IOException + { + if(stream == null) + { + throw new IOException(); + } + try + { + if(false) throw new system.io.IOException(); + return stream.get_Position(); + } + catch(system.io.IOException x) + { + throw new IOException(x.get_Message()); + } + // TODO map al the other exceptions as well... + } + + synchronized long getLength() throws IOException + { + if(stream == null) + { + throw new IOException(); + } + try + { + if(false) throw new system.io.IOException(); + return stream.get_Length(); + } + catch(system.io.IOException x) + { + throw new IOException(x.get_Message()); + } + // TODO map al the other exceptions as well... + } + + synchronized void setLength(long length) throws IOException + { + if(stream == null) + { + throw new IOException(); + } + try + { + if(false) throw new system.io.IOException(); + stream.SetLength(length); + } + catch(system.io.IOException x) + { + throw new IOException(x.get_Message()); + } + // TODO map al the other exceptions as well... + } + + synchronized void seek(long pos) throws IOException + { + if(stream == null) + { + throw new IOException(); + } + try + { + if(false) throw new system.io.IOException(); + stream.Seek(pos, SeekOrigin.Begin); + } + catch(system.io.IOException x) + { + throw new IOException(x.get_Message()); + } + // TODO map al the other exceptions as well... + } + + synchronized int read(byte[] buf, int offset, int length) throws IOException + { + if(stream == null) + { + throw new IOException(); + } + try + { + if(false) throw new system.io.IOException(); + return stream.Read(buf, offset, length); + } + catch(system.io.IOException x) + { + throw new IOException(x.get_Message()); + } + // TODO map al the other exceptions as well... + } + + synchronized void write(byte[] buf, int offset, int length) throws IOException + { + if(stream == null) + { + throw new IOException(); + } + try + { + if(false) throw new system.io.IOException(); + stream.Write(buf, offset, length); + } + catch(system.io.IOException x) + { + throw new IOException(x.get_Message()); + } + // TODO map al the other exceptions as well... + } + + synchronized long skip(long n) throws IOException + { + if(stream == null) + { + throw new IOException(); + } + try + { + if(false) throw new system.io.IOException(); + // TODO this is broken, because non-seekable streams should support skip as well... + // (and I don't think we should seek past EOF here) + stream.Seek(n, SeekOrigin.Current); + return n; + } + catch(system.io.IOException x) + { + throw new IOException(x.get_Message()); + } + // TODO map al the other exceptions as well... + } +} diff --git a/classpath/java/io/FileInputStream.java b/classpath/java/io/FileInputStream.java new file mode 100644 index 00000000..78e2f03e --- /dev/null +++ b/classpath/java/io/FileInputStream.java @@ -0,0 +1,396 @@ +/* FileInputStream.java -- An input stream that reads from disk files. + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Configuration; +import java.nio.channels.FileChannel; +import gnu.java.nio.FileChannelImpl; + +/** + * This class is a stream that reads its bytes from a file. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class FileInputStream extends InputStream +{ + +/*************************************************************************/ + +/* + * Class Variables and Initializers + */ + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javaio"); + } + } + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This is the native file handle for the file this stream is reading from + */ +private FileDescriptor fd; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This method initializes a <code>FileInputStream</code> to read from the + * specified named file. A security check is first made to determine + * whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the <code>SecurityManager</code> + * (if one exists) with the name of this file. An exception is thrown + * if reading is not allowed. If the file does not exist, an exception + * is also thrown. + * + * @param name The name of the file this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + * @exception FileNotFoundException If the file does not exist. + */ +public +FileInputStream(String name) throws SecurityException, FileNotFoundException +{ + this(new File(name)); +} + +/*************************************************************************/ + +/** + * This method initializes a <code>FileInputStream</code> to read from the + * specified <code>File</code> object. A security check is first made to determine + * whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the <code>SecurityManager</code> + * (if one exists) with the name of this file. An exception is thrown + * if reading is not allowed. If the file does not exist, an exception + * is also thrown. + * + * @param file The <code>File</code> object this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + * @exception FileNotFoundException If the file does not exist. + */ +public +FileInputStream(File file) throws SecurityException, FileNotFoundException +{ + // Talk about the lazy man's way out. The File.exists() method does + // a security check so don't repeat it here. + if (!file.exists()) + throw new FileNotFoundException(file.getName()); + + fd = FileDescriptor.open(file.getPath(), false, false, true, false); +} + +/*************************************************************************/ + +/** + * This method initializes a <code>FileInputStream</code> to read from the + * specified <code>FileDescriptor</code> object. A security check is first made to + * determine whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the <code>SecurityManager</code> + * (if one exists) with the specified <code>FileDescriptor</code> An exception is + * thrown if reading is not allowed. + * + * @param fd The <code>FileDescriptor</code> object this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + */ +public +FileInputStream(FileDescriptor fd) throws SecurityException +{ + // Hmmm, no other exception but this one to throw, but if the descriptor + // isn't valid, we surely don't have "permission" to read from it. + if (!fd.valid()) + throw new SecurityException("Invalid FileDescriptor"); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkRead(fd); + + this.fd = fd; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This method returns a <code>FileDescriptor</code> object representing the + * underlying native file handle of the file this stream is reading + * from + * + * @return A <code>FileDescriptor</code> for this stream + * + * @exception IOException If an error occurs + */ +public final FileDescriptor +getFD() throws IOException +{ + return fd; +} + +/*************************************************************************/ + +/** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * This method returns the number of unread bytes remaining in the file if + * the descriptor being read from is an actual file. If this method is + * reading from a ''special'' file such a the standard input, this method + * will return the appropriate value for the stream being read. + * <p> + * Be aware that reads on plain files that do not reside locally might + * possibly block even if this method says they should not. For example, + * a remote server might crash, preventing an NFS mounted file from being + * read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ +public int +available() throws IOException +{ + int retval = (int)(fd.getLength() - fd.getFilePointer()); + + if (retval <= 0) + return(0); + + return(retval); +} + +/*************************************************************************/ + +/** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * <p> + * This method reads and discards bytes into a 256 byte array until the + * specified number of bytes were skipped or until either the end of stream + * is reached or a read attempt returns a short count. Subclasses can + * override this metho to provide a more efficient implementation where + * one exists. + * + * @param num_bytes The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ +public long +skip(long num_bytes) throws IOException +{ + if (num_bytes <= 0) + return(0); + + if (num_bytes > available()) + num_bytes = available(); + + long bytes_skipped = fd.skip(num_bytes); + + return(bytes_skipped); +} + +/*************************************************************************/ + +/** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ +public int +read() throws IOException +{ + byte[] buf = new byte[1]; + + int bytes_read = fd.read(buf, 0, buf.length); + if (bytes_read == 0) + return(-1); + if (bytes_read == -1) + return(-1); + + return((buf[0] & 0xFF)); +} + +/*************************************************************************/ + +/** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling an overloaded read method like so: + * <code>read(buf, 0, buf.length)</code> + * + * @param buf The buffer into which the bytes read will be stored. + * + * @return The number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ +public int +read(byte[] buf) throws IOException +{ + return(read(buf, 0, buf.length)); +} + +/*************************************************************************/ + +/** + * This method read bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> into + * the buffer and attempts to read <code>len</code> bytes. This method can + * return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returned to indicate the + * end of the stream. + * <p> + * This method will block until some data can be read. + * + * @param buf The array into which the bytes read should be stored + * @param offset The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ +public int +read(byte[] buf, int offset, int len) throws IOException +{ + if (len == 0) + return(0); + + int bytes_read = fd.read(buf, offset, len); + if (bytes_read == 0) + return(-1); + if (bytes_read == -1) + return(-1); + + return(bytes_read); +} + +/*************************************************************************/ + +/** + * This method closes the stream. Any futher attempts to read from the + * stream will likely generate an IOException since the underlying file + * will be closed. + * + * @exception IOException If an error occurs. + */ +public void +close() throws IOException +{ + fd.close(); +} + +/*************************************************************************/ + +/** + * This method ensures that the underlying file is closed when this object + * is garbage collected. Since there is a system dependent limit on how + * many files a single process can have open, it is a good idea to close + * streams when they are no longer needed rather than waiting for garbage + * collectio to automatically close them. + * + * @exception IOException If an error occurs + */ +protected void +finalize() throws IOException +{ + if(fd != null) + { + close(); + } +} + + +/*************************************************************************/ + +/** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + +private FileChannel ch; /* cached associated file-channel */ + +public FileChannel +getChannel() +{ + synchronized (this) + { +// if (ch == null) +// ch = new gnu.java.nio.FileChannelImpl(native_fd, +// this); + } + return ch; +} + + +} // class FileInputStream + diff --git a/classpath/java/io/FileOutputStream.java b/classpath/java/io/FileOutputStream.java new file mode 100644 index 00000000..bd7f5cb8 --- /dev/null +++ b/classpath/java/io/FileOutputStream.java @@ -0,0 +1,339 @@ +/* FileOutputStream.java -- Writes to a file on disk. + Copyright (C) 1998, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Configuration; +import java.nio.channels.FileChannel; +import gnu.java.nio.FileChannelImpl; + +/** + * This classes allows a stream of data to be written to a disk file or + * any open <code>FileDescriptor</code>. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class FileOutputStream extends OutputStream +{ + +/*************************************************************************/ + +/* + * Class Variables and Initializers + */ + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javaio"); + } + } + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This is the native file handle + */ +private FileDescriptor fd; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This method initializes a <code>FileOutputStream</code> object to write + * to the named file. The file is created if it does not exist, and + * the bytes written are written starting at the beginning of the file. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param name The name of the file this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + * @exception IOException If a non-security error occurs + */ +public +FileOutputStream(String name) throws SecurityException, IOException +{ + this(name, false); +} + +/*************************************************************************/ + +/** + * This method initializes a <code>FileOutputStream</code> object to write + * to the specified <code>File</code> object. The file is created if it + * does not exist, and the bytes written are written starting at the + * beginning of the file. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param file The <code>File</code> object this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + * @exception IOException If a non-security error occurs + */ +public +FileOutputStream(File file) throws SecurityException, IOException +{ + this(file.getPath(), false); +} + +/*************************************************************************/ + +/** + * This method initializes a <code>FileOutputStream</code> object to write + * to the named file. The file is created if it does not exist, and + * the bytes written are written starting at the beginning of the file if + * the <code>append</code> argument is <code>false</code> or at the end + * of the file if the <code>append</code> argument is true. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param name The name of the file this stream should write to + * @param append <code>true</code> to append bytes to the end of the file, or <code>false</code> to write bytes to the beginning + * + * @exception SecurityException If write access to the file is not allowed + * @exception IOException If a non-security error occurs + */ +public +FileOutputStream(String name, boolean append) throws SecurityException, + IOException +{ + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { +// try +// { + sm.checkWrite(name); +// } +// catch(AccessControlException e) +// { +// throw new SecurityException(e.getMessage()); +// } + } + + fd = FileDescriptor.open((new File(name)).getAbsolutePath(), append, true, false, true); +} + +/*************************************************************************/ + +/** + * This method initializes a <code>FileOutputStream</code> object to write + * to the file represented by the specified <code>FileDescriptor</code> + * object. This method does not create any underlying disk file or + * reposition the file pointer of the given descriptor. It assumes that + * this descriptor is ready for writing as is. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the specified <code>FileDescriptor</code> as an argument. + * An exception is thrown if writing is not allowed. + * + * @param file The <code>FileDescriptor</code> this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + */ +public +FileOutputStream(FileDescriptor fd) throws SecurityException +{ + // Hmm, no other exception but this one to throw, but if the descriptor + // isn't valid, we surely don't have "permission" to write to it. + if (!fd.valid()) + throw new SecurityException("Invalid FileDescriptor"); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { +// try +// { + // sm.checkWrite(fd); +// } +// catch(AccessControlException e) +// { +// throw new SecurityException(e.getMessage()); +// } + } + + this.fd = fd; +} + +/*************************************************************************/ + +/** + * This method returns a <code>FileDescriptor</code> object representing + * the file that is currently being written to + * + * @return A <code>FileDescriptor</code> object for this stream + * + * @exception IOException If an error occurs + */ +public final FileDescriptor +getFD() throws IOException +{ + return fd; +} + +/*************************************************************************/ + +/** + * This method writes a single byte of data to the file. + * + * @param b The byte of data to write, passed as an <code>int</code> + * + * @exception IOException If an error occurs + */ +public void +write(int b) throws IOException +{ + byte[] buf = new byte[1]; + + buf[0] = (byte)(b & 0xFF); + fd.write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * This method writes all the bytes in the specified array to the + * file. + * + * @param buf The array of bytes to write to the file + * + * @exception IOException If an error occurs + */ +public void +write(byte[] buf) throws IOException +{ + fd.write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * This method writes <code>len</code> bytes from the byte array + * <code>buf</code> to the file starting at index <code>offset</code>. + * + * @param buf The array of bytes to write to the file + * @param offset The offset into the array to start writing bytes from + * @param len The number of bytes to write to the file + * + * @exception IOException If an error occurs + */ +public void +write(byte[] buf, int offset, int len) throws IOException +{ + fd.write(buf, offset, len); +} + +/*************************************************************************/ + +/** + * This method closes the underlying file. Any further attempts to + * write to this stream will likely generate an exception since the + * file is closed. + * + * @exception IOException If an error occurs + */ +public void +close() throws IOException +{ + fd.close(); +} + +/*************************************************************************/ + +/** + * This method closes the stream when this object is being garbage + * collected. + * + * @exception IOException If an error occurs (ignored by the Java runtime) + */ +protected void +finalize() throws IOException +{ + if(fd != null) + { + close(); + } +} + +/*************************************************************************/ + +/** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + +private FileChannel ch; /* cached associated file-channel */ + +public FileChannel +getChannel() +{ + synchronized (this) + { +// if (ch == null) +// ch = new gnu.java.nio.FileChannelImpl(native_fd, +// this); + } + return ch; +} + + +} // class FileOutputStream + diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java new file mode 100644 index 00000000..195622da --- /dev/null +++ b/classpath/java/io/RandomAccessFile.java @@ -0,0 +1,1197 @@ +/* RandomAccessFile.java -- Class supporting random file I/O + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Configuration; +import java.nio.channels.FileChannel; +import gnu.java.nio.FileChannelImpl; + +/** + * This class allows reading and writing of files at random locations. + * Most Java I/O classes are either pure sequential input or output. This + * class fulfills the need to be able to read the bytes of a file in an + * arbitrary order. In addition, this class implements the + * <code>DataInput</code> and <code>DataOutput</code> interfaces to allow + * the reading and writing of Java primitives. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class RandomAccessFile implements DataOutput, DataInput +{ + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javaio"); + } + } + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The native file descriptor for this file + */ +private FileDescriptor fd; + +/** + * Whether or not this file is open in read only mode + */ +private boolean read_only; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This method initializes a new instance of <code>RandomAccessFile</code> + * to read from the specified file name with the specified access mode. + * The access mode is either "r" for read only access or "rw" for read + * write access. + * <p> + * Note that a <code>SecurityManager</code> check is made prior to + * opening the file to determine whether or not this file is allowed to + * be read or written. + * + * @param name The name of the file to read and/or write + * @param mode "r" for read only or "rw" for read-write access to the file + * + * @exception IllegalArgumentException If <code>mode</code> has an illegal value + * @exception SecurityException If the requested access to the file is not allowed + * @exception IOException If any other error occurs + */ +public +RandomAccessFile(String name, String mode) throws IllegalArgumentException, + SecurityException, + IOException +{ + this(new File(name), mode); +} + +/*************************************************************************/ + +/** + * This method initializes a new instance of <code>RandomAccessFile</code> + * to read from the specified <code>File</code> object with the specified + * access mode. The access mode is either "r" for read only access or "rw" + * for read-write access. + * <p> + * Note that a <code>SecurityManager</code> check is made prior to + * opening the file to determine whether or not this file is allowed to + * be read or written. + * + * @param file The <code>File</code> object to read and/or write. + * @param mode "r" for read only or "rw" for read-write access to the file + * + * @exception IllegalArgumentException If <code>mode</code> has an illegal value + * @exception SecurityException If the requested access to the file is not allowed + * @exception IOException If any other error occurs + */ +public +RandomAccessFile(File file, String mode) throws IllegalArgumentException, + SecurityException, + IOException +{ + // Check the mode + if (!mode.equals("r") && !mode.equals("rw")) + throw new IllegalArgumentException("Bad mode value: " + mode); + + // The obligatory SecurityManager stuff + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { + if (mode.equals("r")) + sm.checkRead(file.getPath()); + else if (mode.equals("rw")) + sm.checkWrite(file.getPath()); + } + + if (mode.equals("r")) + read_only = true; + + fd = FileDescriptor.open(file.getPath(), false, !read_only, true, !read_only); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This method closes the file and frees up all file related system + * resources. Since most operating systems put a limit on how many files + * may be opened at any given time, it is a good idea to close all files + * when no longer needed to avoid hitting this limit + */ +public void +close() throws IOException +{ + fd.close(); +} + +/*************************************************************************/ + +/** + * This method returns a <code>FileDescriptor</code> object that + * represents the native file handle for this file. + * + * @return The <code>FileDescriptor</code> object for this file + * + * @exception IOException If an error occurs + */ +public final FileDescriptor +getFD() throws IOException +{ + return fd; +} + +/*************************************************************************/ + +/** + * This method returns the current offset in the file at which the next + * read or write will occur + * + * @return The current file position + * + * @exception IOException If an error occurs + */ +public long +getFilePointer() throws IOException +{ + return fd.getFilePointer(); +} + +/*************************************************************************/ + +/** + * This method returns the length of the file in bytes + * + * @return The length of the file + * + * @exception IOException If an error occurs + */ +public long +length() throws IOException +{ + return fd.getLength(); +} + +/*************************************************************************/ + +/** + * This method sets the current file position to the specified offset + * from the beginning of the file. Note that some operating systems will + * allow the file pointer to be set past the current end of the file. + * + * @param pos The offset from the beginning of the file at which to set the file pointer + * + * @exception IOException If an error occurs + */ +public void +seek(long pos) throws IOException +{ + fd.seek(pos); +} + +/*************************************************************************/ + +/** + * This method sets the length of the file to the specified length. If + * the currently length of the file is longer than the specified length, + * then the file is truncated to the specified length. If the current + * length of the file is shorter than the specified length, the file + * is extended with bytes of an undefined value. + * <p> + * The file must be open for write access for this operation to succeed. + * + * @param newlen The new length of the file + * + * @exception IOException If an error occurs + */ +public void +setLength(long newlen) throws IOException +{ + if (read_only) + throw new IOException("File is open read only"); + + fd.setLength(newlen); +} + +/*************************************************************************/ + +/** + * This method reads a single byte of data from the file and returns it + * as an integer. + * + * @return The byte read as an int, or -1 if the end of the file was reached. + * + * @exception IOException If an error occurs + */ +public int +read() throws IOException +{ + byte[] buf = new byte[1]; + + int rc = fd.read(buf, 0, buf.length); + if (rc == 0) + return(-1); + + return(buf[0] & 0xFF); +} + +/*************************************************************************/ + +/** + * This method reads bytes from the file into the specified array. The + * bytes are stored starting at the beginning of the array and up to + * <code>buf.length</code> bytes can be read. + * + * @param buf The buffer to read bytes from the file into + * + * @return The actual number of bytes read or -1 if end of file + * + * @exception IOException If an error occurs + */ +public int +read(byte[] buf) throws IOException +{ + int rc = fd.read(buf, 0, buf.length); + if (rc == 0) + return(-1); + else + return(rc); +} + +/*************************************************************************/ + +/** + * This methods reads up to <code>len</code> bytes from the file into the s + * pecified array starting at position <code>offset</code> into the array. + * + * @param buf The array to read the bytes into + * @param offset The index into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @param len The actual number of bytes read, or -1 if end of file + * + * @exception IOException If an error occurs + */ +public int +read(byte[] buf, int offset, int len) throws IOException +{ + int rc = fd.read(buf, offset, len); + if (rc == 0) + return(-1); + else + return(rc); +} + +/*************************************************************************/ + +/** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is <code>false</code> If the byte is non-zero, then + * the value returned is <code>true</code> + * <p> + * This method can read a <code>boolean</code> written by an object implementing the + * <code>writeBoolean()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>boolean</code> value read + * + * @exception EOFException If end of file is reached before reading the boolean + * @exception IOException If any other error occurs + */ +public final boolean +readBoolean() throws EOFException, IOException +{ + int byte_read = read(); + + if (byte_read == -1) + throw new EOFException("Unexpected end of stream"); + + return(DataInputStream.convertToBoolean(byte_read)); +} + +/*************************************************************************/ + +/** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + * <p> + * This method can read a <code>byte</code> written by an object implementing the + * <code>writeByte()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>byte</code> value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput + */ +public final byte +readByte() throws EOFException, IOException +{ + int byte_read = read(); + + if (byte_read == -1) + throw new EOFException("Unexpected end of stream"); + + return(DataInputStream.convertToByte(byte_read)); +} + +/*************************************************************************/ + +/** + * This method reads 8 unsigned bits into a Java <code>int</code> value from the + * stream. The value returned is in the range of 0 to 255. + * <p> + * This method can read an unsigned byte written by an object implementing the + * <code>writeUnsignedByte()</code> method in the <code>DataOutput</code> interface. + * + * @return The unsigned bytes value read as a Java <code>int</code> + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput + */ +public final int +readUnsignedByte() throws EOFException, IOException +{ + int byte_read = read(); + + if (byte_read == -1) + throw new EOFException("Unexpected end of stream"); + + return(DataInputStream.convertToUnsignedByte(byte_read)); +} + +/*************************************************************************/ + +/** + * This method reads a Java <code>char</code> value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>char</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and code{byte2</code> represent the first + * and second byte read from the stream respectively, they will be + * transformed to a <code>char</code> in the following manner: + * <p> + * <code>(char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)</code> + * <p> + * This method can read a <code>char</code> written by an object implementing the + * <code>writeChar()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>char</code> value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput + */ +public final char +readChar() throws EOFException, IOException +{ + byte[] buf = new byte[2]; + + readFully(buf); + + return(DataInputStream.convertToChar(buf)); +} + +/*************************************************************************/ + +/** + * This method reads a signed 16-bit value into a Java in from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>short</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and code{byte2</code> represent the first + * and second byte read from the stream respectively, they will be + * transformed to a <code>short</code> in the following manner: + * <p> + * <code>(short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)</code> + * <p> + * The value returned is in the range of -32768 to 32767. + * <p> + * This method can read a <code>short</code> written by an object implementing the + * <code>writeShort()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>short</code> value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput + */ +public final short +readShort() throws EOFException, IOException +{ + byte[] buf = new byte[2]; + + readFully(buf); + + return(DataInputStream.convertToShort(buf)); +} + +/*************************************************************************/ + +/** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java <code>int</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and code{byte2</code> represent the first + * and second byte read from the stream respectively, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an unsigned short written by an object implementing + * the <code>writeUnsignedShort()</code> method in the <code>DataOutput</code> interface. + * + * @return The unsigned short value read as a Java <code>int</code> + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + */ +public final int +readUnsignedShort() throws EOFException, IOException +{ + byte[] buf = new byte[2]; + + readFully(buf); + + return(DataInputStream.convertToUnsignedShort(buf)); +} + +/*************************************************************************/ + +/** + * This method reads a Java <code>int</code> value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java <code>int</code> The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte4</code> represent the first + * four bytes read from the stream, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF) << 8) + (byte4 & 0xFF)))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an <code>int</code> written by an object implementing the + * <code>writeInt()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>int</code> value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput + */ +public final int +readInt() throws EOFException, IOException +{ + byte[] buf = new byte[4]; + + readFully(buf); + + return(DataInputStream.convertToInt(buf)); +} + +/*************************************************************************/ + +/** + * This method reads a Java long value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java <code>long</code> The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte8</code> represent the first + * eight bytes read from the stream, they will be + * transformed to an <code>long</code> in the following manner: + * <p> + * <code>(long)((((long)byte1 & 0xFF) << 56) + (((long)byte2 & 0xFF) << 48) + + * (((long)byte3 & 0xFF) << 40) + (((long)byte4 & 0xFF) << 32) + + * (((long)byte5 & 0xFF) << 24) + (((long)byte6 & 0xFF) << 16) + + * (((long)byte7 & 0xFF) << 8) + ((long)byte9 & 0xFF)))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an <code>long</code> written by an object implementing the + * <code>writeLong()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>long</code> value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput + */ +public final long +readLong() throws EOFException, IOException +{ + byte[] buf = new byte[8]; + + readFully(buf); + + return(DataInputStream.convertToLong(buf)); +} + +/*************************************************************************/ + +/** + * This method reads a Java float value from an input stream. It operates + * by first reading an <code>int</code> value from the stream by calling the + * <code>readInt()</code> method in this interface, then converts that <code>int</code> + * to a <code>float</code> using the <code>intBitsToFloat</code> method in + * the class <code>java.lang.Float</code> + * <p> + * This method can read a <code>float</code> written by an object implementing the + * <code>writeFloat()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>float</code> value read + * + * @exception EOFException If end of file is reached before reading the float + * @exception IOException If any other error occurs + * + * @see java.lang.Float + * @see DataOutput + */ +public final float +readFloat() throws EOFException, IOException +{ + int val = readInt(); + + return(Float.intBitsToFloat(val)); +} + +/*************************************************************************/ + +/** + * This method reads a Java double value from an input stream. It operates + * by first reading a <code>logn</code> value from the stream by calling the + * <code>readLong()</code> method in this interface, then converts that <code>long</code> + * to a <code>double</code> using the <code>longBitsToDouble</code> method in + * the class <code>java.lang.Double</code> + * <p> + * This method can read a <code>double</code> written by an object implementing the + * <code>writeDouble()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>double</code> value read + * + * @exception EOFException If end of file is reached before reading the double + * @exception IOException If any other error occurs + * + * @see java.lang.Double + * @see DataOutput + */ +public final double +readDouble() throws EOFException, IOException +{ + long val = readLong(); + + return(Double.longBitsToDouble(val)); +} + +/*************************************************************************/ + +/** + * This method reads the next line of text data from an input stream. + * It operates by reading bytes and converting those bytes to <code>char</code> + * values by treating the byte read as the low eight bits of the <code>char</code> + * and using <code>0</code> as the high eight bits. Because of this, it does + * not support the full 16-bit Unicode character set. + * <p> + * The reading of bytes ends when either the end of file or a line terminator + * is encountered. The bytes read are then returned as a <code>String</code> + * A line terminator is a byte sequence consisting of either + * <code>\r</code> <code>\n</code> or <code>\r\n</code> These termination charaters are + * discarded and are not returned as part of the string. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeLine()</code> method in <code>DataOutput</code> + * + * @return The line read as a <code>String</code> + * + * @exception IOException If an error occurs + * + * @see DataOutput + * + * @deprecated + */ +public final String +readLine() throws IOException +{ + StringBuffer sb = new StringBuffer(""); + + for (;;) + { + int byte_read = read(); + + if (byte_read == -1) + return(sb.toString()); + + char c = (char)byte_read; + + if (c == '\r') + { + byte_read = read(); + if (((char)byte_read) != '\n') + seek(getFilePointer() - 1); + + return(sb.toString()); + } + + if (c == '\n') + return(sb.toString()); + + sb.append(c); + } +} + +/*************************************************************************/ + +/** + * This method reads a <code>String</code> from an input stream that is encoded in + * a modified UTF-8 format. This format has a leading two byte sequence + * that contains the remaining number of bytes to read. This two byte + * sequence is read using the <code>readUnsignedShort()</code> method of this + * interface. + * <p> + * After the number of remaining bytes have been determined, these bytes + * are read an transformed into <code>char</code> values. These <code>char</code> values + * are encoded in the stream using either a one, two, or three byte format. + * The particular format in use can be determined by examining the first + * byte read. + * <p> + * If the first byte has a high order bit of 0 then + * that character consists on only one byte. This character value consists + * of seven bits that are at positions 0 through 6 of the byte. As an + * example, if <code>byte1</code> is the byte read from the stream, it would + * be converted to a <code>char</code> like so: + * <p> + * <code>(char)byte1</code> + * <p> + * If the first byte has <code>110</code> as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> are the first two bytes + * read respectively, and the high order bits of them match the patterns + * which indicate a two byte character encoding, then they would be + * converted to a Java <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F))</code> + * <p> + * If the first byte has a <code>1110</code> as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have <code>10</code> as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> <code>byte2</code> and <code>byte3</code> are the + * three bytes read, and the high order bits of them match the patterns + * which indicate a three byte character encoding, then they would be + * converted to a Java <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F))</code> + * <p> + * Note that all characters are encoded in the method that requires the + * fewest number of bytes with the exception of the character with the + * value of <code>\u0000</code> which is encoded as two bytes. This is a + * modification of the UTF standard used to prevent C language style + * <code>NUL</code> values from appearing in the byte stream. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeUTF()</code> method in <code>DataOutput</code> + * + * @return The <code>String</code> read + * + * @exception EOFException If end of file is reached before reading the String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput + */ +public final String +readUTF() throws EOFException, UTFDataFormatException, IOException +{ + StringBuffer sb = new StringBuffer(""); + + int num_bytes = readUnsignedShort(); + byte[] buf = new byte[num_bytes]; + readFully(buf); + + return(DataInputStream.convertFromUTF(buf)); +} + +/*************************************************************************/ + +/** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer + * + * @param buf The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the buffer + * @exception IOException If any other error occurs + */ +public final void +readFully(byte[] buf) throws EOFException, IOException +{ + readFully(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * This method reads raw bytes into the passed array <code>buf</code> starting + * <code>offset</code> bytes into the buffer. The number of bytes read will be + * exactly <code>len</code> Note that this method blocks until the data is + * available and * throws an exception if there is not enough data left in + * the stream to read <code>len</code> bytes. + * + * @param buf The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param len The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling the buffer + * @exception IOException If any other error occurs + */ +public final void +readFully(byte[] buf, int offset, int len) throws EOFException, IOException +{ + int total_read = 0; + + while (total_read < len) + { + int bytes_read = read(buf, offset + total_read, len - total_read); + if (bytes_read == -1) + throw new EOFException("Unexpected end of stream"); + + total_read += bytes_read; + } +} + +/*************************************************************************/ + +/** + * This method attempts to skip and discard the specified number of bytes + * in the input stream. It may actually skip fewer bytes than requested. + * The actual number of bytes skipped is returned. This method will not + * skip any bytes if passed a negative number of bytes to skip. + * + * @param num_bytes The requested number of bytes to skip. + * + * @return The number of bytes actually skipped. + * + * @exception IOException If an error occurs. + */ +public int +skipBytes(int n) throws EOFException, IOException +{ + if (n <= 0) + return(0); + + long total_skipped = fd.skip(n); + + return((int)n); +} + +/*************************************************************************/ + +/** + * This method writes a single byte of data to the file. The file must + * be open for read-write in order for this operation to succeed. + * + * @param The byte of data to write, passed as an int. + * + * @exception IOException If an error occurs + */ +public void +write(int b) throws IOException +{ + if (read_only) + throw new IOException("File is open read only"); + + byte[] buf = new byte[1]; + buf[0] = (byte)b; + + fd.write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * This method writes all the bytes in the specified array to the file. + * The file must be open read-write in order for this operation to succeed. + * + * @param buf The array of bytes to write to the file + */ +public void +write(byte[] buf) throws IOException +{ + if (read_only) + throw new IOException("File is open read only"); + + fd.write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * This method writes <code>len</code> bytes to the file from the specified + * array starting at index <code>offset</code> into the array. + * + * @param buf The array of bytes to write to the file + * @param offset The index into the array to start writing file + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ +public void +write(byte[] buf, int offset, int len) throws IOException +{ + if (read_only) + throw new IOException("File is open read only"); + + fd.write(buf, offset, len); +} + +/*************************************************************************/ + +/** + * This method writes a Java <code>boolean</code> to the underlying output + * stream. For a value of <code>true</code>, 1 is written to the stream. + * For a value of <code>false</code>, 0 is written. + * + * @param b The <code>boolean</code> value to write to the stream + * + * @exception IOException If an error occurs + */ +public final void +writeBoolean(boolean b) throws IOException +{ + int bool = DataOutputStream.convertFromBoolean(b); + write(bool); +} + +/*************************************************************************/ + +/** + * This method writes a Java <code>byte</code> value to the underlying + * output stream. + * + * @param b The <code>byte</code> to write to the stream, passed as an <code>int</code>. + * + * @exception IOException If an error occurs + */ +public final void +writeByte(int b) throws IOException +{ + write(b); +} + +/*************************************************************************/ + +/** + * This method writes all the bytes in a <code>String</code> out to the + * stream. One byte is written for each character in the <code>String</code>. + * The high eight bits of each character are discarded. + * + * @param s The <code>String</code> to write to the stream + * + * @exception IOException If an error occurs + */ +public final void +writeBytes(String s) throws IOException +{ + if (s.length() == 0) + return; + + byte[] buf = new byte[s.length()]; + + for (int i = 0; i < s.length(); i++) + buf[i] = (byte)(s.charAt(i) & 0xFF); + + write(buf); +} + +/*************************************************************************/ + +/** + * This method writes a single <code>char</code> value to the stream, + * high byte first. + * + * @param c The <code>char</code> value to write, passed as an <code>int</code>. + * + * @exception IOException If an error occurs + */ +public final void +writeChar(int c) throws IOException +{ + write(DataOutputStream.convertFromChar(c)); +} + +/*************************************************************************/ + +/** + * This method writes all the characters in a <code>String</code> to the + * stream. There will be two bytes for each character value. The high + * byte of the character will be written first. + * + * @param s The <code>String</code> to write to the stream. + * + * @exception IOException If an error occurs + */ +public final void +writeChars(String s) throws IOException +{ + if (s.length() == 0) + return; + + byte[] buf = DataOutputStream.getConvertedStringChars(s); + write(buf); +} + +/*************************************************************************/ + +/** + * This method writes a Java <code>short</code> to the stream, high byte + * first. This method requires two bytes to encode the value. + * + * @param s The <code>short</code> value to write to the stream, passed as an <code>int</code>. + * + * @exception IOException If an error occurs + */ +public final void +writeShort(int s) throws IOException +{ + write(DataOutputStream.convertFromShort(s)); +} + +/*************************************************************************/ + +/** + * This method writes a Java <code>int</code> to the stream, high bytes + * first. This method requires four bytes to encode the value. + * + * @param i The <code>int</code> value to write to the stream. + * + * @exception IOException If an error occurs + */ +public final void +writeInt(int i) throws IOException +{ + write(DataOutputStream.convertFromInt(i)); +} + +/*************************************************************************/ + +/** + * This method writes a Java <code>long</code> to the stream, high bytes + * first. This method requires eight bytes to encode the value. + * + * @param l The <code>long</code> value to write to the stream. + * + * @exception IOException If an error occurs + */ +public final void +writeLong(long l) throws IOException +{ + write(DataOutputStream.convertFromLong(l)); +} + +/*************************************************************************/ + +/** + * This method writes a Java <code>float</code> value to the stream. This + * value is written by first calling the method <code>Float.floatToIntBits</code> + * to retrieve an <code>int</code> representing the floating point number, + * then writing this <code>int</code> value to the stream exactly the same + * as the <code>writeInt()</code> method does. + * + * @param f The floating point number to write to the stream. + * + * @exception IOException If an error occurs + * + * @see writeInt + */ +public final void +writeFloat(float f) throws IOException +{ + int i = Float.floatToIntBits(f); + writeInt(i); +} + +/*************************************************************************/ + +/** + * This method writes a Java <code>double</code> value to the stream. This + * value is written by first calling the method <code>Double.doubleToLongBits</code> + * to retrieve an <code>long</code> representing the floating point number, + * then writing this <code>long</code> value to the stream exactly the same + * as the <code>writeLong()</code> method does. + * + * @param d The double precision floating point number to write to the stream. + * + * @exception IOException If an error occurs + * + * @see writeLong + */ +public final void +writeDouble(double d) throws IOException +{ + long l = Double.doubleToLongBits(d); + writeLong(l); +} + +/*************************************************************************/ + +/** + * This method writes a Java <code>String</code> to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. Note that this is the number of bytes in the + * encoded <code>String</code> not the <code>String</code> length. Next + * come the encoded characters. Each character in the <code>String</code> + * is encoded as either one, two or three bytes. For characters in the + * range of <code>\u0001</code> to <code>\u007F</code>, one byte is used. The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of <code>\u0080</code> to <code>\u007FF</code>, two bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * <code>\u0000</code>. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * @param s The <code>String</code> to write to the output in UTF format + * + * @exception IOException If an error occurs + */ +public final void +writeUTF(String s) throws IOException +{ + byte[] buf = DataOutputStream.convertToUTF(s); + + writeShort(buf.length); + write(buf); +} + +/*************************************************************************/ + +/** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + +private FileChannel ch; /* cached associated file-channel */ + +public FileChannel +getChannel() +{ + synchronized (this) + { +// if (ch == null) +// ch = new gnu.java.nio.FileChannelImpl(native_fd, +// this); + } + return ch; +} + +/*************************************************************************/ + +/** + * This method ensures that the underlying file is closed when this object + * is garbage collected. Since there is a system dependent limit on how + * many files a single process can have open, it is a good idea to close + * streams when they are no longer needed rather than waiting for garbage + * collectio to automatically close them. + * + * @exception IOException If an error occurs + */ +protected void finalize() throws IOException +{ + if(fd != null) + { + close(); + } +} + + +} // class RandomAccessFile + diff --git a/classpath/java/lang/Thread.java b/classpath/java/lang/Thread.java new file mode 100644 index 00000000..b52e7995 --- /dev/null +++ b/classpath/java/lang/Thread.java @@ -0,0 +1,1005 @@ +/* Thread -- an independent thread of executable code + Copyright (C) 1998, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +/** + * Thread represents a single thread of execution in the VM. When an + * application VM starts up, it creates a non-daemon Thread which calls the + * main() method of a particular class. There may be other Threads running, + * such as the garbage collection thread. + * + * <p>Threads have names to identify them. These names are not necessarily + * unique. Every Thread has a priority, as well, which tells the VM which + * Threads should get more running time. New threads inherit the priority + * and daemon status of the parent thread, by default. + * + * <p>There are two methods of creating a Thread: you may subclass Thread and + * implement the <code>run()</code> method, at which point you may start the + * Thread by calling its <code>start()</code> method, or you may implement + * <code>Runnable</code> in the class you want to use and then call new + * <code>Thread(your_obj).start()</code>. + * + * <p>The virtual machine runs until all non-daemon threads have died (either + * by returning from the run() method as invoked by start(), or by throwing + * an uncaught exception); or until <code>System.exit</code> is called with + * adequate permissions. + * + * <p>It is unclear at what point a Thread should be added to a ThreadGroup, + * and at what point it should be removed. Should it be inserted when it + * starts, or when it is created? Should it be removed when it is suspended + * or interrupted? The only thing that is clear is that the Thread should be + * removed when it is stopped. + * + * @author John Keiser + * @author Eric Blake <ebb9@email.byu.edu> + * @see Runnable + * @see Runtime#exit(int) + * @see #run() + * @see #start() + * @see ThreadLocal + * @since 1.0 + * @status updated to 1.4 + */ +public class Thread implements Runnable +{ + /** The minimum priority for a Thread. */ + public static final int MIN_PRIORITY = 1; + + /** The priority a Thread gets by default. */ + public static final int NORM_PRIORITY = 5; + + /** The maximum priority for a Thread. */ + public static final int MAX_PRIORITY = 10; + + // note that nativeThread is only set for threads that have actually been started! + private system.threading.Thread nativeThread; + private static system.LocalDataStoreSlot localDataStoreSlot; + + /** + * The group this thread belongs to. This is set to null by + * ThreadGroup.removeThread when the thread dies. + */ + ThreadGroup group; + + /** The object to run(), null if this is the target. */ + final Runnable toRun; + + /** The thread name, non-null. */ + String name; + + /** Whether the thread is a daemon. */ + boolean daemon; + + /** The thread priority, 1 to 10. */ + int priority; + + /** The context classloader for this Thread. */ + private ClassLoader contextClassLoader = ClassLoader.getSystemClassLoader(); + + /** The next thread number to use. */ + private static int numAnonymousThreadsCreated = 0; + + private Thread(ThreadGroup group, system.threading.Thread nativeThread) + { + this.group = group; + this.nativeThread = nativeThread; + this.toRun = null; + this.name = nativeThread.get_Name(); + switch(nativeThread.get_Priority()) + { + case system.threading.ThreadPriority.Lowest: + priority = MIN_PRIORITY; + break; + case system.threading.ThreadPriority.BelowNormal: + priority = 3; + break; + case system.threading.ThreadPriority.Normal: + priority = NORM_PRIORITY; + break; + case system.threading.ThreadPriority.AboveNormal: + priority = 7; + break; + case system.threading.ThreadPriority.Highest: + priority = MAX_PRIORITY; + break; + } + } + + /** + * Allocate a new Thread object, as if by + * <code>Thread(null, null, <i>fake name</i>)</code>, where the fake name + * is "Thread-" + <i>unique integer</i>. + * + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread() + { + this(null, (Runnable) null); + } + + /** + * Allocate a new Thread object, as if by + * <code>Thread(null, toRun, <i>fake name</i>)</code>, where the fake name + * is "Thread-" + <i>unique integer</i>. + * + * @param toRun the Runnable object to execute + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(Runnable toRun) + { + this(null, toRun); + } + + /** + * Allocate a new Thread object, as if by + * <code>Thread(group, toRun, <i>fake name</i>)</code>, where the fake name + * is "Thread-" + <i>unique integer</i>. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @throws SecurityException if this thread cannot access <code>group</code> + * @throws IllegalThreadStateException if group is destroyed + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(ThreadGroup group, Runnable toRun) + { + this(group, toRun, "Thread-" + ++numAnonymousThreadsCreated, 0); + } + + /** + * Allocate a new Thread object, as if by + * <code>Thread(null, null, name)</code>. + * + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(String name) + { + this(null, null, name, 0); + } + + /** + * Allocate a new Thread object, as if by + * <code>Thread(group, null, name)</code>. + * + * @param group the group to put the Thread into + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access <code>group</code> + * @throws IllegalThreadStateException if group is destroyed + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(ThreadGroup group, String name) + { + this(group, null, name, 0); + } + + /** + * Allocate a new Thread object, as if by + * <code>Thread(group, null, name)</code>. + * + * @param toRun the Runnable object to execute + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(Runnable toRun, String name) + { + this(null, toRun, name, 0); + } + + /** + * Allocate a new Thread object, with the specified ThreadGroup and name, and + * using the specified Runnable object's <code>run()</code> method to + * execute. If the Runnable object is null, <code>this</code> (which is + * a Runnable) is used instead. + * + * <p>If the ThreadGroup is null, the security manager is checked. If a + * manager exists and returns a non-null object for + * <code>getThreadGroup</code>, that group is used; otherwise the group + * of the creating thread is used. Note that the security manager calls + * <code>checkAccess</code> if the ThreadGroup is not null. + * + * <p>The new Thread will inherit its creator's priority and daemon status. + * These can be changed with <code>setPriority</code> and + * <code>setDaemon</code>. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access <code>group</code> + * @throws IllegalThreadStateException if group is destroyed + * @see Runnable#run() + * @see #run() + * @see #setDaemon(boolean) + * @see #setPriority(int) + * @see SecurityManager#checkAccess(ThreadGroup) + * @see ThreadGroup#checkAccess() + */ + public Thread(ThreadGroup group, Runnable toRun, String name) + { + this(group, toRun, name, 0); + } + + /** + * Allocate a new Thread object, as if by + * <code>Thread(group, null, name)</code>, and give it the specified stack + * size, in bytes. The stack size is <b>highly platform independent</b>, + * and the virtual machine is free to round up or down, or ignore it + * completely. A higher value might let you go longer before a + * <code>StackOverflowError</code>, while a lower value might let you go + * longer before an <code>OutOfMemoryError</code>. Or, it may do absolutely + * nothing! So be careful, and expect to need to tune this value if your + * virtual machine even supports it. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @param name the name for the Thread + * @param size the stack size, in bytes; 0 to be ignored + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access <code>group</code> + * @throws IllegalThreadStateException if group is destroyed + * @since 1.4 + */ + public Thread(ThreadGroup group, Runnable toRun, String name, long size) + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = Runtime.securityManager; + if (group == null) + { + if (sm != null) + group = sm.getThreadGroup(); + if (group == null) + group = currentThread().group; + } + else if (sm != null) + sm.checkAccess(group); + this.group = group; + + // Use toString hack to detect null. + this.name = name.toString(); + this.toRun = toRun; + Thread current = currentThread(); + priority = current.priority; + daemon = current.daemon; + + group.addThread(this); + InheritableThreadLocal.newChildThread(this); + } + + /** + * Get the currently executing Thread. + * + * @return the currently executing Thread + */ + public static Thread currentThread() + { + if(localDataStoreSlot == null) + { + localDataStoreSlot = system.threading.Thread.AllocateDataSlot(); + } + system.threading.Thread thread = system.threading.Thread.get_CurrentThread(); + Thread javaThread = (Thread)system.threading.Thread.GetData(localDataStoreSlot); + if(javaThread == null) + { + // threads created outside of Java always run in the root thread group + javaThread = new Thread(ThreadGroup.root, thread); + system.threading.Thread.SetData(localDataStoreSlot, javaThread); + } + return javaThread; + } + + /** + * Yield to another thread. The Thread will not lose any locks it holds + * during this time. There are no guarantees which thread will be + * next to run, and it could even be this one, but most VMs will choose + * the highest priority threat that has been waiting longest. + */ + public static void yield() + { + system.threading.Thread.Sleep(0); + } + + /** + * Suspend the current Thread's execution for the specified amount of + * time. The Thread will not lose any locks it has during this time. There + * are no guarantees which thread will be next to run, but most VMs will + * choose the highest priority threat that has been waiting longest. + * + * @param ms the number of milliseconds to sleep, or 0 for forever + * @throws InterruptedException if the Thread is interrupted; it's + * <i>interrupted status</i> will be cleared + * @see #notify() + * @see #wait(long) + */ + public static void sleep(long ms) throws InterruptedException + { + sleep(ms, 0); + } + + /** + * Suspend the current Thread's execution for the specified amount of + * time. The Thread will not lose any locks it has during this time. There + * are no guarantees which thread will be next to run, but most VMs will + * choose the highest priority threat that has been waiting longest. + * + * <p>Note that 1,000,000 nanoseconds == 1 millisecond, but most VMs do + * not offer that fine a grain of timing resolution. Besides, there is + * no guarantee that this thread can start up immediately when time expires, + * because some other thread may be active. So don't expect real-time + * performance. + * + * @param ms the number of milliseconds to sleep, or 0 for forever + * @param ns the number of extra nanoseconds to sleep (0-999999) + * @throws InterruptedException if the Thread is interrupted; it's + * <i>interrupted status</i> will be cleared + * @throws IllegalArgumentException if ns is invalid + * @see #notify() + * @see #wait(long, int) + */ + public static native void sleep(long ms, int ns) throws InterruptedException; + + /** + * Start this Thread, calling the run() method of the Runnable this Thread + * was created with, or else the run() method of the Thread itself. This + * is the only way to start a new thread; calling run by yourself will just + * stay in the same thread. The virtual machine will remove the thread from + * its thread group when the run() method completes. + * + * @throws IllegalThreadStateException if the thread has already started + * @see #run() + */ + public synchronized void start() throws IllegalThreadStateException + { + if(nativeThread != null) + { + throw new IllegalThreadStateException(); + } + if(localDataStoreSlot == null) + { + localDataStoreSlot = system.threading.Thread.AllocateDataSlot(); + } + system.threading.ThreadStart starter = new system.threading.ThreadStart( + new system.threading.ThreadStart.Method() + { + public void Invoke() + { + try + { + system.threading.Thread.SetData(localDataStoreSlot, Thread.this); + try + { + run(); + } + catch(Throwable t) + { + if(group != null) + { + group.uncaughtException(Thread.this, t); + } + } + } + finally + { + if(group != null) + { + group.removeThread(Thread.this); + // NOTE shouldn't we set group to null here? + } + } + } + }); + nativeThread = new system.threading.Thread(starter); + nativeThread.set_Name(name); + nativeThread.set_IsBackground(daemon); + setPriorityNative(); + nativeThread.Start(); + } + + /** + * The method of Thread that will be run if there is no Runnable object + * associated with the Thread. Thread's implementation does nothing at all. + * + * @see #start() + * @see #Thread(ThreadGroup, Runnable, String) + */ + public void run() + { + if (toRun != null) + toRun.run(); + } + + /** + * Cause this Thread to stop abnormally because of the throw of a ThreadDeath + * error. If you stop a Thread that has not yet started, it will stop + * immediately when it is actually started. + * + * <p>This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * <code>checkAccess(this)</code>, plus another one if the current thread + * is not this: <code>RuntimePermission("stopThread")</code>. If you must + * catch a ThreadDeath, be sure to rethrow it after you have cleaned up. + * ThreadDeath is the only exception which does not print a stack trace when + * the thread dies. + * + * @throws SecurityException if you cannot stop the Thread + * @see #interrupt() + * @see #checkAccess() + * @see #start() + * @see ThreadDeath + * @see ThreadGroup#uncaughtException(Thread, Throwable) + * @see SecurityManager#checkAccess(Thread) + * @see SecurityManager#checkPermission(Permission) + * @deprecated unsafe operation, try not to use + */ + public final void stop() + { + stop(new ThreadDeath()); + } + + /** + * Cause this Thread to stop abnormally and throw the specified exception. + * If you stop a Thread that has not yet started, it will stop immediately + * when it is actually started. <b>WARNING</b>This bypasses Java security, + * and can throw a checked exception which the call stack is unprepared to + * handle. Do not abuse this power. + * + * <p>This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * <code>checkAccess(this)</code>, plus another one if the current thread + * is not this: <code>RuntimePermission("stopThread")</code>. If you must + * catch a ThreadDeath, be sure to rethrow it after you have cleaned up. + * ThreadDeath is the only exception which does not print a stack trace when + * the thread dies. + * + * @param t the Throwable to throw when the Thread dies + * @throws SecurityException if you cannot stop the Thread + * @throws NullPointerException in the calling thread, if t is null + * @see #interrupt() + * @see #checkAccess() + * @see #start() + * @see ThreadDeath + * @see ThreadGroup#uncaughtException(Thread, Throwable) + * @see SecurityManager#checkAccess(Thread) + * @see SecurityManager#checkPermission(Permission) + * @deprecated unsafe operation, try not to use + */ + public final synchronized void stop(Throwable t) + { + if (t == null) + throw new NullPointerException(); + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = Runtime.securityManager; + if (sm != null) + { + sm.checkAccess(this); + if (this != currentThread()) + sm.checkPermission(new RuntimePermission("stopThread")); + } + group.removeThread(this); + // TODO what happens if the thread hasn't been started yet? + nativeThread.Abort(t); + } + + /** + * Interrupt this Thread. First, there is a security check, + * <code>checkAccess</code>. Then, depending on the current state of the + * thread, various actions take place: + * + * <p>If the thread is waiting because of {@link #wait()}, + * {@link #sleep(long)}, or {@link #join()}, its <i>interrupt status</i> + * will be cleared, and an InterruptedException will be thrown. Notice that + * this case is only possible if an external thread called interrupt(). + * + * <p>If the thread is blocked in an interruptible I/O operation, in + * {@link java.nio.channels.InterruptibleChannel}, the <i>interrupt + * status</i> will be set, and ClosedByInterruptException will be thrown. + * + * <p>If the thread is blocked on a {@link java.nio.channels.Selector}, the + * <i>interrupt status</i> will be set, and the selection will return, with + * a possible non-zero value, as though by the wakeup() method. + * + * <p>Otherwise, the interrupt status will be set. + * + * @throws SecurityException if you cannot modify this Thread + */ + public synchronized void interrupt() + { + checkAccess(); + // TODO what happens if the thread hasn't been started yet? + // TODO figure out how interrupt really works + nativeThread.Interrupt(); + } + + /** + * Determine whether the current Thread has been interrupted, and clear + * the <i>interrupted status</i> in the process. + * + * @return whether the current Thread has been interrupted + * @see #isInterrupted() + */ + public static boolean interrupted() + { + try + { + synchronized(currentThread()) + { + if(false) throw new InterruptedException(); + system.threading.Thread.Sleep(0); + } + return false; + } + catch(InterruptedException x) + { + return true; + } + } + + /** + * Determine whether the given Thread has been interrupted, but leave + * the <i>interrupted status</i> alone in the process. + * + * @return whether the current Thread has been interrupted + * @see #interrupted() + */ + public boolean isInterrupted() + { + // NOTE special case for current thread, because then we can use the .NET interrupted status + if(this == currentThread()) + { + try + { + if(false) throw new InterruptedException(); + system.threading.Thread.Sleep(0); + return false; + } + catch(InterruptedException x) + { + // because we "consumed" the interrupt, we need to interrupt ourself again + nativeThread.Interrupt(); + return true; + } + } + // HACK since quering the interrupted state of another thread is inherently racy, I hope + // we can get away with always returning false, because I have no idea how to obtain this + // information from the .NET runtime + return false; + } + + /** + * Originally intended to destroy this thread, this method was never + * implemented by Sun, and is hence a no-op. + */ + public void destroy() + { + } + + /** + * Determine whether this Thread is alive. A thread which is alive has + * started and not yet died. + * + * @return whether this Thread is alive + */ + public final boolean isAlive() + { + system.threading.Thread t = nativeThread; + return t != null && t.get_IsAlive(); + } + + /** + * Suspend this Thread. It will not come back, ever, unless it is resumed. + * + * <p>This is inherently unsafe, as the suspended thread still holds locks, + * and can potentially deadlock your program. Hence, there is a security + * check: <code>checkAccess</code>. + * + * @throws SecurityException if you cannot suspend the Thread + * @see #checkAccess() + * @see #resume() + * @deprecated unsafe operation, try not to use + */ + public final synchronized void suspend() + { + checkAccess(); + // TODO what happens if the thread hasn't been started yet? + // TODO handle errors + nativeThread.Suspend(); + } + + /** + * Resume this Thread. If the thread is not suspended, this method does + * nothing. To mirror suspend(), there may be a security check: + * <code>checkAccess</code>. + * + * @throws SecurityException if you cannot resume the Thread + * @see #checkAccess() + * @see #suspend() + * @deprecated pointless, since suspend is deprecated + */ + public final synchronized void resume() + { + checkAccess(); + // TODO what happens if the thread hasn't been started yet? + // TODO handle errors + nativeThread.Resume(); + } + + /** + * Set this Thread's priority. There may be a security check, + * <code>checkAccess</code>, then the priority is set to the smaller of + * priority and the ThreadGroup maximum priority. + * + * @param priority the new priority for this Thread + * @throws IllegalArgumentException if priority exceeds MIN_PRIORITY or + * MAX_PRIORITY + * @throws SecurityException if you cannot modify this Thread + * @see #getPriority() + * @see #checkAccess() + * @see ThreadGroup#getMaxPriority() + * @see #MIN_PRIORITY + * @see #MAX_PRIORITY + */ + public final void setPriority(int priority) + { + checkAccess(); + if (priority < MIN_PRIORITY || priority > MAX_PRIORITY) + { + throw new IllegalArgumentException("Invalid thread priority value " + priority + "."); + } + this.priority = Math.min(priority, group.getMaxPriority()); + if(nativeThread != null) + { + setPriorityNative(); + } + } + + private void setPriorityNative() + { + if(priority == MIN_PRIORITY) + { + nativeThread.set_Priority(system.threading.ThreadPriority.Lowest); + } + else if(priority > MIN_PRIORITY && priority < NORM_PRIORITY) + { + nativeThread.set_Priority(system.threading.ThreadPriority.BelowNormal); + } + else if(priority == NORM_PRIORITY) + { + nativeThread.set_Priority(system.threading.ThreadPriority.Normal); + } + else if(priority > NORM_PRIORITY && priority < MAX_PRIORITY) + { + nativeThread.set_Priority(system.threading.ThreadPriority.AboveNormal); + } + else if(priority == MAX_PRIORITY) + { + nativeThread.set_Priority(system.threading.ThreadPriority.Highest); + } + } + + /** + * Get this Thread's priority. + * + * @return the Thread's priority + */ + public final int getPriority() + { + return priority; + } + + /** + * Set this Thread's name. There may be a security check, + * <code>checkAccess</code>. + * + * @param name the new name for this Thread + * @throws NullPointerException if name is null + * @throws SecurityException if you cannot modify this Thread + */ + public final void setName(String name) + { + checkAccess(); + // Use toString hack to detect null. + this.name = name.toString(); + } + + /** + * Get this Thread's name. + * + * @return this Thread's name + */ + public final String getName() + { + return name; + } + + /** + * Get the ThreadGroup this Thread belongs to. If the thread has died, this + * returns null. + * + * @return this Thread's ThreadGroup + */ + public final ThreadGroup getThreadGroup() + { + return group; + } + + /** + * Get the number of active threads in the current Thread's ThreadGroup. + * This implementation calls + * <code>currentThread().getThreadGroup().activeCount()</code>. + * + * @return the number of active threads in the current ThreadGroup + * @see ThreadGroup#activeCount() + */ + public static int activeCount() + { + return currentThread().group.activeCount(); + } + + /** + * Copy every active thread in the current Thread's ThreadGroup into the + * array. Extra threads are silently ignored. This implementation calls + * <code>getThreadGroup().enumerate(array)</code>, which may have a + * security check, <code>checkAccess(group)</code>. + * + * @param array the array to place the Threads into + * @return the number of Threads placed into the array + * @throws NullPointerException if array is null + * @throws SecurityException if you cannot access the ThreadGroup + * @see ThreadGroup#enumerate(Thread[]) + * @see #activeCount() + * @see SecurityManager#checkAccess(ThreadGroup) + */ + public static int enumerate(Thread[] array) + { + return currentThread().group.enumerate(array); + } + + /** + * Count the number of stack frames in this Thread. The Thread in question + * must be suspended when this occurs. + * + * @return the number of stack frames in this Thread + * @throws IllegalThreadStateException if this Thread is not suspended + * @deprecated pointless, since suspend is deprecated + */ + public int countStackFrames() + { + // Sun's 1.4 JVM returns 0, so we can do the same + return 0; + } + + /** + * Wait the specified amount of time for the Thread in question to die. + * + * @param ms the number of milliseconds to wait, or 0 for forever + * @throws InterruptedException if the Thread is interrupted; it's + * <i>interrupted status</i> will be cleared + */ + public final void join(long ms) throws InterruptedException + { + join(ms, 0); + } + + /** + * Wait the specified amount of time for the Thread in question to die. + * + * <p>Note that 1,000,000 nanoseconds == 1 millisecond, but most VMs do + * not offer that fine a grain of timing resolution. Besides, there is + * no guarantee that this thread can start up immediately when time expires, + * because some other thread may be active. So don't expect real-time + * performance. + * + * @param ms the number of milliseconds to wait, or 0 for forever + * @param ns the number of extra nanoseconds to sleep (0-999999) + * @throws InterruptedException if the Thread is interrupted; it's + * <i>interrupted status</i> will be cleared + * @throws IllegalArgumentException if ns is invalid + * @XXX A ThreadListener would be nice, to make this efficient. + */ + public final void join(long ms, int ns) throws InterruptedException + { + joinInternal(nativeThread, ms, ns); + } + private static native void joinInternal(system.threading.Thread nativeThread, long ms, int ns) throws InterruptedException; + + /** + * Wait forever for the Thread in question to die. + * + * @throws InterruptedException if the Thread is interrupted; it's + * <i>interrupted status</i> will be cleared + */ + public final void join() throws InterruptedException + { + join(0, 0); + } + + /** + * Print a stack trace of the current thread to stderr using the same + * format as Throwable's printStackTrace() method. + * + * @see Throwable#printStackTrace() + */ + public static void dumpStack() + { + new Throwable().printStackTrace(); + } + + /** + * Set the daemon status of this Thread. If this is a daemon Thread, then + * the VM may exit even if it is still running. This may only be called + * before the Thread starts running. There may be a security check, + * <code>checkAccess</code>. + * + * @param daemon whether this should be a daemon thread or not + * @throws SecurityException if you cannot modify this Thread + * @throws IllegalThreadStateException if the Thread is active + * @see #isDaemon() + * @see #checkAccess() + */ + public final synchronized void setDaemon(boolean daemon) + { + if (isAlive() || group == null) + throw new IllegalThreadStateException(); + checkAccess(); + this.daemon = daemon; + } + + /** + * Tell whether this is a daemon Thread or not. + * + * @return whether this is a daemon Thread or not + * @see #setDaemon(boolean) + */ + public final boolean isDaemon() + { + return daemon; + } + + /** + * Check whether the current Thread is allowed to modify this Thread. This + * passes the check on to <code>SecurityManager.checkAccess(this)</code>. + * + * @throws SecurityException if the current Thread cannot modify this Thread + * @see SecurityManager#checkAccess(Thread) + */ + public final void checkAccess() + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = Runtime.securityManager; + if (sm != null) + sm.checkAccess(this); + } + + /** + * Return a human-readable String representing this Thread. The format of + * the string is:<br> + * <code>"Thread[" + getName() + ',' + getPriority() + ',' + * + (getThreadGroup() == null ? "" : getThreadGroup().getName()) + + ']'</code>. + * + * @return a human-readable String representing this Thread + */ + public String toString() + { + return "Thread[" + name + ',' + priority + ',' + + (group == null ? "" : group.name) + ']'; + } + + /** + * Returns the context classloader of this Thread. The context + * classloader can be used by code that want to load classes depending + * on the current thread. Normally classes are loaded depending on + * the classloader of the current class. There may be a security check + * for <code>RuntimePermission("getClassLoader")</code> if the caller's + * class loader is not null or an ancestor of this thread's context class + * loader. + * + * @return the context class loader + * @throws SecurityException when permission is denied + * @see setContextClassLoader(ClassLoader) + * @since 1.2 + */ + public ClassLoader getContextClassLoader() + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = Runtime.securityManager; + if (sm != null) + // XXX Don't check this if the caller's class loader is an ancestor. + sm.checkPermission(new RuntimePermission("getClassLoader")); + return contextClassLoader; + } + + /** + * Sets the context classloader for this Thread. When not explicitly set, + * the context classloader for a thread is the same as the context + * classloader of the thread that created this thread. The first thread has + * as context classloader the system classloader. There may be a security + * check for <code>RuntimePermission("setContextClassLoader")</code>. + * + * @param classloader the new context class loader + * @throws SecurityException when permission is denied + * @see getContextClassLoader() + * @since 1.2 + */ + public void setContextClassLoader(ClassLoader classloader) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + this.contextClassLoader = classloader; + } + + /** + * Checks whether the current thread holds the monitor on a given object. + * This allows you to do <code>assert Thread.holdsLock(obj)</code>. + * + * @param obj the object to check + * @return true if the current thread is currently synchronized on obj + * @throws NullPointerException if obj is null + * @since 1.4 + */ + public static boolean holdsLock(Object obj) + { + if(obj == null) + { + throw new NullPointerException(); + } + try + { + // HACK this is a lame way of doing this, but I can't see any other way + // NOTE Wait causes the lock to be released temporarily, which isn't what we want + if(false) throw new IllegalMonitorStateException(); + if(false) throw new InterruptedException(); + system.threading.Monitor.Wait(obj, 0); + return true; + } + catch(IllegalMonitorStateException x) + { + return false; + } + catch(InterruptedException x1) + { + // Since we "consumed" the interrupt, we have to interrupt ourself again + Thread.currentThread().nativeThread.Interrupt(); + return true; + } + } +} // class Thread diff --git a/classpath/java/lang/VMClassLoader.java b/classpath/java/lang/VMClassLoader.java new file mode 100644 index 00000000..6c4506db --- /dev/null +++ b/classpath/java/lang/VMClassLoader.java @@ -0,0 +1,271 @@ +/* VMClassLoader.java -- Reference implementation of native interface + required by ClassLoader + Copyright (C) 1998, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.security.ProtectionDomain; +import java.net.URL; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Map; +import java.util.HashMap; +import system.*; +import system.reflection.*; +/** + * java.lang.VMClassLoader is a package-private helper for VMs to implement + * on behalf of java.lang.ClassLoader. + * + * @author John Keiser + * @author Mark Wielaard <mark@klomp.org> + * @author Eric Blake <ebb9@email.byu.edu> + */ +final class VMClassLoader +{ + /** + * Helper to define a class using a string of bytes. This assumes that + * the security checks have already been performed, if necessary. + * <strong>This method will be removed in a future version of GNU + * Classpath</strong>. + * + * @param name the name to give the class, or null if unknown + * @param data the data representing the classfile, in classfile format + * @param offset the offset into the data where the classfile starts + * @param len the length of the classfile data in the array + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + * @deprecated Implement + * {@link #defineClass(ClassLoader, String, byte[], int, int, ProtectionDomain)} + * instead. + */ + static final Class defineClass(ClassLoader cl, String name, byte[] data, int offset, int len) throws ClassFormatError + { + return defineClass(cl, name, data, offset, len, null); + } + + /** + * Helper to define a class using a string of bytes. This assumes that + * the security checks have already been performed, if necessary. + * + * <strong>For backward compatibility, this just ignores the protection + * domain; that is the wrong behavior, and you should directly implement + * this method natively if you can.</strong> + * + * @param name the name to give the class, or null if unknown + * @param data the data representing the classfile, in classfile format + * @param offset the offset into the data where the classfile starts + * @param len the length of the classfile data in the array + * @param pd the protection domain + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + */ + static final native Class defineClass(ClassLoader cl, String name, byte[] data, int offset, int len, ProtectionDomain pd) + throws ClassFormatError; + + /** + * Helper to resolve all references to other classes from this class. + * + * @param c the class to resolve + */ + static final void resolveClass(Class c) + { + // TODO + } + + /** + * Helper to load a class from the bootstrap class loader. + * + * @param name the class name to load + * @param resolve whether to resolve it + * @return the class, loaded by the bootstrap classloader + */ + static Class loadClass(String name, boolean resolve) throws ClassNotFoundException + { + Class c = Class.loadBootstrapClass(name, false); + if(c == null) + { + throw new ClassNotFoundException(name); + } + return c; + } + + /** + * Helper to load a resource from the bootstrap class loader. + * + * XXX - Not implemented yet; this requires native help. + * + * @param name the resource to find + * @return the URL to the resource + */ + static URL getResource(String name) + { + try + { + Assembly assembly = findResourceAssembly(name); + if(assembly != null) + { + return new URL("ikvmres:" + name + ":" + assembly.get_FullName()); + } + } + catch(java.net.MalformedURLException x) + { + } + return null; + } + private static native Assembly findResourceAssembly(String name); + + /** + * Helper to get a list of resources from the bootstrap class loader. + * + * XXX - Not implemented yet; this requires native help. + * + * @param name the resource to find + * @return an enumeration of resources + * @throws IOException if one occurs + */ + static Enumeration getResources(String name) throws IOException + { + System.out.println("*** VMClassLoader.getResources: " + name); + // TODO + return new java.util.Vector(0).elements(); + //return ClassLoader.getSystemResources(name); + } + + /** + * Helper to get a package from the bootstrap class loader. The default + * implementation of returning null may be adequate, or you may decide + * that this needs some native help. + * + * @param name the name to find + * @return the named package, if it exists + */ + static Package getPackage(String name) + { + return null; + } + + /** + * Helper to get all packages from the bootstrap class loader. The default + * implementation of returning an empty array may be adequate, or you may + * decide that this needs some native help. + * + * @return all named packages, if any exist + */ + static Package[] getPackages() + { + return new Package[0]; + } + + /** + * Helper for java.lang.Integer, Byte, etc to get the TYPE class + * at initialization time. The type code is one of the chars that + * represents the primitive type as in JNI. + * + * <ul> + * <li>'Z' - boolean</li> + * <li>'B' - byte</li> + * <li>'C' - char</li> + * <li>'D' - double</li> + * <li>'F' - float</li> + * <li>'I' - int</li> + * <li>'J' - long</li> + * <li>'S' - short</li> + * <li>'V' - void</li> + * </ul> + * + * Note that this is currently a java version that converts the type code + * to a string and calls the native <code>getPrimitiveClass(String)</code> + * method for backwards compatibility with VMs that used old versions of + * GNU Classpath. Please replace this method with a native method + * <code>final static native Class getPrimitiveClass(char type);</code> + * if your VM supports it. <strong>The java version of this method and + * the String version of this method will disappear in a future version + * of GNU Classpath</strong>. + * + * @param type the primitive type + * @return a "bogus" class representing the primitive type + */ + static final Class getPrimitiveClass(char type) + { + return Class.getClassFromType(getPrimitiveType(type)); + } + + private static native system.Type getPrimitiveType(char type); + + /** + * The system default for assertion status. This is used for all system + * classes (those with a null ClassLoader), as well as the initial value for + * every ClassLoader's default assertion status. + * + * XXX - Not implemented yet; this requires native help. + * + * @return the system-wide default assertion status + */ + static final boolean defaultAssertionStatus() + { + return true; + } + + /** + * The system default for package assertion status. This is used for all + * ClassLoader's packageAssertionStatus defaults. It must be a map of + * package names to Boolean.TRUE or Boolean.FALSE, with the unnamed package + * represented as a null key. + * + * XXX - Not implemented yet; this requires native help. + * + * @return a (read-only) map for the default packageAssertionStatus + */ + static final Map packageAssertionStatus() + { + return new HashMap(); + } + + /** + * The system default for class assertion status. This is used for all + * ClassLoader's classAssertionStatus defaults. It must be a map of + * class names to Boolean.TRUE or Boolean.FALSE + * + * XXX - Not implemented yet; this requires native help. + * + * @return a (read-only) map for the default classAssertionStatus + */ + static final Map classAssertionStatus() + { + return new HashMap(); + } +} diff --git a/classpath/java/lang/reflect/Constructor.java b/classpath/java/lang/reflect/Constructor.java new file mode 100644 index 00000000..a52aedd7 --- /dev/null +++ b/classpath/java/lang/reflect/Constructor.java @@ -0,0 +1,282 @@ +/* java.lang.reflect.Constructor - reflection of Java constructors + Copyright (C) 1998, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import system.*; +import system.reflection.*; + +/** + * The Constructor class represents a constructor of a class. It also allows + * dynamic creation of an object, via reflection. Invocation on Constructor + * objects knows how to do widening conversions, but throws + * {@link IllegalArgumentException} if a narrowing conversion would be + * necessary. You can query for information on this Constructor regardless + * of location, but construction access may be limited by Java language + * access controls. If you can't do it in the compiler, you can't normally + * do it here either.<p> + * + * <B>Note:</B> This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are <code>java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,</code>, also available as <code>boolean.class, + * byte.class</code>, etc. These are not to be confused with the + * classes <code>java.lang.Boolean, java.lang.Byte</code>, etc., which are + * real classes.<p> + * + * Also note that this is not a serializable class. It is entirely feasible + * to make it serializable using the Externalizable interface, but this is + * on Sun, not me. + * + * @author John Keiser + * @author Eric Blake <ebb9@email.byu.edu> + * @see Member + * @see Class + * @see java.lang.Class#getConstructor(Object[]) + * @see java.lang.Class#getDeclaredConstructor(Object[]) + * @see java.lang.Class#getConstructors() + * @see java.lang.Class#getDeclaredConstructors() + * @since 1.1 + * @status updated to 1.4 + */ +public final class Constructor + extends AccessibleObject implements Member +{ + private Class clazz; + private Object methodCookie; + + /** + * This class is instantiated by java.lang.Class + */ + // TODO this constructor shouldn't be public (but it needs to be accessible to java.lang.Class) + public Constructor(Class declaringClass, Object methodCookie) + { + this.clazz = declaringClass; + this.methodCookie = methodCookie; + } + + /** + * Gets the class that declared this constructor. + * @return the class that declared this member + */ + public Class getDeclaringClass() + { + return clazz; + } + + /** + * Gets the name of this constructor (the non-qualified name of the class + * it was declared in). + * @return the name of this constructor + */ + public String getName() + { + return Method.GetName(methodCookie); + } + + /** + * Gets the modifiers this constructor uses. Use the <code>Modifier</code> + * class to interpret the values. A constructor can only have a subset of the + * following modifiers: public, private, protected. + * + * @return an integer representing the modifiers to this Member + * @see Modifier + */ + public int getModifiers() + { + return Method.GetModifiers(methodCookie); + } + + /** + * Get the parameter list for this constructor, in declaration order. If the + * constructor takes no parameters, returns a 0-length array (not null). + * + * @return a list of the types of the constructor's parameters + */ + public Class[] getParameterTypes() + { + Object[] params = Method.GetParameterTypes(methodCookie); + Class[] paramsClass = new Class[params.length]; + System.arraycopy(params, 0, paramsClass, 0, params.length); + return paramsClass; + } + + /** + * Get the exception types this constructor says it throws, in no particular + * order. If the constructor has no throws clause, returns a 0-length array + * (not null). + * + * @return a list of the types in the constructor's throws clause + */ + public Class[] getExceptionTypes() + { + Object[] ex = Method.GetExceptionTypes(methodCookie); + Class[] exc = new Class[ex.length]; + System.arraycopy(ex, 0, exc, 0, ex.length); + return exc; + } + + /** + * Compare two objects to see if they are semantically equivalent. + * Two Constructors are semantically equivalent if they have the same + * declaring class and the same parameter list. This ignores different + * exception clauses, but since you can't create a Method except through the + * VM, this is just the == relation. + * + * @param o the object to compare to + * @return <code>true</code> if they are equal; <code>false</code> if not. + */ + public boolean equals(Object o) + { + if(o instanceof ConstructorInfo) + { + return methodCookie == ((Constructor)o).methodCookie; + } + return false; + } + + /** + * Get the hash code for the Constructor. The Constructor hash code is the + * hash code of the declaring class's name. + * + * @return the hash code for the object + */ + public int hashCode() + { + return getDeclaringClass().getName().hashCode(); + } + + /** + * Get a String representation of the Constructor. A Constructor's String + * representation is "<modifier> <classname>(<paramtypes>) + * throws <exceptions>", where everything after ')' is omitted if + * there are no exceptions.<br> Example: + * <code>public java.io.FileInputStream(java.lang.Runnable) + * throws java.io.FileNotFoundException</code> + * + * @return the String representation of the Constructor + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + Modifier.toString(getModifiers(), sb).append(' '); + sb.append(getDeclaringClass().getName()).append('('); + Class[] c = getParameterTypes(); + if (c.length > 0) + { + sb.append(c[0].getName()); + for (int i = 1; i < c.length; i++) + sb.append(',').append(c[i].getName()); + } + sb.append(')'); + c = getExceptionTypes(); + if (c.length > 0) + { + sb.append(" throws ").append(c[0].getName()); + for (int i = 1; i < c.length; i++) + sb.append(',').append(c[i].getName()); + } + return sb.toString(); + } + + /** + * Create a new instance by invoking the constructor. Arguments are + * automatically unwrapped and widened, if needed.<p> + * + * If this class is abstract, you will get an + * <code>InstantiationException</code>. If the constructor takes 0 + * arguments, you may use null or a 0-length array for <code>args</code>.<p> + * + * If this Constructor enforces access control, your runtime context is + * evaluated, and you may have an <code>IllegalAccessException</code> if + * you could not create this object in similar compiled code. If the class + * is uninitialized, you trigger class initialization, which may end in a + * <code>ExceptionInInitializerError</code>.<p> + * + * Then, the constructor is invoked. If it completes normally, the return + * value will be the new object. If it completes abruptly, the exception is + * wrapped in an <code>InvocationTargetException</code>. + * + * @param args the arguments to the constructor + * @return the newly created object + * @throws IllegalAccessException if the constructor could not normally be + * called by the Java code (i.e. it is not public) + * @throws IllegalArgumentException if the number of arguments is incorrect; + * or if the arguments types are wrong even with a widening + * conversion + * @throws InstantiationException if the class is abstract + * @throws InvocationTargetException if the constructor throws an exception + * @throws ExceptionInInitializerError if construction triggered class + * initialization, which then failed + */ + public Object newInstance(Object args[]) + throws InstantiationException, IllegalAccessException, + InvocationTargetException + { + return Method.Invoke(methodCookie, null, args); + /* + try + { + if(false) throw new MemberAccessException(); + if(false) throw new ArgumentException(); + if(false) throw new TargetParameterCountException(); + if(false) throw new TargetInvocationException(null); + // TODO wrappers need to be unwrapped (e.g. java.lang.Integer -> boxed System.Int32) + return ci.Invoke(args); + } + catch(MemberAccessException x1) + { + throw new IllegalAccessException(x1.get_Message()); + } + catch(ArgumentException x2) + { + throw new IllegalArgumentException(); + } + catch(TargetParameterCountException x3) + { + throw new IllegalArgumentException(); + } + catch(TargetInvocationException x4) + { + InstantiationException ie = new InstantiationException(); + ie.initCause(Method.mapException(x4.get_InnerException())); + throw ie; + } + */ + } +} diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java new file mode 100644 index 00000000..fc603836 --- /dev/null +++ b/classpath/java/lang/reflect/Field.java @@ -0,0 +1,646 @@ +/* java.lang.reflect.Field - reflection of Java fields + Copyright (C) 1998, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * The Field class represents a member variable of a class. It also allows + * dynamic access to a member, via reflection. This works for both + * static and instance fields. Operations on Field objects know how to + * do widening conversions, but throw {@link IllegalArgumentException} if + * a narrowing conversion would be necessary. You can query for information + * on this Field regardless of location, but get and set access may be limited + * by Java language access controls. If you can't do it in the compiler, you + * can't normally do it here either.<p> + * + * <B>Note:</B> This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are <code>java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,</code>, also available as <code>boolean.class, + * byte.class</code>, etc. These are not to be confused with the + * classes <code>java.lang.Boolean, java.lang.Byte</code>, etc., which are + * real classes.<p> + * + * Also note that this is not a serializable class. It is entirely feasible + * to make it serializable using the Externalizable interface, but this is + * on Sun, not me. + * + * @author John Keiser + * @author Eric Blake <ebb9@email.byu.edu> + * @see Member + * @see Class + * @see Class#getField(String) + * @see Class#getDeclaredField(String) + * @see Class#getFields() + * @see Class#getDeclaredFields() + * @since 1.1 + * @status updated to 1.4 + */ +public final class Field extends AccessibleObject implements Member +{ + private Class declaringClass; + private Object fieldCookie; + + /** + * This class is uninstantiable except natively. + */ + public Field(Class declaringClass, Object fieldCookie) + { + this.declaringClass = declaringClass; + this.fieldCookie = fieldCookie; + } + + /** + * Gets the class that declared this field, or the class where this field + * is a non-inherited member. + * @return the class that declared this member + */ + public Class getDeclaringClass() + { + return declaringClass; + } + + /** + * Gets the name of this field. + * @return the name of this field + */ + public String getName() + { + return GetName(fieldCookie); + } + private static native String GetName(Object fieldCookie); + + + /** + * Gets the modifiers this field uses. Use the <code>Modifier</code> + * class to interpret the values. A field can only have a subset of the + * following modifiers: public, private, protected, static, final, + * transient, and volatile. + * + * @return an integer representing the modifiers to this Member + * @see Modifier + */ + public int getModifiers() + { + return GetModifiers(fieldCookie); + } + private static native int GetModifiers(Object fieldCookie); + + /** + * Gets the type of this field. + * @return the type of this field + */ + public Class getType() + { + return (Class)GetFieldType(fieldCookie); + } + private static native Object GetFieldType(Object fieldCookie); + + /** + * Compare two objects to see if they are semantically equivalent. + * Two Fields are semantically equivalent if they have the same declaring + * class, name, and type. Since you can't creat a Field except through + * the VM, this is just the == relation. + * + * @param o the object to compare to + * @return <code>true</code> if they are equal; <code>false</code> if not + */ + public boolean equals(Object o) + { + if(o instanceof Field) + { + return fieldCookie == ((Field)o).fieldCookie; + } + return false; + } + + /** + * Get the hash code for the Field. The Field hash code is the hash code + * of its name XOR'd with the hash code of its class name. + * + * @return the hash code for the object. + */ + public int hashCode() + { + return getDeclaringClass().getName().hashCode() ^ getName().hashCode(); + } + + /** + * Get a String representation of the Field. A Field's String + * representation is "<modifiers> <type> + * <class>.<fieldname>".<br> Example: + * <code>public transient boolean gnu.parse.Parser.parseComplete</code> + * + * @return the String representation of the Field + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + Modifier.toString(getModifiers(), sb).append(' '); + sb.append(getType().getName()).append(' '); + sb.append(getDeclaringClass().getName()).append('.'); + sb.append(getName()); + return sb.toString(); + } + + /** + * Get the value of this Field. If it is primitive, it will be wrapped + * in the appropriate wrapper type (boolean = java.lang.Boolean).<p> + * + * If the field is static, <code>o</code> will be ignored. Otherwise, if + * <code>o</code> is null, you get a <code>NullPointerException</code>, + * and if it is incompatible with the declaring class of the field, you + * get an <code>IllegalArgumentException</code>.<p> + * + * Next, if this Field enforces access control, your runtime context is + * evaluated, and you may have an <code>IllegalAccessException</code> if + * you could not access this field in similar compiled code. If the field + * is static, and its class is uninitialized, you trigger class + * initialization, which may end in a + * <code>ExceptionInInitializerError</code>.<p> + * + * Finally, the field is accessed, and primitives are wrapped (but not + * necessarily in new objects). This method accesses the field of the + * declaring class, even if the instance passed in belongs to a subclass + * which declares another field to hide this one. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if <code>o</code> is not an instance of + * the class or interface declaring this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #getBoolean(Object) + * @see #getByte(Object) + * @see #getChar(Object) + * @see #getShort(Object) + * @see #getInt(Object) + * @see #getLong(Object) + * @see #getFloat(Object) + * @see #getDouble(Object) + */ + public Object get(Object o) + throws IllegalAccessException + { + return GetValue(fieldCookie, o); + } + private static native Object GetValue(Object fieldCookie, Object o); + + /** + * Get the value of this boolean Field. If the field is static, + * <code>o</code> will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a boolean field of + * <code>o</code>, or if <code>o</code> is not an instance of the + * declaring class of this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public boolean getBoolean(Object o) + throws IllegalAccessException + { + return ((Boolean)get(o)).booleanValue(); + } + + /** + * Get the value of this byte Field. If the field is static, + * <code>o</code> will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte field of + * <code>o</code>, or if <code>o</code> is not an instance of the + * declaring class of this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public byte getByte(Object o) + throws IllegalAccessException + { + return ((Byte)get(o)).byteValue(); + } + + /** + * Get the value of this Field as a char. If the field is static, + * <code>o</code> will be ignored. + * + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a char field of + * <code>o</code>, or if <code>o</code> is not an instance + * of the declaring class of this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public char getChar(Object o) + throws IllegalAccessException + { + return ((Character)get(o)).charValue(); + } + + /** + * Get the value of this Field as a short. If the field is static, + * <code>o</code> will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte or short + * field of <code>o</code>, or if <code>o</code> is not an instance + * of the declaring class of this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public short getShort(Object o) + throws IllegalAccessException + { + return ((Short)get(o)).shortValue(); + } + + /** + * Get the value of this Field as an int. If the field is static, + * <code>o</code> will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, char, or + * int field of <code>o</code>, or if <code>o</code> is not an + * instance of the declaring class of this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public int getInt(Object o) + throws IllegalAccessException + { + return ((Integer)get(o)).intValue(); + } + + /** + * Get the value of this Field as a long. If the field is static, + * <code>o</code> will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, char, int, + * or long field of <code>o</code>, or if <code>o</code> is not an + * instance of the declaring class of this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public long getLong(Object o) + throws IllegalAccessException + { + return ((Long)get(o)).longValue(); + } + + /** + * Get the value of this Field as a float. If the field is static, + * <code>o</code> will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, char, int, + * long, or float field of <code>o</code>, or if <code>o</code> is + * not an instance of the declaring class of this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public float getFloat(Object o) + throws IllegalAccessException + { + return ((Float)get(o)).floatValue(); + } + + /** + * Get the value of this Field as a double. If the field is static, + * <code>o</code> will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, char, int, + * long, float, or double field of <code>o</code>, or if + * <code>o</code> is not an instance of the declaring class of this + * field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public double getDouble(Object o) + throws IllegalAccessException + { + return ((Double)get(o)).doubleValue(); + } + + /** + * Set the value of this Field. If it is a primitive field, the value + * will be unwrapped from the passed object (boolean = java.lang.Boolean).<p> + * + * If the field is static, <code>o</code> will be ignored. Otherwise, if + * <code>o</code> is null, you get a <code>NullPointerException</code>, + * and if it is incompatible with the declaring class of the field, you + * get an <code>IllegalArgumentException</code>.<p> + * + * Next, if this Field enforces access control, your runtime context is + * evaluated, and you may have an <code>IllegalAccessException</code> if + * you could not access this field in similar compiled code. This also + * occurs whether or not there is access control if the field is final. + * If the field is primitive, and unwrapping your argument fails, you will + * get an <code>IllegalArgumentException</code>; likewise, this error + * happens if <code>value</code> cannot be cast to the correct object type. + * If the field is static, and its class is uninitialized, you trigger class + * initialization, which may end in a + * <code>ExceptionInInitializerError</code>.<p> + * + * Finally, the field is set with the widened value. This method accesses + * the field of the declaring class, even if the instance passed in belongs + * to a subclass which declares another field to hide this one. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if <code>value</code> cannot be + * converted by a widening conversion to the underlying type of + * the Field, or if <code>o</code> is not an instance of the class + * declaring this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #setBoolean(Object, boolean) + * @see #setByte(Object, byte) + * @see #setChar(Object, char) + * @see #setShort(Object, short) + * @see #setInt(Object, int) + * @see #setLong(Object, long) + * @see #setFloat(Object, float) + * @see #setDouble(Object, double) + */ + public void set(Object o, Object value) + throws IllegalAccessException + { + SetValue(fieldCookie, o, value); + } + private static native void SetValue(Object fieldCookie, Object o, Object value); + + /** + * Set this boolean Field. If the field is static, <code>o</code> will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a boolean field, or if + * <code>o</code> is not an instance of the class declaring this + * field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setBoolean(Object o, boolean value) + throws IllegalAccessException + { + set(o, new Boolean(value)); + } + + /** + * Set this byte Field. If the field is static, <code>o</code> will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, int, long, + * float, or double field, or if <code>o</code> is not an instance + * of the class declaring this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setByte(Object o, byte value) + throws IllegalAccessException + { + set(o, new Byte(value)); + } + + /** + * Set this char Field. If the field is static, <code>o</code> will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a char, int, long, + * float, or double field, or if <code>o</code> is not an instance + * of the class declaring this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setChar(Object o, char value) + throws IllegalAccessException + { + set(o, new Character(value)); + } + + /** + * Set this short Field. If the field is static, <code>o</code> will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a short, int, long, + * float, or double field, or if <code>o</code> is not an instance + * of the class declaring this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setShort(Object o, short value) + throws IllegalAccessException + { + set(o, new Short(value)); + } + + /** + * Set this int Field. If the field is static, <code>o</code> will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not an int, long, float, or + * double field, or if <code>o</code> is not an instance of the + * class declaring this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setInt(Object o, int value) + throws IllegalAccessException + { + set(o, new Integer(value)); + } + + /** + * Set this long Field. If the field is static, <code>o</code> will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a long, float, or double + * field, or if <code>o</code> is not an instance of the class + * declaring this field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setLong(Object o, long value) + throws IllegalAccessException + { + set(o, new Long(value)); + } + + /** + * Set this float Field. If the field is static, <code>o</code> will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a float or long field, or + * if <code>o</code> is not an instance of the class declaring this + * field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setFloat(Object o, float value) + throws IllegalAccessException + { + set(o, new Float(value)); + } + + /** + * Set this double Field. If the field is static, <code>o</code> will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a double field, or if + * <code>o</code> is not an instance of the class declaring this + * field + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setDouble(Object o, double value) + throws IllegalAccessException + { + set(o, new Double(value)); + } +} diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java new file mode 100644 index 00000000..4bf682a8 --- /dev/null +++ b/classpath/java/lang/reflect/Method.java @@ -0,0 +1,376 @@ +/* java.lang.reflect.Method - reflection of Java methods + Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import system.*; +import system.reflection.*; + +/** + * The Method class represents a member method of a class. It also allows + * dynamic invocation, via reflection. This works for both static and + * instance methods. Invocation on Method objects knows how to do + * widening conversions, but throws {@link IllegalArgumentException} if + * a narrowing conversion would be necessary. You can query for information + * on this Method regardless of location, but invocation access may be limited + * by Java language access controls. If you can't do it in the compiler, you + * can't normally do it here either.<p> + * + * <B>Note:</B> This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are <code>java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,</code>, also available as <code>boolean.class, + * byte.class</code>, etc. These are not to be confused with the + * classes <code>java.lang.Boolean, java.lang.Byte</code>, etc., which are + * real classes.<p> + * + * Also note that this is not a serializable class. It is entirely feasible + * to make it serializable using the Externalizable interface, but this is + * on Sun, not me. + * + * @author John Keiser + * @author Eric Blake <ebb9@email.byu.edu> + * @see Member + * @see Class + * @see java.lang.Class#getMethod(String,Object[]) + * @see java.lang.Class#getDeclaredMethod(String,Object[]) + * @see java.lang.Class#getMethods() + * @see java.lang.Class#getDeclaredMethods() + * @since 1.1 + * @status updated to 1.4 + */ +public final class Method extends AccessibleObject implements Member +{ + private Class declaringClass; + private Object methodCookie; + + /** + * This class is uninstantiable. + */ + public Method(Class declaringClass, Object methodCookie) + { + this.declaringClass = declaringClass; + this.methodCookie = methodCookie; + } + + /** + * Gets the class that declared this method, or the class where this method + * is a non-inherited member. + * @return the class that declared this member + */ + public Class getDeclaringClass() + { + return declaringClass; + } + + /** + * Gets the name of this method. + * @return the name of this method + */ + public String getName() + { + return GetName(methodCookie); + } + static native String GetName(Object methodCookie); + + /** + * Gets the modifiers this method uses. Use the <code>Modifier</code> + * class to interpret the values. A method can only have a subset of the + * following modifiers: public, private, protected, abstract, static, + * final, synchronized, native, and strictfp. + * + * @return an integer representing the modifiers to this Member + * @see Modifier + */ + public int getModifiers() + { + return GetModifiers(methodCookie); + } + static native int GetModifiers(Object methodCookie); + + /** + * Gets the return type of this method. + * @return the type of this method + */ + public Class getReturnType() + { + return (Class)GetReturnType(methodCookie); + } + static native Object GetReturnType(Object methodCookie); + + /** + * Get the parameter list for this method, in declaration order. If the + * method takes no parameters, returns a 0-length array (not null). + * + * @return a list of the types of the method's parameters + */ + public Class[] getParameterTypes() + { + Object[] params = GetParameterTypes(methodCookie); + Class[] paramsClass = new Class[params.length]; + System.arraycopy(params, 0, paramsClass, 0, params.length); + return paramsClass; + } + static native Object[] GetParameterTypes(Object methodCookie); + + /** + * Get the exception types this method says it throws, in no particular + * order. If the method has no throws clause, returns a 0-length array + * (not null). + * + * @return a list of the types in the method's throws clause + */ + public Class[] getExceptionTypes() + { + Object[] ex = GetExceptionTypes(methodCookie); + Class[] exc = new Class[ex.length]; + System.arraycopy(ex, 0, exc, 0, ex.length); + return exc; + } + static native Object[] GetExceptionTypes(Object methodCookie); + + /** + * Compare two objects to see if they are semantically equivalent. + * Two Methods are semantically equivalent if they have the same declaring + * class, name, and parameter list. This ignores different exception + * clauses or return types. + * + * @param o the object to compare to + * @return <code>true</code> if they are equal; <code>false</code> if not + */ + public boolean equals(Object o) + { + // Implementation note: + // The following is a correct but possibly slow implementation. + // + // This class has a private field 'slot' that could be used by + // the VM implementation to "link" a particular method to a Class. + // In that case equals could be simply implemented as: + // + // if (o instanceof Method) + // { + // Method m = (Method)o; + // return m.declaringClass == this.declaringClass + // && m.slot == this.slot; + // } + // return false; + // + // If a VM uses the Method class as their native/internal representation + // then just using the following would be optimal: + // + // return return this == o; + // + + if (!(o instanceof Method)) + return false; + + Method m = (Method)o; + if(!getName().equals(m.getName())) + return false; + + if(declaringClass != m.declaringClass) + return false; + + Class[] params1 = getParameterTypes(); + Class[] params2 = m.getParameterTypes(); + if(params1.length != params2.length) + return false; + + for(int i = 0; i < params1.length; i++) + if(params1[i] != params2[i]) + return false; + + return true; + } + + /** + * Get the hash code for the Method. The Method hash code is the hash code + * of its name XOR'd with the hash code of its class name. + * + * @return the hash code for the object + */ + public int hashCode() + { + return getDeclaringClass().getName().hashCode() ^ getName().hashCode(); + } + + /** + * Get a String representation of the Method. A Method's String + * representation is "<modifiers> <returntype> + * <methodname>(<paramtypes>) throws <exceptions>", where + * everything after ')' is omitted if there are no exceptions.<br> Example: + * <code>public static int run(java.lang.Runnable,int)</code> + * + * @return the String representation of the Method + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + Modifier.toString(getModifiers(), sb).append(' '); + sb.append(getReturnType().getName()).append(' '); + sb.append(getDeclaringClass().getName()).append('.'); + sb.append(getName()).append('('); + Class[] c = getParameterTypes(); + if (c.length > 0) + { + sb.append(c[0].getName()); + for (int i = 1; i < c.length; i++) + sb.append(',').append(c[i].getName()); + } + sb.append(')'); + c = getExceptionTypes(); + if (c.length > 0) + { + sb.append(" throws ").append(c[0].getName()); + for (int i = 1; i < c.length; i++) + sb.append(',').append(c[i].getName()); + } + return sb.toString(); + } + + /** + * Invoke the method. Arguments are automatically unwrapped and widened, + * and the result is automatically wrapped, if needed.<p> + * + * If the method is static, <code>o</code> will be ignored. Otherwise, + * the method uses dynamic lookup as described in JLS 15.12.4.4. You cannot + * mimic the behavior of nonvirtual lookup (as in super.foo()). This means + * you will get a <code>NullPointerException</code> if <code>o</code> is + * null, and an <code>IllegalArgumentException</code> if it is incompatible + * with the declaring class of the method. If the method takes 0 arguments, + * you may use null or a 0-length array for <code>args</code>.<p> + * + * Next, if this Method enforces access control, your runtime context is + * evaluated, and you may have an <code>IllegalAccessException</code> if + * you could not acces this method in similar compiled code. If the method + * is static, and its class is uninitialized, you trigger class + * initialization, which may end in a + * <code>ExceptionInInitializerError</code>.<p> + * + * Finally, the method is invoked. If it completes normally, the return value + * will be null for a void method, a wrapped object for a primitive return + * method, or the actual return of an Object method. If it completes + * abruptly, the exception is wrapped in an + * <code>InvocationTargetException</code>. + * + * @param o the object to invoke the method on + * @param args the arguments to the method + * @return the return value of the method, wrapped in the appropriate + * wrapper if it is primitive + * @throws IllegalAccessException if the method could not normally be called + * by the Java code (i.e. it is not public) + * @throws IllegalArgumentException if the number of arguments is incorrect; + * if the arguments types are wrong even with a widening conversion; + * or if <code>o</code> is not an instance of the class or interface + * declaring this method + * @throws InvocationTargetException if the method throws an exception + * @throws NullPointerException if <code>o</code> is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static method triggered + * class initialization, which then failed + */ + public Object invoke(Object o, Object[] args) + throws IllegalAccessException, InvocationTargetException + { + // TODO + return Invoke(methodCookie, o, args); + /* + try + { + if(false) throw new MemberAccessException(); + if(false) throw new ArgumentException(); + if(false) throw new TargetParameterCountException(); + if(false) throw new TargetInvocationException(null); + ParameterInfo[] params = method.GetParameters(); + for(int i = 0; i < params.length; i++) + { + Type type = params[i].get_ParameterType(); + if(type.get_IsPrimitive()) + { + if(type == BoxHelper.INT) + { + args[i] = BoxHelper.boxInteger(((Integer)args[i]).intValue()); + } + else if(type == BoxHelper.BOOLEAN) + { + args[i] = BoxHelper.boxBoolean(((java.lang.Boolean)args[i]).booleanValue()); + } + else + { + throw new InternalError("method invoke arg boxing not implemented for " + type.get_FullName()); + } + } + } + // TODO wrappers need to be unwrapped (e.g. java.lang.Integer -> boxed System.Int32) + Object retval = method.Invoke(o, args); + Type rettype = method.get_ReturnType(); + if(rettype.get_IsPrimitive()) + { + if(rettype == BoxHelper.INT) + { + retval = new Integer(BoxHelper.unboxInteger(retval)); + } + else + { + throw new InternalError("method invoke retval unboxing not implemented for " + rettype.get_FullName()); + } + } + return retval; + } + catch(MemberAccessException x1) + { + throw new IllegalAccessException(x1.get_Message()); + } + catch(ArgumentException x2) + { + throw new IllegalArgumentException(); + } + catch(TargetParameterCountException x3) + { + throw new IllegalArgumentException(); + } + catch(TargetInvocationException x4) + { + throw new InvocationTargetException(mapException(x4.get_InnerException())); + } + */ + } + static native Object Invoke(Object methodCookie, Object o, Object[] args); + + static native Throwable mapException(Throwable x); +} diff --git a/classpath/java/net/PlainDatagramSocketImpl.java b/classpath/java/net/PlainDatagramSocketImpl.java new file mode 100644 index 00000000..1cad3cd7 --- /dev/null +++ b/classpath/java/net/PlainDatagramSocketImpl.java @@ -0,0 +1,312 @@ +/* PlainDatagramSocketImpl.java -- Default DatagramSocket implementation + Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.IOException; +import system.net.*; +import system.net.sockets.*; + +/** +* This is the default socket implementation for datagram sockets. +* It makes native calls to C routines that implement BSD style +* SOCK_DGRAM sockets in the AF_INET family. +* +* @version 0.1 +* +* @author Aaron M. Renn (arenn@urbanophile.com) +*/ +public class PlainDatagramSocketImpl extends DatagramSocketImpl +{ + /* + * Static Variables + */ + + /** + * Option id for the IP_TTL (time to live) value. + */ + private static final int IP_TTL = 0x1E61; // 7777 + + + /* + * Instance Variables + */ + + /** + * This is the actual underlying socket + */ + private UdpClient socket; + + /*************************************************************************/ + + /* + * Constructors + */ + + /** + * Default do nothing constructor + */ + public PlainDatagramSocketImpl() + { + } + + /*************************************************************************/ + + /* + * Instance Methods + */ + + /** + * Creates a new datagram socket + * + * @exception SocketException If an error occurs + */ + protected void create() throws SocketException + { + } + + /*************************************************************************/ + + /** + * Closes the socket + */ + protected void close() + { + socket.Close(); + } + + /*************************************************************************/ + + /** + * Binds this socket to a particular port and interface + * + * @param port The port to bind to + * @param addr The address to bind to + * + * @exception SocketException If an error occurs + */ + protected void bind(int port, InetAddress addr) throws SocketException + { + // TODO error handling + socket = new UdpClient(new IPEndPoint(PlainSocketImpl.getAddressFromInetAddress(addr), port)); + } + + /*************************************************************************/ + + /** + * Sends a packet of data to a remote host + * + * @param packet The packet to send + * + * @exception IOException If an error occurs + */ + protected void send(DatagramPacket packet) throws IOException + { + // TODO error handling + int len = packet.getLength(); + if(socket.Send(packet.getData(), len, new IPEndPoint(PlainSocketImpl.getAddressFromInetAddress(packet.getAddress()), packet.getPort())) != len) + { + // TODO + throw new IOException(); + } + } + + /*************************************************************************/ + + /** + * What does this method really do? + */ + protected int peek(InetAddress addr) throws IOException + { + throw new IOException("Not Implemented Yet"); + } + + /*************************************************************************/ + + /** + * Receives a UDP packet from the network + * + * @param packet The packet to fill in with the data received + * + * @exception IOException IOException If an error occurs + */ + protected native void receive(DatagramPacket packet) throws IOException; + + /*************************************************************************/ + + /** + * Joins a multicast group + * + * @param addr The group to join + * + * @exception IOException If an error occurs + */ + protected void join(InetAddress addr) throws IOException + { + System.out.println("PlainDatagramSocketImpl.join not implemented"); + throw new IOException("join not implemented"); + } + + /*************************************************************************/ + + /** + * Leaves a multicast group + * + * @param addr The group to leave + * + * @exception IOException If an error occurs + */ + protected void leave(InetAddress addr) throws IOException + { + System.out.println("PlainDatagramSocketImpl.leave not implemented"); + throw new IOException("leave not implemented"); + } + + /*************************************************************************/ + + /** + * Gets the Time to Live value for the socket + * + * @return The TTL value + * + * @exception IOException If an error occurs + */ + protected byte getTTL() throws IOException + { + Object obj = getOption(IP_TTL); + + if (!(obj instanceof Integer)) + throw new IOException("Internal Error"); + + return(((Integer)obj).byteValue()); + } + + /*************************************************************************/ + + /** + * Sets the Time to Live value for the socket + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + */ + protected void setTTL(byte ttl) throws IOException + { + setOption(IP_TTL, new Integer(ttl)); + } + + /*************************************************************************/ + + /** + * Gets the Time to Live value for the socket + * + * @return The TTL value + * + * @exception IOException If an error occurs + */ + protected int getTimeToLive() throws IOException + { + Object obj = getOption(IP_TTL); + + if (!(obj instanceof Integer)) + throw new IOException("Internal Error"); + + return(((Integer)obj).intValue()); + } + + /*************************************************************************/ + + /** + * Sets the Time to Live value for the socket + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + */ + protected void setTimeToLive(int ttl) throws IOException + { + setOption(IP_TTL, new Integer(ttl)); + } + + /*************************************************************************/ + + /** + * Retrieves the value of an option on the socket + * + * @param option_id The identifier of the option to retrieve + * + * @return The value of the option + * + * @exception SocketException If an error occurs + */ + public Object getOption(int option_id) throws SocketException + { + // TODO + return null; + } + + /*************************************************************************/ + + /** + * Sets the value of an option on the socket + * + * @param option_id The identifier of the option to set + * @param val The value of the option to set + * + * @exception SocketException If an error occurs + */ + public void setOption(int option_id, Object val) throws SocketException + { + // TODO + } + + public int peekData(DatagramPacket packet) + { + throw new InternalError ("PlainDatagramSocketImpl::peekData is not implemented"); + } + + public void joinGroup(SocketAddress address, NetworkInterface netIf) + { + throw new InternalError ("PlainDatagramSocketImpl::joinGroup is not implemented"); + } + + public void leaveGroup(SocketAddress address, NetworkInterface netIf) + { + throw new InternalError ("PlainDatagramSocketImpl::leaveGroup is not implemented"); + } +} // class PlainDatagramSocketImpl diff --git a/classpath/java/net/PlainSocketImpl.java b/classpath/java/net/PlainSocketImpl.java new file mode 100644 index 00000000..6262c63b --- /dev/null +++ b/classpath/java/net/PlainSocketImpl.java @@ -0,0 +1,327 @@ +/* PlainSocketImpl.java -- Default socket implementation + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import system.net.*; +import system.net.sockets.*; + +/** + * Unless the application installs its own SocketImplFactory, this is the + * default socket implemetation that will be used. It simply uses a + * combination of Java and native routines to implement standard BSD + * style sockets of family AF_INET and types SOCK_STREAM and SOCK_DGRAM + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class PlainSocketImpl extends SocketImpl +{ + /** + * This is the native file descriptor for this socket + */ + private system.net.sockets.Socket socket; + + + /** + * Default do nothing constructor + */ + public PlainSocketImpl() + { + } + + /** + * Accepts a new connection on this socket and returns in in the + * passed in SocketImpl. + * + * @param impl The SocketImpl object to accept this connection. + */ + protected void accept(SocketImpl impl) throws IOException + { + // TODO catch .NET exceptions + system.net.sockets.Socket accept = socket.Accept(); + ((PlainSocketImpl)impl).socket = accept; + IPEndPoint remoteEndPoint = ((IPEndPoint)accept.get_RemoteEndPoint()); + long remoteIP = remoteEndPoint.get_Address().get_Address(); + String remote = (remoteIP & 0xff) + "." + ((remoteIP >> 8) & 0xff) + "." + ((remoteIP >> 16) & 0xff) + "." + ((remoteIP >> 24) & 0xff); + impl.address = InetAddress.getByName(remote); + impl.port = remoteEndPoint.get_Port(); + impl.localport = ((IPEndPoint)accept.get_LocalEndPoint()).get_Port(); + } + + /** + * Returns the number of bytes that the caller can read from this socket + * without blocking. + * + * @return The number of readable bytes before blocking + * + * @exception IOException If an error occurs + */ + protected int available() throws IOException + { + // TODO catch .NET exceptions + return socket.get_Available(); + } + + /** + * Binds to the specified port on the specified addr. Note that this addr + * must represent a local IP address. **** How bind to INADDR_ANY? **** + * + * @param addr The address to bind to + * @param port The port number to bind to + * + * @exception IOException If an error occurs + */ + protected void bind(InetAddress addr, int port) throws IOException + { + // TODO catch .NET exceptions + socket.Bind(new IPEndPoint(getAddressFromInetAddress(addr), port)); + } + + static int getAddressFromInetAddress(InetAddress addr) + { + byte[] b = addr.getAddress(); + return ((b[3] & 0xff) << 24) + ((b[2] & 0xff) << 16) + ((b[1] & 0xff) << 8) + (b[0] & 0xff); + } + + /** + * Closes the socket. This will cause any InputStream or OutputStream + * objects for this Socket to be closed as well. + * <p> + * Note that if the SO_LINGER option is set on this socket, then the + * operation could block. + * + * @exception IOException If an error occurs + */ + protected void close() throws IOException + { + // TODO catch .NET exceptions + socket.Close(); + } + + /** + * Connects to the remote address and port specified as arguments. + * + * @param addr The remote address to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected void connect(InetAddress addr, int port) throws IOException + { + // TODO error handling + socket.Connect(new IPEndPoint(getAddressFromInetAddress(addr), port)); + } + + /** + * Connects to the remote hostname and port specified as arguments. + * + * @param hostname The remote hostname to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected void connect(String hostname, int port) throws IOException + { + // TODO error handling + InetAddress addr = InetAddress.getByName(hostname); + connect(addr, port); + } + + /** + * Creates a new socket that is not bound to any local address/port and + * is not connected to any remote address/port. This will be created as + * a stream socket if the stream parameter is true, or a datagram socket + * if the stream parameter is false. + * + * @param stream true for a stream socket, false for a datagram socket + */ + protected void create(boolean stream) throws IOException + { + // TODO error handling + if(!stream) + { + // TODO + System.out.println("NOTE: PlainSocketImpl.create(false) not implemented"); + throw new IOException("PlainSocketImpl.create(false) not implemented"); + } + socket = new system.net.sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + } + + /** + * Starts listening for connections on a socket. The queuelen parameter + * is how many pending connections will queue up waiting to be serviced + * before being accept'ed. If the queue of pending requests exceeds this + * number, additional connections will be refused. + * + * @param queuelen The length of the pending connection queue + * + * @exception IOException If an error occurs + */ + protected void listen(int queuelen) throws IOException + { + // TODO error handling + socket.Listen(queuelen); + localport = ((IPEndPoint)socket.get_LocalEndPoint()).get_Port(); + } + + /** + * Internal method used by SocketInputStream for reading data from + * the connection. Reads up to len bytes of data into the buffer + * buf starting at offset bytes into the buffer. + * + * @return The actual number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs + */ + protected int read(byte[] buf, int offset, int len) throws IOException + { + // TODO error handling + return socket.Receive(buf, offset, len, SocketFlags.None); + } + + /** + * Internal method used by SocketOuputStream for writing data to + * the connection. Writes up to len bytes of data from the buffer + * buf starting at offset bytes into the buffer. + * + * @exception IOException If an error occurs + */ + protected void write(byte[] buf, int offset, int len) throws IOException + { + // TODO error handling + socket.Send(buf, offset, len, SocketFlags.None); + } + + /** + * Sets the specified option on a socket to the passed in object. For + * options that take an integer argument, the passed in object is an + * Integer. The option_id parameter is one of the defined constants in + * this interface. + * + * @param option_id The identifier of the option + * @param val The value to set the option to + * + * @exception SocketException If an error occurs + */ + public void setOption(int option_id, Object val) throws SocketException + { + switch(option_id) + { + case 0x1006: + // TODO set the timeout + return; + } + // TODO + System.out.println("setOption: " + option_id); + } + + /** + * Returns the current setting of the specified option. The Object returned + * will be an Integer for options that have integer values. The option_id + * is one of the defined constants in this interface. + * + * @param option_id The option identifier + * + * @return The current value of the option + * + * @exception SocketException If an error occurs + */ + public Object getOption(int option_id) throws SocketException + { + switch(option_id) + { + case 0x1006: + // TODO get the timeout from somewhere + return new Integer(0); + } + // TODO + System.out.println("getOption: " + option_id); + return null; + } + + /** + * Returns an InputStream object for reading from this socket. This will + * be an instance of SocketInputStream. + * + * @return An InputStream + * + * @exception IOException If an error occurs + */ + protected InputStream getInputStream() throws IOException + { + return new SocketInputStream(this); + } + + /** + * Returns an OutputStream object for writing to this socket. This will + * be an instance of SocketOutputStream. + * + * @return An OutputStream + * + * @exception IOException If an error occurs + */ + protected OutputStream getOutputStream() throws IOException + { + return new SocketOutputStream(this); + } + + public void connect(SocketAddress address, int timeout) + { + throw new InternalError ("PlainSocketImpl::connect not implemented"); + } + + public void sendUrgentData(int data) + { + throw new InternalError ("PlainSocketImpl::sendUrgentData not implemented"); + } + + public void shutdownInput() + { + throw new InternalError ("PlainSocketImpl::shutdownInput not implemented"); + } + + public void shutdownOutput() + { + throw new InternalError ("PlainSocketImpl::shutdownOutput not implemented"); + } +} // class PlainSocketImpl diff --git a/classpath/mscorlib.jar b/classpath/mscorlib.jar Binary files differnew file mode 100644 index 00000000..d72b2d0d --- /dev/null +++ b/classpath/mscorlib.jar diff --git a/classpath/sun/misc/Ref.java b/classpath/sun/misc/Ref.java new file mode 100644 index 00000000..1f2eafeb --- /dev/null +++ b/classpath/sun/misc/Ref.java @@ -0,0 +1,66 @@ +/* + 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 + +*/ + +package sun.misc; + +import system.WeakReference; + +public abstract class Ref +{ + private WeakReference weakRef = new WeakReference(null); + + public Object get() + { + Object p = weakRef.get_Target(); + if(p == null) + { + synchronized(this) + { + if((p = weakRef.get_Target()) == null) + { + p = reconstitute(); + weakRef.set_Target(p); + } + } + } + return p; + } + + public abstract Object reconstitute(); + + public void flush() + { + weakRef.set_Target(null); + } + + public void setThing(Object thing) + { + weakRef.set_Target(thing); + } + + public Object check() + { + return weakRef.get_Target(); + } +} diff --git a/ikvm.build b/ikvm.build new file mode 100644 index 00000000..fe36fad7 --- /dev/null +++ b/ikvm.build @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<project name="ikvm" default="all"> + <!-- + NOTE because of the circular dependencies, we don't use NAnt dependencies, + but some predefined order, that hopefully makes sense. + Please note that, because of the circular dependencies, the BIN directory + needs to contain the IK.VM.NET.dll and classpath.dll binaries, before the project + can be built. + --> + <target name="all"> + <nant buildfile="ikvmc/ikvmc.build" /> + <nant buildfile="netexp/netexp.build" /> + <nant buildfile="ik.vm.jni/ik.vm.jni.build" /> + <nant buildfile="ik.vm.net/ik.vm.net.build" /> + <nant buildfile="classpath/classpath.build" /> + <nant buildfile="ikvm/ikvm.build" /> + <nant buildfile="awt/awt.build" /> + </target> +</project> diff --git a/ikvm.sln b/ikvm.sln new file mode 100644 index 00000000..d9fb8eec --- /dev/null +++ b/ikvm.sln @@ -0,0 +1,51 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IK.VM.NET", "IK.VM.NET\IK.VM.NET.csproj", "{F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IK.VM.JNI", "IK.VM.JNI\IK.VM.JNI.vcproj", "{4D400F9D-68A1-4066-95F6-85AF0E58B710}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ikvm", "ikvm\ikvm.csproj", "{4FBAFF23-1E48-4977-8C50-30F87E70A8B5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "netexp", "netexp\netexp.csproj", "{D2A9434D-59E4-43E9-8D9C-332AA138CEAD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ikvmc", "ikvmc\ikvmc.csproj", "{4CA3F3DF-D2B7-4FA2-874C-BFC4B04286A2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "awt", "awt\awt.csproj", "{E00A0FA2-1FD7-4DD9-8C06-7202CE366102}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}.Debug.ActiveCfg = Debug|.NET + {F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}.Debug.Build.0 = Debug|.NET + {F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}.Release.ActiveCfg = Release|.NET + {F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}.Release.Build.0 = Release|.NET + {4D400F9D-68A1-4066-95F6-85AF0E58B710}.Debug.ActiveCfg = Debug|Win32 + {4D400F9D-68A1-4066-95F6-85AF0E58B710}.Debug.Build.0 = Debug|Win32 + {4D400F9D-68A1-4066-95F6-85AF0E58B710}.Release.ActiveCfg = Release|Win32 + {4D400F9D-68A1-4066-95F6-85AF0E58B710}.Release.Build.0 = Release|Win32 + {4FBAFF23-1E48-4977-8C50-30F87E70A8B5}.Debug.ActiveCfg = Debug|.NET + {4FBAFF23-1E48-4977-8C50-30F87E70A8B5}.Debug.Build.0 = Debug|.NET + {4FBAFF23-1E48-4977-8C50-30F87E70A8B5}.Release.ActiveCfg = Release|.NET + {4FBAFF23-1E48-4977-8C50-30F87E70A8B5}.Release.Build.0 = Release|.NET + {D2A9434D-59E4-43E9-8D9C-332AA138CEAD}.Debug.ActiveCfg = Debug|.NET + {D2A9434D-59E4-43E9-8D9C-332AA138CEAD}.Debug.Build.0 = Debug|.NET + {D2A9434D-59E4-43E9-8D9C-332AA138CEAD}.Release.ActiveCfg = Release|.NET + {D2A9434D-59E4-43E9-8D9C-332AA138CEAD}.Release.Build.0 = Release|.NET + {4CA3F3DF-D2B7-4FA2-874C-BFC4B04286A2}.Debug.ActiveCfg = Debug|.NET + {4CA3F3DF-D2B7-4FA2-874C-BFC4B04286A2}.Debug.Build.0 = Debug|.NET + {4CA3F3DF-D2B7-4FA2-874C-BFC4B04286A2}.Release.ActiveCfg = Release|.NET + {4CA3F3DF-D2B7-4FA2-874C-BFC4B04286A2}.Release.Build.0 = Release|.NET + {E00A0FA2-1FD7-4DD9-8C06-7202CE366102}.Debug.ActiveCfg = Debug|.NET + {E00A0FA2-1FD7-4DD9-8C06-7202CE366102}.Debug.Build.0 = Debug|.NET + {E00A0FA2-1FD7-4DD9-8C06-7202CE366102}.Release.ActiveCfg = Release|.NET + {E00A0FA2-1FD7-4DD9-8C06-7202CE366102}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/ikvm/AssemblyInfo.cs b/ikvm/AssemblyInfo.cs new file mode 100644 index 00000000..1fb1f8ef --- /dev/null +++ b/ikvm/AssemblyInfo.cs @@ -0,0 +1,81 @@ +/* + 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 + +*/ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\<configuration>. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/ikvm/bin/Debug/lib/security/classpath.security b/ikvm/bin/Debug/lib/security/classpath.security new file mode 100644 index 00000000..cd1223be --- /dev/null +++ b/ikvm/bin/Debug/lib/security/classpath.security @@ -0,0 +1,39 @@ +# classpath.security +# Copyright (C) 2002 Free Software Foundation, Inc. +# +# This file is part of GNU Classpath. +# +# GNU Classpath is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Classpath is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Classpath; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307 USA. +# +# Linking this library statically or dynamically with other modules is +# making a combined work based on this library. Thus, the terms and +# conditions of the GNU General Public License cover the whole +# combination. +# +# As a special exception, the copyright holders of this library give you +# permission to link this library with independent modules to produce an +# executable, regardless of the license terms of these independent +# modules, and to copy and distribute the resulting executable under +# terms of your choice, provided that you also meet, for each linked +# independent module, the terms and conditions of the license of that +# module. An independent module is a module which is not derived from +# or based on this library. If you modify this library, you may extend +# this exception to your version of the library, but you are not +# obligated to do so. If you do not wish to do so, delete this +# exception statement from your version. + + +security.provider.1=gnu.java.security.provider.Gnu diff --git a/ikvm/bin/Release/lib/security/classpath.security b/ikvm/bin/Release/lib/security/classpath.security new file mode 100644 index 00000000..cd1223be --- /dev/null +++ b/ikvm/bin/Release/lib/security/classpath.security @@ -0,0 +1,39 @@ +# classpath.security +# Copyright (C) 2002 Free Software Foundation, Inc. +# +# This file is part of GNU Classpath. +# +# GNU Classpath is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Classpath is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Classpath; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307 USA. +# +# Linking this library statically or dynamically with other modules is +# making a combined work based on this library. Thus, the terms and +# conditions of the GNU General Public License cover the whole +# combination. +# +# As a special exception, the copyright holders of this library give you +# permission to link this library with independent modules to produce an +# executable, regardless of the license terms of these independent +# modules, and to copy and distribute the resulting executable under +# terms of your choice, provided that you also meet, for each linked +# independent module, the terms and conditions of the license of that +# module. An independent module is a module which is not derived from +# or based on this library. If you modify this library, you may extend +# this exception to your version of the library, but you are not +# obligated to do so. If you do not wish to do so, delete this +# exception statement from your version. + + +security.provider.1=gnu.java.security.provider.Gnu diff --git a/ikvm/ikvm.build b/ikvm/ikvm.build new file mode 100644 index 00000000..46cd4024 --- /dev/null +++ b/ikvm/ikvm.build @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<project name="ikvm" default="ikvm"> + <target name="ikvm"> + <csc target="exe" output="../bin/ikvm.exe"> + <sources> + <includes name="*.cs" /> + </sources> + <references> + <includes name="../bin/classpath.dll" asis="true" /> + <includes name="../bin/IK.VM.NET.dll" asis="true" /> + </references> + </csc> + </target> +</project> diff --git a/ikvm/ikvm.csproj b/ikvm/ikvm.csproj new file mode 100644 index 00000000..ffe9af6d --- /dev/null +++ b/ikvm/ikvm.csproj @@ -0,0 +1,103 @@ +<VisualStudioProject> + <CSHARP + ProjectType = "Local" + ProductVersion = "7.0.9466" + SchemaVersion = "1.0" + ProjectGuid = "{4FBAFF23-1E48-4977-8C50-30F87E70A8B5}" + > + <Build> + <Settings + ApplicationIcon = "" + AssemblyKeyContainerName = "" + AssemblyName = "ikvm" + AssemblyOriginatorKeyFile = "" + DefaultClientScript = "JScript" + DefaultHTMLPageLayout = "Grid" + DefaultTargetSchema = "IE50" + DelaySign = "false" + OutputType = "Exe" + RootNamespace = "ikvm" + StartupObject = "" + > + <Config + Name = "Debug" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "DEBUG;TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "true" + Optimize = "false" + OutputPath = "bin\Debug\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + <Config + Name = "Release" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "false" + Optimize = "true" + OutputPath = "bin\Release\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + </Settings> + <References> + <Reference + Name = "System" + AssemblyName = "System" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.dll" + /> + <Reference + Name = "System.Data" + AssemblyName = "System.Data" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Data.dll" + /> + <Reference + Name = "System.XML" + AssemblyName = "System.Xml" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll" + /> + <Reference + Name = "IK.VM.NET" + Project = "{F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> + <Reference + Name = "classpath" + AssemblyName = "classpath" + HintPath = "..\bin\classpath.dll" + /> + </References> + </Build> + <Files> + <Include> + <File + RelPath = "AssemblyInfo.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "starter.cs" + SubType = "Code" + BuildAction = "Compile" + /> + </Include> + </Files> + </CSHARP> +</VisualStudioProject> + diff --git a/ikvm/starter.cs b/ikvm/starter.cs new file mode 100644 index 00000000..c7bdda45 --- /dev/null +++ b/ikvm/starter.cs @@ -0,0 +1,249 @@ +/* + 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 + +*/ +using System; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections; + +using java.lang.reflect; +using java.net; +using java.util.jar; +using java.io; + +public class Starter +{ + private class Timer + { + private static Timer t; + private DateTime now = DateTime.Now; + + internal Timer() + { + t = this; + } + + ~Timer() + { + Console.WriteLine(DateTime.Now - now); + } + } + + private class ExtClassLoader : URLClassLoader + { + private static URL[] GetClassPath() + { + string classpath = java.lang.System.getProperty("java.ext.dirs", ""); + string[] s = classpath.Split(';'); + ArrayList jars = new ArrayList(); + for(int i = 0; i < s.Length; i++) + { + try + { + string[] files = Directory.GetFiles(s[i]); + for(int j = 0; j < files.Length; j++) + { + jars.Add(new java.io.File(files[j]).toURL()); + } + } + catch(ArgumentException) + { + // ignore any malformed components + } + } + return (URL[])jars.ToArray(typeof(URL)); + } + + internal ExtClassLoader(java.lang.ClassLoader parent) + : base(GetClassPath(), parent) + { + } + } + + public class AppClassLoader : URLClassLoader + { + private static URL[] GetClassPath() + { + string classpath = java.lang.System.getProperty("java.class.path", "."); + string[] s = classpath.Split(';'); + URL[] urls = new URL[s.Length]; + for(int i = 0; i < urls.Length; i++) + { + // TODO non-existing file/dir is treated as current directory, this obviously isn't correct + urls[i] = new java.io.File(s[i]).toURL(); + } + return urls; + } + + public AppClassLoader(java.lang.ClassLoader parent) + : base(GetClassPath(), new ExtClassLoader(parent)) + { + } + } + + [StackTraceInfo(Hidden = true, EatFrames = 1)] + [STAThread] // NOTE this is here because otherwise SWT's RegisterDragDrop (a COM thing) doesn't work + static int Main(string[] args) + { + System.Threading.Thread.CurrentThread.Name = "main"; + bool jar = false; + bool saveAssembly = false; + string mainClass = null; + string[] vmargs = null; + for(int i = 0; i < args.Length; i++) + { + if(args[i][0] == '-') + { + if(args[i] == "-help") + { + break; + } + else if(args[i] == "-save") + { + saveAssembly = true; + } + else if(args[i] == "-time") + { + new Timer(); + } + else if(args[i] == "-jar") + { + jar = true; + } + else if(args[i].StartsWith("-D")) + { + string[] keyvalue = args[i].Substring(2).Split('='); + if(keyvalue.Length != 2) + { + keyvalue = new string[] { keyvalue[0], "" }; + } + java.lang.System.setProperty(keyvalue[0], keyvalue[1]); + } + else if(args[i] == "-cp" || args[i] == "-classpath") + { + java.lang.System.setProperty("java.class.path", args[++i]); + } + else + { + Console.Error.WriteLine("{0}: illegal argument", args[i]); + break; + } + } + else + { + mainClass = args[i]; + vmargs = new string[args.Length - (i + 1)]; + System.Array.Copy(args, i + 1, vmargs, 0, vmargs.Length); + break; + } + } + if(mainClass == null) + { + Console.Error.WriteLine("usage: ikvm [-options] <class> [args...]"); + Console.Error.WriteLine(" (to execute a class)"); + Console.Error.WriteLine(" or ikvm -jar [-options] <jarfile> [args...]"); + Console.Error.WriteLine(" (to execute a jar file)"); + Console.Error.WriteLine(); + Console.Error.WriteLine("where options include:"); + Console.Error.WriteLine(" -help Display this message"); + Console.Error.WriteLine(" -cp -classpath <directories and zip/jar files separated by ;>"); + Console.Error.WriteLine(" set search path for application classes and resources"); + Console.Error.WriteLine(" -save Save the generated assembly (for debugging)"); + Console.Error.WriteLine(" -time Time the execution"); + Console.Error.WriteLine(" -D<name>=<value> Set a system property"); + return 1; + } + try + { + // HACK we take our own assembly location as the location of classpath (this is used by the Security infrastructure + // to find the classpath.security file) + java.lang.System.setProperty("gnu.classpath.home", new System.IO.FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName); + if(jar) + { + // TODO if there is no classpath, we're adding the current directory, but is this correct when running a jar? + java.lang.System.setProperty("java.class.path", mainClass + ";" + java.lang.System.getProperty("java.class.path")); + JarFile jf = new JarFile(mainClass); + try + { + // TODO use Attributes.Name.MAIN_CLASS (we don't support inner classes at the moment) + mainClass = jf.getManifest().getMainAttributes().getValue("Main-Class"); + } + finally + { + jf.close(); + } + if(mainClass == null) + { + Console.Error.WriteLine("Manifest doesn't contain a Main-Class."); + return 1; + } + } + // NOTE we should use the default systemclassloader (gnu.java.lang.SystemClassLoader), + // but at the moment it is broken (doesn't implement findClass()) + java.lang.System.setProperty("java.system.class.loader", typeof(AppClassLoader).AssemblyQualifiedName); + java.lang.ClassLoader loader = java.lang.ClassLoader.getSystemClassLoader(); + java.lang.Class clazz = loader.loadClass(mainClass); + Method method = clazz.getMethod("main", new java.lang.Class[] { java.lang.Class.getClassFromType(typeof(string[])) }); + if(!Modifier.isPublic(method.getModifiers())) + { + Console.Error.WriteLine("Main method not public."); + return 1; + } + try + { + try + { + method.invoke(null, new object[] { vmargs }); + } + finally + { + if(saveAssembly) + { + // TODO it would be nice to wait for other threads to exit + // TODO consider using a Shutdown hook! + JVM.SaveDebugImage(clazz); + saveAssembly = false; + } + } + } + catch(InvocationTargetException x) + { + throw x.getCause(); + } + if(saveAssembly) + { + // TODO it would be nice to wait for other threads to exit + // TODO consider using a Shutdown hook! + JVM.SaveDebugImage(clazz); + } + return 0; + } + catch(System.Exception x) + { + java.lang.Thread thread = java.lang.Thread.currentThread(); + thread.getThreadGroup().uncaughtException(thread, ExceptionHelper.MapExceptionFast(x)); + return 1; + } + } +} diff --git a/ikvmc/AssemblyInfo.cs b/ikvmc/AssemblyInfo.cs new file mode 100644 index 00000000..1fb1f8ef --- /dev/null +++ b/ikvmc/AssemblyInfo.cs @@ -0,0 +1,81 @@ +/* + 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 + +*/ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\<configuration>. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/ikvmc/Compiler.cs b/ikvmc/Compiler.cs new file mode 100644 index 00000000..2d3575f2 --- /dev/null +++ b/ikvmc/Compiler.cs @@ -0,0 +1,244 @@ +/* + 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 + +*/ +using System; +using System.Collections; +using System.IO; +using System.Reflection; +using java.io; +using java.util.zip; + +class Compiler : MarshalByRefObject +{ + private static ArrayList GetArgs(string[] args) + { + ArrayList arglist = new ArrayList(); + foreach(string s in args) + { + if(s.StartsWith("@")) + { + using(StreamReader sr = new StreamReader(s.Substring(1))) + { + string line; + while((line = sr.ReadLine()) != null) + { + arglist.Add(line); + } + } + } + else + { + arglist.Add(s); + } + } + return arglist; + } + + static int Main(string[] args) + { + System.Reflection.Emit.PEFileKinds target = System.Reflection.Emit.PEFileKinds.ConsoleApplication; + string assemblyname = null; + string outputfile = null; + string main = null; + bool nojni = false; + ArrayList references = new ArrayList(); + ArrayList arglist = GetArgs(args); + if(arglist.Count == 0) + { + Console.Error.WriteLine("usage: ikvmc [-options] <classOrJar1> ... <classOrJarN>"); + Console.Error.WriteLine(); + Console.Error.WriteLine("options:"); + Console.Error.WriteLine(" -out:<outputfile> Required"); + Console.Error.WriteLine(" -assembly:<outputfile> Optionally used to specify assembly name"); + Console.Error.WriteLine(" -target:exe Build a console executable"); + Console.Error.WriteLine(" -target:winexe Build a windows executable"); + Console.Error.WriteLine(" -target:library Build a library"); + Console.Error.WriteLine(" -main:<class> Required (for executables)"); + Console.Error.WriteLine(" -reference:<path> Reference an assembly"); + Console.Error.WriteLine(" -recurse:<filespec> Recurse directory and include matching files"); + Console.Error.WriteLine(" -nojni Do not generate JNI stub for native methods"); + return 1; + } + ArrayList classes = new ArrayList(); + Hashtable resources = new Hashtable(); + foreach(string s in arglist) + { + if(s[0] == '-') + { + if(s.StartsWith("-out:")) + { + outputfile = s.Substring(5); + } + else if(s.StartsWith("-assembly:")) + { + assemblyname = s.Substring(10); + } + else if(s.StartsWith("-target:")) + { + switch(s) + { + case "-target:exe": + target = System.Reflection.Emit.PEFileKinds.ConsoleApplication; + break; + case "-target:winexe": + target = System.Reflection.Emit.PEFileKinds.WindowApplication; + break; + case "-target:library": + target = System.Reflection.Emit.PEFileKinds.Dll; + break; + } + } + else if(s.StartsWith("-main:")) + { + main = s.Substring(6); + } + else if(s.StartsWith("-reference:")) + { + references.Add(s.Substring(11)); + } + else if(s.StartsWith("-recurse:")) + { + string spec = s.Substring(9); + Recurse(classes, resources, new DirectoryInfo(Path.GetDirectoryName(spec)), Path.GetFileName(spec)); + } + else if(s == "-nojni") + { + nojni = true; + } + else + { + Console.Error.WriteLine("Warning: Unrecognized option: {0}", s); + } + } + else + { + ProcessFile(classes, resources, s); + } + } + if(outputfile == null) + { + Console.Error.WriteLine("Error: -out:<outputfile> must be specified"); + return 1; + } + if(assemblyname == null) + { + int idx = outputfile.LastIndexOf('.'); + if(idx > 0) + { + assemblyname = outputfile.Substring(0, idx); + } + else + { + assemblyname = outputfile; + } + } + if(target != System.Reflection.Emit.PEFileKinds.Dll && main == null) + { + Console.Error.WriteLine("Error: -main:<class> must be specified when creating an executable"); + return 1; + } + // HACK since we use Classpath's zip code, our VM is already running in this AppDomain, which means that + // it cannot be (ab)used to statically compile anymore, so we create a new AppDomain to run the compile in. + Compiler c = (Compiler)AppDomain.CreateDomain("Compiler").CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "Compiler"); + return c.Compile(outputfile, assemblyname, main, target, (byte[][])classes.ToArray(typeof(byte[])), (string[])references.ToArray(typeof(string)), nojni, resources); + } + + private static void ProcessFile(ArrayList classes, Hashtable resources, string file) + { + if(file.ToLower().EndsWith(".class")) + { + using(FileStream fs = new FileStream(file, FileMode.Open)) + { + byte[] b = new byte[fs.Length]; + fs.Read(b, 0, b.Length); + classes.Add(b); + } + } + else if(file.ToLower().EndsWith(".jar") || file.ToLower().EndsWith(".zip")) + { + ZipFile zf = new ZipFile(file); + java.util.Enumeration e = zf.entries(); + while(e.hasMoreElements()) + { + ZipEntry ze = (ZipEntry)e.nextElement(); + if(ze.getName().ToLower().EndsWith(".class")) + { + sbyte[] sbuf = new sbyte[ze.getSize()]; + DataInputStream dis = new DataInputStream(zf.getInputStream(ze)); + dis.readFully(sbuf); + dis.close(); + byte[] buf = new byte[sbuf.Length]; + for(int i = 0; i < buf.Length; i++) + { + buf[i] = (byte)sbuf[i]; + } + classes.Add(buf); + } + else + { + // if it's not a class, we treat it as a resource + sbyte[] sbuf = new sbyte[ze.getSize()]; + DataInputStream dis = new DataInputStream(zf.getInputStream(ze)); + dis.readFully(sbuf); + dis.close(); + byte[] buf = new byte[sbuf.Length]; + for(int i = 0; i < buf.Length; i++) + { + buf[i] = (byte)sbuf[i]; + } + resources.Add(ze.getName(), buf); + } + } + } + else + { + Console.Error.WriteLine("Warning: Unknown file type: {0}", file); + } + } + + private static void Recurse(ArrayList classes, Hashtable resources, DirectoryInfo dir, string spec) + { + foreach(FileInfo file in dir.GetFiles(spec)) + { + ProcessFile(classes, resources, file.FullName); + } + foreach(DirectoryInfo sub in dir.GetDirectories()) + { + Recurse(classes, resources, sub, spec); + } + } + + public int Compile(string fileName, string assemblyName, string mainClass, System.Reflection.Emit.PEFileKinds target, byte[][] classes, string[] references, bool nojni, Hashtable resources) + { + try + { + JVM.Compile(fileName, assemblyName, mainClass, target, classes, references, nojni, resources); + return 0; + } + catch(Exception x) + { + Console.Error.WriteLine(x); + return 1; + } + } +} diff --git a/ikvmc/bin/Release/native.txt b/ikvmc/bin/Release/native.txt new file mode 100644 index 00000000..a71719cf --- /dev/null +++ b/ikvmc/bin/Release/native.txt @@ -0,0 +1,4 @@ +Compiling class files (1) +Constructing compiler +Loading remapped types +Parsing class files diff --git a/ikvmc/ikvmc.build b/ikvmc/ikvmc.build new file mode 100644 index 00000000..27a325ce --- /dev/null +++ b/ikvmc/ikvmc.build @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<project name="ikvmc" default="ikvmc"> + <target name="ikvmc"> + <csc target="exe" output="../bin/ikvmc.exe"> + <sources> + <includes name="*.cs" /> + </sources> + <references> + <includes name="../bin/classpath.dll" asis="true" /> + <includes name="../bin/ik.vm.net.dll" asis="true" /> + </references> + </csc> + </target> +</project> diff --git a/ikvmc/ikvmc.csproj b/ikvmc/ikvmc.csproj new file mode 100644 index 00000000..06554c49 --- /dev/null +++ b/ikvmc/ikvmc.csproj @@ -0,0 +1,103 @@ +<VisualStudioProject> + <CSHARP + ProjectType = "Local" + ProductVersion = "7.0.9466" + SchemaVersion = "1.0" + ProjectGuid = "{4CA3F3DF-D2B7-4FA2-874C-BFC4B04286A2}" + > + <Build> + <Settings + ApplicationIcon = "" + AssemblyKeyContainerName = "" + AssemblyName = "ikvmc" + AssemblyOriginatorKeyFile = "" + DefaultClientScript = "JScript" + DefaultHTMLPageLayout = "Grid" + DefaultTargetSchema = "IE50" + DelaySign = "false" + OutputType = "Exe" + RootNamespace = "ikvmc" + StartupObject = "" + > + <Config + Name = "Debug" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "DEBUG;TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "true" + Optimize = "false" + OutputPath = "bin\Debug\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + <Config + Name = "Release" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "false" + Optimize = "true" + OutputPath = "bin\Release\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + </Settings> + <References> + <Reference + Name = "System" + AssemblyName = "System" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.dll" + /> + <Reference + Name = "System.Data" + AssemblyName = "System.Data" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Data.dll" + /> + <Reference + Name = "System.XML" + AssemblyName = "System.Xml" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll" + /> + <Reference + Name = "IK.VM.NET" + Project = "{F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> + <Reference + Name = "classpath" + AssemblyName = "classpath" + HintPath = "..\classpath\classpath.dll" + /> + </References> + </Build> + <Files> + <Include> + <File + RelPath = "AssemblyInfo.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Compiler.cs" + SubType = "Code" + BuildAction = "Compile" + /> + </Include> + </Files> + </CSHARP> +</VisualStudioProject> + diff --git a/netexp/AssemblyInfo.cs b/netexp/AssemblyInfo.cs new file mode 100644 index 00000000..1fb1f8ef --- /dev/null +++ b/netexp/AssemblyInfo.cs @@ -0,0 +1,81 @@ +/* + 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 + +*/ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\<configuration>. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/netexp/ClassFileWriter.cs b/netexp/ClassFileWriter.cs new file mode 100644 index 00000000..faa19915 --- /dev/null +++ b/netexp/ClassFileWriter.cs @@ -0,0 +1,632 @@ +/* + 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 + +*/ +using System; +using System.IO; +using System.Collections; + +class BigEndianStream +{ + private Stream stream; + + public BigEndianStream(Stream stream) + { + this.stream = stream; + } + + public void WriteUInt16(ushort s) + { + stream.WriteByte((byte)(s >> 8)); + stream.WriteByte((byte)s); + } + + public void WriteUInt32(uint u) + { + stream.WriteByte((byte)(u >> 24)); + stream.WriteByte((byte)(u >> 16)); + stream.WriteByte((byte)(u >> 8)); + stream.WriteByte((byte)u); + } + + public void WriteInt64(long l) + { + WriteUInt32((uint)(l >> 32)); + WriteUInt32((uint)l); + } + + public void WriteFloat(float f) + { + WriteUInt32(BitConverter.ToUInt32(BitConverter.GetBytes(f), 0)); + } + + public void WriteDouble(double d) + { + WriteInt64(BitConverter.ToInt64(BitConverter.GetBytes(d), 0)); + } + + public void WriteByte(byte b) + { + stream.WriteByte(b); + } + + public void WriteUtf8(string str) + { + byte[] buf = new byte[str.Length * 3 + 1]; + int j = 0; + for(int i = 0, e = str.Length; i < e; i++) + { + char ch = str[i]; + if ((ch != 0) && (ch <=0x7f)) + { + buf[j++] = (byte)ch; + } + else if (ch <= 0x7FF) + { + /* 11 bits or less. */ + byte high_five = (byte)(ch >> 6); + byte low_six = (byte)(ch & 0x3F); + buf[j++] = (byte)(high_five | 0xC0); /* 110xxxxx */ + buf[j++] = (byte)(low_six | 0x80); /* 10xxxxxx */ + } + else + { + /* possibly full 16 bits. */ + byte high_four = (byte)(ch >> 12); + byte mid_six = (byte)((ch >> 6) & 0x3F); + byte low_six = (byte)(ch & 0x3f); + buf[j++] = (byte)(high_four | 0xE0); /* 1110xxxx */ + buf[j++] = (byte)(mid_six | 0x80); /* 10xxxxxx */ + buf[j++] = (byte)(low_six | 0x80); /* 10xxxxxx*/ + } + } + WriteUInt16((ushort)j); + stream.Write(buf, 0, j); + } +} + +enum Constant +{ + Utf8 = 1, + Integer = 3, + Float = 4, + Long = 5, + Double = 6, + Class = 7, + String = 8, + Fieldref = 9, + Methodref = 10, + InterfaceMethodref = 11, + NameAndType = 12 +} + +abstract class ConstantPoolItem +{ + public abstract void Write(BigEndianStream bes); +} + +class ConstantPoolItemClass : ConstantPoolItem +{ + private ushort name_index; + + public ConstantPoolItemClass(ushort name_index) + { + this.name_index = name_index; + } + + public override int GetHashCode() + { + return name_index; + } + + public override bool Equals(object o) + { + if(o != null && o.GetType() == typeof(ConstantPoolItemClass)) + { + return ((ConstantPoolItemClass)o).name_index == name_index; + } + return false; + } + + public override void Write(BigEndianStream bes) + { + bes.WriteByte((byte)Constant.Class); + bes.WriteUInt16(name_index); + } +} + +class ConstantPoolItemUtf8 : ConstantPoolItem +{ + private string str; + + public ConstantPoolItemUtf8(string str) + { + this.str = str; + } + + public override int GetHashCode() + { + return str.GetHashCode(); + } + + public override bool Equals(object o) + { + if(o != null && o.GetType() == typeof(ConstantPoolItemUtf8)) + { + return ((ConstantPoolItemUtf8)o).str == str; + } + return false; + } + + public override void Write(BigEndianStream bes) + { + bes.WriteByte((byte)Constant.Utf8); + bes.WriteUtf8(str); + } +} + +class ConstantPoolItemInt : ConstantPoolItem +{ + private int v; + + public ConstantPoolItemInt(int v) + { + this.v = v; + } + + public override int GetHashCode() + { + return v; + } + + public override bool Equals(object o) + { + if(o != null && o.GetType() == typeof(ConstantPoolItemInt)) + { + return ((ConstantPoolItemInt)o).v == v; + } + return false; + } + + public override void Write(BigEndianStream bes) + { + bes.WriteByte((byte)Constant.Integer); + bes.WriteUInt32((uint)v); + } +} + +class ConstantPoolItemLong : ConstantPoolItem +{ + private long v; + + public ConstantPoolItemLong(long v) + { + this.v = v; + } + + public override int GetHashCode() + { + return (int)v; + } + + public override bool Equals(object o) + { + if(o != null && o.GetType() == typeof(ConstantPoolItemLong)) + { + return ((ConstantPoolItemLong)o).v == v; + } + return false; + } + + public override void Write(BigEndianStream bes) + { + bes.WriteByte((byte)Constant.Long); + bes.WriteInt64(v); + } +} + +class ConstantPoolItemFloat : ConstantPoolItem +{ + private float v; + + public ConstantPoolItemFloat(float v) + { + this.v = v; + } + + public override int GetHashCode() + { + return BitConverter.ToInt32(BitConverter.GetBytes(v), 0); + } + + public override bool Equals(object o) + { + if(o != null && o.GetType() == typeof(ConstantPoolItemFloat)) + { + return ((ConstantPoolItemFloat)o).v == v; + } + return false; + } + + public override void Write(BigEndianStream bes) + { + bes.WriteByte((byte)Constant.Float); + bes.WriteFloat(v); + } +} + +class ConstantPoolItemDouble : ConstantPoolItem +{ + private double v; + + public ConstantPoolItemDouble(double v) + { + this.v = v; + } + + public override int GetHashCode() + { + long l = BitConverter.DoubleToInt64Bits(v); + return ((int)l) ^ ((int)(l >> 32)); + } + + public override bool Equals(object o) + { + if(o != null && o.GetType() == typeof(ConstantPoolItemDouble)) + { + return ((ConstantPoolItemDouble)o).v == v; + } + return false; + } + + public override void Write(BigEndianStream bes) + { + bes.WriteByte((byte)Constant.Double); + bes.WriteDouble(v); + } +} + +class ConstantPoolItemString : ConstantPoolItem +{ + private ushort string_index; + + public ConstantPoolItemString(ushort string_index) + { + this.string_index = string_index; + } + + public override int GetHashCode() + { + return string_index; + } + + public override bool Equals(object o) + { + if(o != null && o.GetType() == typeof(ConstantPoolItemString)) + { + return ((ConstantPoolItemString)o).string_index == string_index; + } + return false; + } + + public override void Write(BigEndianStream bes) + { + bes.WriteByte((byte)Constant.String); + bes.WriteUInt16(string_index); + } +} + +class ClassFileAttribute +{ + private ushort name_index; + + public ClassFileAttribute(ushort name_index) + { + this.name_index = name_index; + } + + public virtual void Write(BigEndianStream bes) + { + bes.WriteUInt16(name_index); + } +} + +class ConstantValueAttribute : ClassFileAttribute +{ + private ushort constant_index; + + public ConstantValueAttribute(ushort name_index, ushort constant_index) + : base(name_index) + { + this.constant_index = constant_index; + } + + public override void Write(BigEndianStream bes) + { + base.Write(bes); + bes.WriteUInt32(2); + bes.WriteUInt16(constant_index); + } +} + +class StringAttribute : ClassFileAttribute +{ + private ushort string_index; + + public StringAttribute(ushort name_index, ushort string_index) + : base(name_index) + { + this.string_index = string_index; + } + + public override void Write(BigEndianStream bes) + { + base.Write(bes); + bes.WriteUInt32(2); + bes.WriteUInt16(string_index); + } +} + +class InnerClassesAttribute : ClassFileAttribute +{ + private ClassFileWriter classFile; + private ArrayList classes = new ArrayList(); + + public InnerClassesAttribute(ClassFileWriter classFile) + : base(classFile.AddUtf8("InnerClasses")) + { + this.classFile = classFile; + } + + public override void Write(BigEndianStream bes) + { + base.Write(bes); + bes.WriteUInt32((uint)(2 + 8 * classes.Count)); + bes.WriteUInt16((ushort)classes.Count); + foreach(Item i in classes) + { + bes.WriteUInt16(i.inner_class_info_index); + bes.WriteUInt16(i.outer_class_info_index); + bes.WriteUInt16(i.inner_name_index); + bes.WriteUInt16(i.inner_class_access_flags); + } + } + + private class Item + { + internal ushort inner_class_info_index; + internal ushort outer_class_info_index; + internal ushort inner_name_index; + internal ushort inner_class_access_flags; + } + + public void Add(string inner, string outer, string name, ushort access) + { + Item i = new Item(); + i.inner_class_info_index = classFile.AddClass(inner); + i.outer_class_info_index = classFile.AddClass(outer); + if(name != null) + { + i.inner_name_index = classFile.AddUtf8(name); + } + i.inner_class_access_flags = access; + classes.Add(i); + } +} + +class FieldOrMethod +{ + private Modifiers access_flags; + private ushort name_index; + private ushort descriptor_index; + private ArrayList attribs = new ArrayList(); + + public FieldOrMethod(Modifiers access_flags, ushort name_index, ushort descriptor_index) + { + this.access_flags = access_flags; + this.name_index = name_index; + this.descriptor_index = descriptor_index; + } + + public void AddAttribute(ClassFileAttribute attrib) + { + attribs.Add(attrib); + } + + public void Write(BigEndianStream bes) + { + bes.WriteUInt16((ushort)access_flags); + bes.WriteUInt16(name_index); + bes.WriteUInt16(descriptor_index); + bes.WriteUInt16((ushort)attribs.Count); + for(int i = 0; i < attribs.Count; i++) + { + ((ClassFileAttribute)attribs[i]).Write(bes); + } + } +} + +class ClassFileWriter +{ + private ArrayList cplist = new ArrayList(); + private Hashtable cphashtable = new Hashtable(); + private ArrayList fields = new ArrayList(); + private ArrayList methods = new ArrayList(); + private ArrayList attribs = new ArrayList(); + private Modifiers access_flags; + private ushort this_class; + private ushort super_class; + + public ClassFileWriter(Modifiers mods, string name, string super) + { + cplist.Add(null); + access_flags = mods; + this_class = AddClass(name); + if(super != null) + { + super_class = AddClass(super); + } + } + + private ushort Add(ConstantPoolItem cpi) + { + object index = cphashtable[cpi]; + if(index == null) + { + index = (ushort)cplist.Add(cpi); + if(cpi is ConstantPoolItemDouble || cpi is ConstantPoolItemLong) + { + cplist.Add(null); + } + cphashtable[cpi] = index; + } + return (ushort)index; + } + + public ushort AddUtf8(string str) + { + return Add(new ConstantPoolItemUtf8(str)); + } + + public ushort AddClass(string classname) + { + return Add(new ConstantPoolItemClass(AddUtf8(classname))); + } + + private ushort AddInt(int i) + { + return Add(new ConstantPoolItemInt(i)); + } + + private ushort AddLong(long l) + { + return Add(new ConstantPoolItemLong(l)); + } + + private ushort AddFloat(float f) + { + return Add(new ConstantPoolItemFloat(f)); + } + + private ushort AddDouble(double d) + { + return Add(new ConstantPoolItemDouble(d)); + } + + private ushort AddString(string s) + { + return Add(new ConstantPoolItemString(AddUtf8(s))); + } + + public FieldOrMethod AddMethod(Modifiers access, string name, string signature) + { + FieldOrMethod method = new FieldOrMethod(access, AddUtf8(name), AddUtf8(signature)); + methods.Add(method); + return method; + } + + public FieldOrMethod AddField(Modifiers access, string name, string signature, object constantValue) + { + FieldOrMethod field = new FieldOrMethod(access, AddUtf8(name), AddUtf8(signature)); + if(constantValue != null) + { + ushort constantValueIndex; + if(constantValue is int) + { + constantValueIndex = AddInt((int)constantValue); + } + else if(constantValue is long) + { + constantValueIndex = AddLong((long)constantValue); + } + else if(constantValue is float) + { + constantValueIndex = AddFloat((float)constantValue); + } + else if(constantValue is double) + { + constantValueIndex = AddDouble((double)constantValue); + } + else if(constantValue is string) + { + constantValueIndex = AddString((string)constantValue); + } + else + { + throw new InvalidOperationException(); + } + field.AddAttribute(new ConstantValueAttribute(AddUtf8("ConstantValue"), constantValueIndex)); + } + fields.Add(field); + return field; + } + + public void AddStringAttribute(string name, string value) + { + attribs.Add(new StringAttribute(AddUtf8(name), AddUtf8(value))); + } + + public void AddAttribute(ClassFileAttribute attrib) + { + attribs.Add(attrib); + } + + public void Write(Stream stream) + { + BigEndianStream bes = new BigEndianStream(stream); + bes.WriteUInt32(0xCAFEBABE); + bes.WriteUInt16((ushort)3); + bes.WriteUInt16((ushort)45); + bes.WriteUInt16((ushort)cplist.Count); + for(int i = 1; i < cplist.Count; i++) + { + ConstantPoolItem cpi = (ConstantPoolItem)cplist[i]; + if(cpi != null) + { + cpi.Write(bes); + } + } + bes.WriteUInt16((ushort)access_flags); + bes.WriteUInt16(this_class); + bes.WriteUInt16(super_class); + // interfaces count + bes.WriteUInt16(0); + // fields count + bes.WriteUInt16((ushort)fields.Count); + for(int i = 0; i < fields.Count; i++) + { + ((FieldOrMethod)fields[i]).Write(bes); + } + // methods count + bes.WriteUInt16((ushort)methods.Count); + for(int i = 0; i < methods.Count; i++) + { + ((FieldOrMethod)methods[i]).Write(bes); + } + // attributes count + bes.WriteUInt16((ushort)attribs.Count); + for(int i = 0; i < attribs.Count; i++) + { + ((ClassFileAttribute)attribs[i]).Write(bes); + } + } +} diff --git a/netexp/NetExp.cs b/netexp/NetExp.cs new file mode 100644 index 00000000..f2007683 --- /dev/null +++ b/netexp/NetExp.cs @@ -0,0 +1,474 @@ +/* + 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 + +*/ +using System; +using System.Reflection; +using System.IO; +using System.Text; +using System.Collections; +using java.util.zip; +using java.io; + +public class NetExp +{ + private static ZipOutputStream zipFile; + + public static void Main(string[] args) + { + Assembly assembly = null; + if(new FileInfo(args[0]).Exists) + { + assembly = Assembly.LoadFrom(args[0]); + } + if(assembly == null) + { + Console.Error.WriteLine("Error: Assembly \"{0}\" not found", args[0]); + } + else + { + zipFile = new ZipOutputStream(new FileOutputStream(assembly.GetName().Name + ".jar")); + ProcessAssembly(assembly); + zipFile.close(); + } + } + + private static void ProcessAssembly(Assembly assembly) + { + object[] attribs = assembly.GetCustomAttributes(typeof(CLSCompliantAttribute), false); + bool assemblyIsCLSCompliant = true; + if(attribs.Length != 1) + { + assemblyIsCLSCompliant = false; + Console.Error.WriteLine("Warning: assembly has no (or multiple) CLS compliance attribute"); + } + else if(!((CLSCompliantAttribute)attribs[0]).IsCompliant) + { + assemblyIsCLSCompliant = false; + Console.Error.WriteLine("Warning: assembly is marked as non-CLS compliant"); + } + foreach(Type t in assembly.GetTypes()) + { + bool typeIsCLSCompliant = true; + if(assemblyIsCLSCompliant) + { + attribs = t.GetCustomAttributes(typeof(CLSCompliantAttribute), false); + if(attribs.Length == 1) + { + typeIsCLSCompliant = ((CLSCompliantAttribute)attribs[0]).IsCompliant; + } + } + if(t.IsPublic && typeIsCLSCompliant) + { + ProcessType(t); + } + } + } + + private static object UnwrapEnum(object o) + { + // is there a way to generically convert a boxed enum to its boxed underlying value? + Type underlyingType = Enum.GetUnderlyingType(o.GetType()); + if(underlyingType == typeof(int)) + { + o = (int)o; + } + else if(underlyingType == typeof(short)) + { + o = (short)o; + } + else + { + throw new NotImplementedException(o.GetType().Name); + } + return o; + } + + private static string ClassName(Type t) + { + if(t == typeof(object)) + { + return "java/lang/Object"; + } + else if(t == typeof(string)) + { + return "java/lang/String"; + } + string name = t.FullName; + int lastDot = name.LastIndexOf('.'); + if(lastDot > 0) + { + name = name.Substring(0, lastDot).ToLower() + name.Substring(lastDot); + } + return name.Replace('.', '/'); + } + + // returns the mapped type in signature format (e.g. Ljava/lang/String;) + private static string SigType(Type t) + { + if(t.IsEnum) + { + t = Enum.GetUnderlyingType(t); + } + if(t == typeof(void)) + { + return "V"; + } + else if(t == typeof(byte) || t == typeof(sbyte)) + { + return "B"; + } + else if(t == typeof(bool)) + { + return "Z"; + } + else if(t == typeof(short) || t == typeof(ushort)) + { + return "S"; + } + else if(t == typeof(char)) + { + return "C"; + } + else if(t == typeof(int) || t == typeof(uint)) + { + return "I"; + } + else if(t == typeof(long) || t == typeof(ulong)) + { + return "J"; + } + else if(t == typeof(float)) + { + return "F"; + } + else if(t == typeof(double)) + { + return "D"; + } + else if(t == typeof(IntPtr)) + { + // HACK + return "I"; + } + else if(t.IsArray) + { + StringBuilder sb = new StringBuilder(); + while(t.IsArray) + { + sb.Append('['); + t = t.GetElementType(); + } + sb.Append(SigType(t)); + return sb.ToString(); + } + else if(!t.IsPrimitive) + { + return "L" + ClassName(t) + ";"; + } + else + { + throw new NotImplementedException(t.FullName); + } + } + + private static void ProcessType(Type type) + { + if(type == typeof(object) || type == typeof(string)) + { + // special case for System.Object & System.String, don't emit those + return; + } + string name = ClassName(type); + string super; + if(type.BaseType == null) + { + // in .NET interfaces don't have a baseType, but in Java they "extend" java/lang/Object + super = "java/lang/Object"; + } + else + { + if(type == typeof(Exception)) + { + super = "java/lang/Throwable"; + } + else + { + super = ClassName(type.BaseType); + } + } + Modifiers mods = Modifiers.Public | Modifiers.Super; + if(type.IsInterface) + { + mods |= Modifiers.Interface; + } + if(type.IsSealed) + { + mods |= Modifiers.Final; + } + if(type.IsAbstract) + { + mods |= Modifiers.Abstract; + } + ClassFileWriter f = new ClassFileWriter(mods, name, super); + f.AddStringAttribute("IK.VM.NET.Type", type.AssemblyQualifiedName); + FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + Hashtable clashtable = new Hashtable(); + for(int i = 0; i < fields.Length; i++) + { + if(fields[i].IsPublic || fields[i].IsFamily) + { + object[] attribs = fields[i].GetCustomAttributes(typeof(CLSCompliantAttribute), false); + if(attribs.Length == 1 && !((CLSCompliantAttribute)attribs[0]).IsCompliant) + { + // skip non-CLS compliant field + } + else + { + ProcessField(type, f, fields[i], clashtable); + } + } + } + clashtable.Clear(); + ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + for(int i = 0; i < constructors.Length; i++) + { + if(constructors[i].IsPublic || constructors[i].IsFamily) + { + object[] attribs = constructors[i].GetCustomAttributes(typeof(CLSCompliantAttribute), false); + if(attribs.Length == 1 && !((CLSCompliantAttribute)attribs[0]).IsCompliant) + { + // skip non-CLS compliant field + } + else + { + ProcessMethod(type, f, constructors[i], clashtable); + } + } + } + MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); + for(int i = 0; i < methods.Length; i++) + { + if(methods[i].IsPublic || methods[i].IsFamily) + { + object[] attribs = methods[i].GetCustomAttributes(typeof(CLSCompliantAttribute), false); + if(attribs.Length == 1 && !((CLSCompliantAttribute)attribs[0]).IsCompliant) + { + // skip non-CLS compliant field + } + else + { + ProcessMethod(type, f, methods[i], clashtable); + } + } + } + // for delegates we have to construct the inner interface + if(type.IsSubclassOf(typeof(MulticastDelegate))) + { + InnerClassesAttribute innerclasses = new InnerClassesAttribute(f); + string outer = ClassName(type); + innerclasses.Add(outer + "$Method", outer, "Method", 0x209); + f.AddAttribute(innerclasses); + // now we construct the inner interface type + ClassFileWriter iface = new ClassFileWriter(Modifiers.Interface | Modifiers.Public, outer + "$Method", "java/lang/Object"); + MethodInfo invoke = type.GetMethod("Invoke"); + StringBuilder sb = new StringBuilder(); + sb.Append('('); + ParameterInfo[] parameters = invoke.GetParameters(); + for(int i = 0; i < parameters.Length; i++) + { + sb.Append(SigType(parameters[i].ParameterType)); + } + sb.Append(')'); + sb.Append(SigType(invoke.ReturnType)); + // TODO IK.VM.NET.Sig must be set here as well + iface.AddMethod(Modifiers.Public | Modifiers.Abstract, "Invoke", sb.ToString()); + innerclasses = new InnerClassesAttribute(iface); + innerclasses.Add(outer + "$Method", outer, "Method", 0x209); + iface.AddAttribute(innerclasses); + WriteClass(outer + "$Method.class", iface); + } + WriteClass(name + ".class", f); + } + + private static void WriteClass(string name, ClassFileWriter c) + { + zipFile.putNextEntry(new ZipEntry(name)); + MemoryStream s = new MemoryStream(); + c.Write(s); + byte[] buf = s.ToArray(); + sbyte[] sbuf = new sbyte[buf.Length]; + for(int i = 0; i < buf.Length; i++) + { + sbuf[i] = (sbyte)buf[i]; + } + zipFile.write(sbuf, 0, sbuf.Length); + } + + private static void ProcessField(Type type, ClassFileWriter f, FieldInfo fi, Hashtable clashtable) + { + Modifiers access; + if(fi.IsPublic) + { + access = Modifiers.Public; + } + else + { + access = Modifiers.Protected; + } + object v = null; + if(fi.IsLiteral) + { + v = fi.GetValue(null); + if(v is Enum) + { + v = UnwrapEnum(v); + } + if(v is byte) + { + v = (int)(byte)v; + } + else if(v is char) + { + v = (int)(char)v; + } + else if(v is short) + { + v = (int)(short)v; + } + else if(v is bool) + { + v = ((bool)v) ? 1 : 0; + } + else if(v is int || v is long || v is float || v is double || v is string) + { + } + else + { + throw new NotImplementedException(v.GetType().FullName); + } + access |= Modifiers.Static | Modifiers.Final; + } + else + { + if(fi.IsInitOnly) + { + access |= Modifiers.Final; + } + if(fi.IsStatic) + { + access |= Modifiers.Static; + } + if(type.IsEnum) + { + // we don't want the value__ field + return; + } + } + string sig = SigType(fi.FieldType); + string key = fi.Name + sig; + if(clashtable.ContainsKey(key)) + { + // TODO instead of skipping, we should mangle the name + Console.Error.WriteLine("Skipping field " + type.FullName + "." + fi.Name + " (type " + sig + ") because it clashes"); + } + else + { + clashtable.Add(key, key); + f.AddField(access, fi.Name, sig, v); + } + } + + private static void ProcessMethod(Type type, ClassFileWriter f, MethodBase mb, Hashtable clashtable) + { + Modifiers access = 0; + if(!mb.IsAbstract) + { + access = Modifiers.Native; + } + if(mb.IsPublic) + { + access |= Modifiers.Public; + } + else + { + access |= Modifiers.Protected; + } + if(mb.IsFinal || !mb.IsVirtual) + { + access |= Modifiers.Final; + } + if(mb.IsStatic) + { + access |= Modifiers.Static; + } + if(mb.IsAbstract) + { + access |= Modifiers.Abstract; + } + // special case for delegate constructors! + if(mb.IsConstructor && type.IsSubclassOf(typeof(MulticastDelegate))) + { + access &= ~Modifiers.Final; + f.AddMethod(access, "<init>", "(L" + ClassName(type) + "$Method;)V"); + return; + } + // HACK the native signature is really is very lame way of storing the signature + // TODO only store it when it doesn't match the Java sig and split it into parts (instead of one giant string) + StringBuilder nativesig = new StringBuilder(); + StringBuilder sb = new StringBuilder(); + sb.Append('('); + ParameterInfo[] parameters = mb.GetParameters(); + string sep = ""; + for(int i = 0; i < parameters.Length; i++) + { + sb.Append(SigType(parameters[i].ParameterType)); + nativesig.Append(sep).Append(parameters[i].ParameterType.AssemblyQualifiedName); + sep = "|"; + } + sb.Append(')'); + if(mb.IsConstructor) + { + // HACK constructors may not be final in Java + access &= ~Modifiers.Final; + sb.Append('V'); + } + else + { + sb.Append(SigType(((MethodInfo)mb).ReturnType)); + } + string name = mb.IsConstructor ? "<init>" : mb.Name; + string sig = sb.ToString(); + string key = name + sig; + if(clashtable.ContainsKey(key)) + { + // TODO instead of skipping, we should mangle the name + Console.Error.WriteLine("Skipping method " + type.FullName + "." + name + sig + " because it clashes"); + } + else + { + clashtable.Add(key, key); + f.AddMethod(access, name, sig) + .AddAttribute(new StringAttribute(f.AddUtf8("IK.VM.NET.Sig"), f.AddUtf8(nativesig.ToString()))); + } + } +} diff --git a/netexp/netexp.build b/netexp/netexp.build new file mode 100644 index 00000000..14bda8cb --- /dev/null +++ b/netexp/netexp.build @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<project name="netexp" default="netexp"> + <target name="netexp"> + <csc target="exe" output="../bin/netexp.exe"> + <sources> + <includes name="*.cs" /> + </sources> + <references> + <includes name="../bin/classpath.dll" asis="true" /> + <includes name="../bin/IK.VM.NET.dll" asis="true" /> + </references> + </csc> + </target> +</project> diff --git a/netexp/netexp.csproj b/netexp/netexp.csproj new file mode 100644 index 00000000..52d354ce --- /dev/null +++ b/netexp/netexp.csproj @@ -0,0 +1,108 @@ +<VisualStudioProject> + <CSHARP + ProjectType = "Local" + ProductVersion = "7.0.9466" + SchemaVersion = "1.0" + ProjectGuid = "{D2A9434D-59E4-43E9-8D9C-332AA138CEAD}" + > + <Build> + <Settings + ApplicationIcon = "" + AssemblyKeyContainerName = "" + AssemblyName = "netexp" + AssemblyOriginatorKeyFile = "" + DefaultClientScript = "JScript" + DefaultHTMLPageLayout = "Grid" + DefaultTargetSchema = "IE50" + DelaySign = "false" + OutputType = "Exe" + RootNamespace = "netexp" + StartupObject = "" + > + <Config + Name = "Debug" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "DEBUG;TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "true" + Optimize = "false" + OutputPath = "bin\Debug\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + <Config + Name = "Release" + AllowUnsafeBlocks = "false" + BaseAddress = "285212672" + CheckForOverflowUnderflow = "false" + ConfigurationOverrideFile = "" + DefineConstants = "TRACE" + DocumentationFile = "" + DebugSymbols = "true" + FileAlignment = "4096" + IncrementalBuild = "false" + Optimize = "true" + OutputPath = "bin\Release\" + RegisterForComInterop = "false" + RemoveIntegerChecks = "false" + TreatWarningsAsErrors = "false" + WarningLevel = "4" + /> + </Settings> + <References> + <Reference + Name = "System" + AssemblyName = "System" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.dll" + /> + <Reference + Name = "System.Data" + AssemblyName = "System.Data" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Data.dll" + /> + <Reference + Name = "System.XML" + AssemblyName = "System.Xml" + HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll" + /> + <Reference + Name = "classpath" + AssemblyName = "classpath" + HintPath = "..\classpath\classpath.dll" + /> + <Reference + Name = "IK.VM.NET" + Project = "{F5C7B588-0403-4AF2-A4DE-5697DE21BC2C}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> + </References> + </Build> + <Files> + <Include> + <File + RelPath = "AssemblyInfo.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "ClassFileWriter.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "NetExp.cs" + SubType = "Code" + BuildAction = "Compile" + /> + </Include> + </Files> + </CSHARP> +</VisualStudioProject> + |