From 59d175f0205a15dec48c593c215d6b6f560e9097 Mon Sep 17 00:00:00 2001 From: jfrijters Date: Wed, 18 Dec 2002 16:00:25 +0000 Subject: [PATCH] Initial revision --- IK.VM.JNI/AssemblyInfo.cpp | 81 + IK.VM.JNI/IK.VM.JNI.vcproj | 144 + IK.VM.JNI/Stdafx.cpp | 5 + IK.VM.JNI/Stdafx.h | 9 + IK.VM.JNI/ik.vm.jni.build | 6 + IK.VM.JNI/jni.cpp | 178 + IK.VM.JNI/jni.h | 245 ++ IK.VM.JNI/jnienv.cpp | 1097 ++++++ IK.VM.JNI/jnienv.h | 440 +++ IK.VM.NET/AssemblyInfo.cs | 81 + IK.VM.NET/BigEndianBinaryReader.cs | 177 + IK.VM.NET/ByteCode.cs | 676 ++++ IK.VM.NET/ByteCodeHelper.cs | 72 + IK.VM.NET/ClassFile.cs | 1579 ++++++++ IK.VM.NET/ClassLoaderWrapper.cs | 900 +++++ IK.VM.NET/DoubleToString.cs | 539 +++ IK.VM.NET/ExceptionHelper.cs | 659 ++++ IK.VM.NET/IK.VM.NET.csproj | 192 + IK.VM.NET/JavaException.cs | 148 + IK.VM.NET/ObjectHelper.cs | 133 + IK.VM.NET/StringHelper.cs | 369 ++ IK.VM.NET/TypeWrapper.cs | 3253 +++++++++++++++++ IK.VM.NET/WeakHashtable.cs | 214 ++ IK.VM.NET/attributes.cs | 260 ++ IK.VM.NET/classpath.cs | 1636 +++++++++ IK.VM.NET/compiler.cs | 2450 +++++++++++++ IK.VM.NET/ik.vm.net.build | 14 + IK.VM.NET/map.xml | 446 +++ IK.VM.NET/profiler.cs | 79 + IK.VM.NET/remapper.cs | 387 ++ IK.VM.NET/verifier.cs | 2185 +++++++++++ IK.VM.NET/vm.cs | 195 + awt/AssemblyInfo.cs | 58 + awt/awt.build | 14 + awt/awt.csproj | 113 + awt/toolkit.cs | 850 +++++ bin/lib/security/classpath.security | 39 + classpath/System.Xml.jar | Bin 0 -> 121709 bytes classpath/System.jar | Bin 0 -> 288598 bytes classpath/allsources.lst | 1488 ++++++++ classpath/classpath.build | 33 + classpath/gnu/classpath/Configuration.java | 98 + .../java/net/protocol/ikvmres/Handler.java | 103 + classpath/ikvm/awt/NetToolkit.java | 316 ++ classpath/java/io/FileDescriptor.java | 249 ++ classpath/java/io/FileInputStream.java | 396 ++ classpath/java/io/FileOutputStream.java | 339 ++ classpath/java/io/RandomAccessFile.java | 1197 ++++++ classpath/java/lang/Thread.java | 1005 +++++ classpath/java/lang/VMClassLoader.java | 271 ++ classpath/java/lang/reflect/Constructor.java | 282 ++ classpath/java/lang/reflect/Field.java | 646 ++++ classpath/java/lang/reflect/Method.java | 376 ++ .../java/net/PlainDatagramSocketImpl.java | 312 ++ classpath/java/net/PlainSocketImpl.java | 327 ++ classpath/mscorlib.jar | Bin 0 -> 518720 bytes classpath/sun/misc/Ref.java | 66 + ikvm.build | 19 + ikvm.sln | 51 + ikvm/AssemblyInfo.cs | 81 + .../bin/Debug/lib/security/classpath.security | 39 + .../Release/lib/security/classpath.security | 39 + ikvm/ikvm.build | 14 + ikvm/ikvm.csproj | 103 + ikvm/starter.cs | 249 ++ ikvmc/AssemblyInfo.cs | 81 + ikvmc/Compiler.cs | 244 ++ ikvmc/bin/Release/native.txt | 4 + ikvmc/ikvmc.build | 14 + ikvmc/ikvmc.csproj | 103 + netexp/AssemblyInfo.cs | 81 + netexp/ClassFileWriter.cs | 632 ++++ netexp/NetExp.cs | 474 +++ netexp/netexp.build | 14 + netexp/netexp.csproj | 108 + 75 files changed, 29777 insertions(+) create mode 100644 IK.VM.JNI/AssemblyInfo.cpp create mode 100644 IK.VM.JNI/IK.VM.JNI.vcproj create mode 100644 IK.VM.JNI/Stdafx.cpp create mode 100644 IK.VM.JNI/Stdafx.h create mode 100644 IK.VM.JNI/ik.vm.jni.build create mode 100644 IK.VM.JNI/jni.cpp create mode 100644 IK.VM.JNI/jni.h create mode 100644 IK.VM.JNI/jnienv.cpp create mode 100644 IK.VM.JNI/jnienv.h create mode 100644 IK.VM.NET/AssemblyInfo.cs create mode 100644 IK.VM.NET/BigEndianBinaryReader.cs create mode 100644 IK.VM.NET/ByteCode.cs create mode 100644 IK.VM.NET/ByteCodeHelper.cs create mode 100644 IK.VM.NET/ClassFile.cs create mode 100644 IK.VM.NET/ClassLoaderWrapper.cs create mode 100644 IK.VM.NET/DoubleToString.cs create mode 100644 IK.VM.NET/ExceptionHelper.cs create mode 100644 IK.VM.NET/IK.VM.NET.csproj create mode 100644 IK.VM.NET/JavaException.cs create mode 100644 IK.VM.NET/ObjectHelper.cs create mode 100644 IK.VM.NET/StringHelper.cs create mode 100644 IK.VM.NET/TypeWrapper.cs create mode 100644 IK.VM.NET/WeakHashtable.cs create mode 100644 IK.VM.NET/attributes.cs create mode 100644 IK.VM.NET/classpath.cs create mode 100644 IK.VM.NET/compiler.cs create mode 100644 IK.VM.NET/ik.vm.net.build create mode 100644 IK.VM.NET/map.xml create mode 100644 IK.VM.NET/profiler.cs create mode 100644 IK.VM.NET/remapper.cs create mode 100644 IK.VM.NET/verifier.cs create mode 100644 IK.VM.NET/vm.cs create mode 100644 awt/AssemblyInfo.cs create mode 100644 awt/awt.build create mode 100644 awt/awt.csproj create mode 100644 awt/toolkit.cs create mode 100644 bin/lib/security/classpath.security create mode 100644 classpath/System.Xml.jar create mode 100644 classpath/System.jar create mode 100644 classpath/allsources.lst create mode 100644 classpath/classpath.build create mode 100644 classpath/gnu/classpath/Configuration.java create mode 100644 classpath/gnu/java/net/protocol/ikvmres/Handler.java create mode 100644 classpath/ikvm/awt/NetToolkit.java create mode 100644 classpath/java/io/FileDescriptor.java create mode 100644 classpath/java/io/FileInputStream.java create mode 100644 classpath/java/io/FileOutputStream.java create mode 100644 classpath/java/io/RandomAccessFile.java create mode 100644 classpath/java/lang/Thread.java create mode 100644 classpath/java/lang/VMClassLoader.java create mode 100644 classpath/java/lang/reflect/Constructor.java create mode 100644 classpath/java/lang/reflect/Field.java create mode 100644 classpath/java/lang/reflect/Method.java create mode 100644 classpath/java/net/PlainDatagramSocketImpl.java create mode 100644 classpath/java/net/PlainSocketImpl.java create mode 100644 classpath/mscorlib.jar create mode 100644 classpath/sun/misc/Ref.java create mode 100644 ikvm.build create mode 100644 ikvm.sln create mode 100644 ikvm/AssemblyInfo.cs create mode 100644 ikvm/bin/Debug/lib/security/classpath.security create mode 100644 ikvm/bin/Release/lib/security/classpath.security create mode 100644 ikvm/ikvm.build create mode 100644 ikvm/ikvm.csproj create mode 100644 ikvm/starter.cs create mode 100644 ikvmc/AssemblyInfo.cs create mode 100644 ikvmc/Compiler.cs create mode 100644 ikvmc/bin/Release/native.txt create mode 100644 ikvmc/ikvmc.build create mode 100644 ikvmc/ikvmc.csproj create mode 100644 netexp/AssemblyInfo.cs create mode 100644 netexp/ClassFileWriter.cs create mode 100644 netexp/NetExp.cs create mode 100644 netexp/netexp.build create mode 100644 netexp/netexp.csproj 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + +#include 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 @@ + + + + + + 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 +#include "jnienv.h" +#include "jni.h" + +#pragma managed + +#include + +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(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(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(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 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(__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 +#include + +#define DEBUG +#undef NDEBUG + +#include + +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(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, "", "(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(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(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(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(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(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(GCHandle::op_Explicit((IntPtr)fieldID->field).Target);\ + return __unbox(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(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(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(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(UnwrapRef(str)); + return s->Length; +} + +const jchar* JNIEnv::GetStringChars(jstring str, jboolean *isCopy) +{ + String* s = __try_cast(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(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(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(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(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(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\. 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 != "" && name != "")) + { + throw JavaException.ClassFormatError("{0} (Illegal method name \"{1}\")", Name, name); + } + if((name == "" || name == "") && !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 + if(Name == "") + { + access_flags &= Modifiers.Strictfp; + access_flags |= (Modifiers.Static | Modifiers.Private); + } + else + { + if((Name == "" && (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 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 = ""; + } + else if(methodName == ".cctor") + { + methodName = ""; + } + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 ? "" : "", 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(), "", "(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(), "", "(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 == "" && 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(), "", "(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 we need to inject one to call the base class + 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 == "") + { + method = typeBuilder.DefineConstructor(attribs, CallingConventions.Standard, args); + } + else if(m.Name == "" && 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(), "", 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(), "", 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 == "") + { + //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 == "") + { + 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 == "" && 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 == "") + { + 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 . 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 == "") + { + 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 == "") + { + 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 != "") + { + 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 == "") + { + 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 == "") + { + 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 + // 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 ) + 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 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 != ""); + // 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, "", "(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, "", "()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 @@ + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
\ 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 == "") + { + 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 == "") + { + throw new VerifyError("Must call initializers using invokespecial"); + } + if(cpi.Name == "") + { + 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 == "") + { + 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\. 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 @@ + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..4c7e5df400dd44896c11fe5fbfd85b658a070922 GIT binary patch literal 121709 zcmZsCQ(z?OvUMi5ZQIGjwr$(CH8Cf)Cz==?+fF97ZBCs0+5bNGo;&-Thwg`d>$R#@ z)vBsbK^g=U6$l6j66mL=E?pZj2mcia5YPq!5D?ktug;#%E~d5&9=0|N9u9^s77Q8+ zpWkwZZkFbTF7{6J#x{n|&hx3Z&a+~O0T1ClM2;JTN$3{4SLk5TXW!k0@*W0#{QvocmK%G}Cj~Lliq~Z+f-qT?mEE8eTIUODA9UUEQ_o@Ex z&#=N*kiVhEH7HUhu$Se8r`$5;_$E7Pc0Z79tzE60S)hWu_rzC33gP=wfg(VVvajbjJ7hbjY*q zkKM*hGQq8v$wiBt%8RW}=()>o_|s}NwRMZePHskfpLdYQDl1W%?^IY=U$6p+EZ+mV zH4v3W%Ta)UsRq1yXp^zT=W3wVk|Q@YZw6n90wZ|rS<6O7*Jf;BSvEw#DAdwou~wK> z%<{*HYiQ8Ey{g(Mvz*|3B;!s#uuR3J8L`aX3k=8D!FO1uyYwlwlvxAnC67v+Yda@e zCQBU`4xl6wkXhoc0P?j3DasS(`sE<{uSx2jg0;ryG2V5rwZ#PKu;q`g6-LqNeO(_z z00{#*C8Mf(&kaD(Lq?M$=z^Dx#(2#p0!JYCn_a(9i6!&cH6pu8y?ZE#@V8+{r?{09 zraoh*O7(*p1d*s+#f!EC;k$0#(4eY zRsGbx_*7C{4DH1%iy~(v=yfM90%{>_LcG0K`1bYG>akZq-npP+?Qy=7*Q_Nxl*Z=U ztRzbf2KO>vy{Q?Np6p-UkX+3^I)y8@cJSe-@(-)<5&>T~>U0x6={B5YTJI~R58a{o|9Q47GkZbF!Z&Qo9@4pUnzuR}q=GwQs)=za2 zP6==^^MTaIU#~h)F#F+LJo`-TzaYJFeg}R74Sc$Kjwc9yBjQILWZ8Zt;s@sJ)eLk7 zW`KOv*b)s)HCR4G+fxl(4E9*umJDVG^_B@(9FZPDM%pp&SB<-kXudPStcU=m9NQS-t#n$GhRG@wV z0>b@MG9)Dooi%K25Alk+-b=b{F4|9~E$&fF9H1tOv5j+PO(o=z=45)DE|cJ z|7z(cIFc@=PXCru*Y8tuy#k1X=6SD*sER&7bNvEAtH>gx;TSuCgc86+{ZX0@${1tx zmQA2~6Wroj zHKmzSsgKElkeHQQ^NNK-Z24kJYzmMOMJ1Y6OqPh&!@giu9KZ_RgK#ouaFQYtf9yrJ zIFzK}FfZ;Zu}bw5@7I|km8V%h$KL2JAdaik&r9?ApIu32R)XJTM+`P=6(vjV7pu_d zvz#`DB6jTtm3s$UbTW!?z}nuo!~j8hvneyk9>bu~0ubwZ`rUQORph;NEO8*qVyJpW zJFl8a2wS&)KWA|X)sbLSjHPtVOhMKblZ43aIz2qZ2%HRJiF60tM-_g7lBj~Sj_mG? z&7!I~PkAPYFXi z6B|oA^S|R%HMU2pPXJYP6Z5N8C5-#}KFE%XPz4~WLa18_wFhxc?le!Bf`N<$WTLv| z3gng2k-4%o3gW>D;h}S?`T+rQ3px~3taVmwZr8SQ5DeS0l{=O}YR4KfxZ8jL!{QVX z7#QOgtn%VmSaGIq6%zQALVo&C>(>XE;~ma`BAFH)&N%X@MFz8&Choo()@med$X7b| z=|WxV7On6qn+4$deV0*+@)22P>1wdH?3O2^HphEE3251Xagc!bS;LJyHeA_(WLJOP zi{aOpCVm%re1V;d>2joC2Vl_2c5YX{tD)S74u`$_YxX~3;)RS(efxw*@e?MzKVg#m zOk^2LJ5xzJGyA_m(v20A?h`-^G0$8n1q@^^dMJly($y~DJ0bTdREC5S>zSYo)_h$~ zGEpDbzuiK(5|3CFh~OxBNb|Jsyv&_qsM!Jz1(o~Rs+8tU;CT=t@9D-s8hQh$2}I=3 zyLG=X)hXUEZXXm`HbBA*Woh-wyIlRQ{G!;I+)BftsvP92el~ zhL4QdpduZQ3}>GtIpj_{;)YYk#oAQVJZs9mcF(4Cy;F!aINMfK2x<_K5+l{kyCnkb-_9IpHG_m>KLF;m^zI3e_WY&>ps$OXv@B5^w0lX3c z;QGlz*d_+DyzaXBpf(neY=+jc^~zb9yS5>>{Ex59)va?4(*FOzy)d_?m;DoWxKG@% z|BTmvg07@14J?S-QCQ|{2_DF6$BD; zVF?QM3AK8&?}d^3s|3=T8@XLY7Dw&LO7uK{9*%?!d4ig))-;`*&2=KSC0cj|l{JM( zvG{ap*JirF*!@mGbhdm^(pG`Hf~^9?3jZsjiF}&D4XR+P&N1=uRHRE<98i)28!qt@ zo&H85g^>fXPvIxtue+o6(1QLe*O}7*LhNXv$#zk+4f+S0p(h&bc;0L-aIkQ^q3~m zlBnQ0TU!9~&i_1Fq2&|NZ7<F; z6RzJ%rb@{?h&m$)hDVxsb*#%*T-32TpkYs0vWdsz+K&%wz8Z*(O(EU+CbmF)z~4-X zs7HePe?i0DDvk(wH@6{)<)`2!yd>h_Xvv&sL=C?yw(S6>FwO4>tH7SE>@CFf;C&+-QM;a zKE9u(=SzrnGpU0rJD|s@hHiiBNf)z%y}cWvO0P)48Ugal$0D<8<6Qvq(Zm6*ze$wk zV4c}F(7QUXeYFuzmHNZ4=jAnlVX`}`!I0{WnC8Mf^>_GPMNYxL@gC&z^ z5Y<-fn8zB-jltbL@WM8xFJ25}UziKGf&|UI%B_&gj=o~N|m#vMNb^f@+ow>sPw9i!yk{)FvSYh?H46Ry8X75SgA{oRpD z{vUh)zxB#AZbGV00WoB%0dNiBk59BCMzryxUn|N)RVT@cjy1{7z86v1J|ST(>0n;x z5r{7dbGM+NKHYh1cRW3As>!{WoLRvp0MeYM zCB8Ai(u5y^1|AT|wr#?L6h*=0dljNznf`q$ld)DN$wH|=mvL+jDWzqrJ-mMI6iU3g zY8P^dS-fb*bW(4$XwnpJVpL#YJD7&wdw&iF9Ap{crKuy|yKi#+y93v#rqz2(etnjM zqv!qOWu?BT)lrT^yET6t32^J7MgF(TeE6PjK1c|F^>&TgXL0xY@+WFR&gA*vpg=%M za6mwqf1>t>x#>`|b;f2#)Ekhqg>BqzXFJA`%-G9P=yt^&D*7?dA5q4HNGM0nA}Jow ztV4Z~#+_~Lelxa`?q|<`N8#Gq$_+2Kh2=iTMv&&+P(EK@Zcg_0c6sUFi|Pa73XG90 zEMB8`kT=9q?cHwVx&(^|jKMeUo3TAN$1=YFv4GCPC~A=3khrlbtKOJ#d|;T1bi~?z zY?+k-UtE&VSF7R_w;XGj{*>&(*3p_VQCweU@@u~rpt)gERYS!!zlP4f!hKd_`PMt` zW9^$=1z5zdw5i}TSsuaxMUP@D3{pMn%vUyC2DP&u+ebQq@TJ}`*IT^ecNs-WBe=1} zc&%6h5yxwPFr7 z2_<7W=1sY+-bkeJiy4f3p#f57G3f^{1UE%?o1*7S$}HkzG=|npgtV`nQdmbtDkGh( zQ6rRiqr~ zT!q;`M-V!oyP$TGD%tj(gdxXAT)}aeOX31xkb~O}$aq#AUVO*U;RC>mSo>)N0T0 z_Xq8pwZ@I7rPya@uoJO{`Yus5;p_#OWiU24LR!ShzP317Ch# z@K}jo$r_A^9Sr(vLQpE2`^JL7ZY4$peheA;XAk7RU)$KldW^WF(Lw8~@T!f9XI5A* z+vKU7gy%Ws&48*k(TEF5Do zXb!?rFgnz_JFPL(;v?MWi}I9z!;QK2D%uAsD#pylht_QeyI9SMIr)iuhzGphRomNW zoVBFcO!JQFF7rKKdAm*LES15Lx3GN&LCBEi9(+TBwDMM>pvn2BY>=9vk~(y`r|^+A zlFf}+By^$zdZNy@G%?2%g?(oK6by|MoH-=zGA2T|`c87XprC!OP$5^$dN(4w8SKmm zcausa%?MKeI2cL91>yV{9W6}Ue~YgNd0pUR)~f`nuX07!%oEKX!;Yr|cm1_LH`eH` z3!a_$)E_)oe8)F`sY~TbJzu;?SkUH8c;)2jC2ElppqWss}~CX@8qS#FBeHcJ1QU03*I%_5;lm z!Jpbeo8>uz_v!z6!2X*t5Oi^IvNUr2pQZ_*tZR?_*`q3}NWm(#&QA=rvz-dHZ`hObU8;dF*~pK9^`J+-OdMFcjL zFJdrD@btx*MmSZSujSPh(L;^_Rs$O{5J0+|fZ>r#{M|k$ZN16twAt8A3`DHkQd|f{ z^x!P1j)`nkEY-7ZnBiR!?s%-J}S@<3p* zcQOBI=#t*52FK0b!3IqNQQ5#TK?Kl+s9R#wkJ}{+A5}J;%>;aRFZF#cYYLk^@+vMH z%xL|9SotDR&!|?760#!xTa8UD;u>>-yPM=;0tK24k>Z{rgP)m82RkhrprTtwozW-+ zk|S7X>I9yyN@MIZ(7SqeQ};Is!s1Vq825++2kZxP#+Sd?<~iS&YO1IkR}@ok5bc?N z-?e+R5P1N>IKa(S2W&cFqt`xTiNMqZdtlW;Wqo@E-4tmr*jCk$KC%}y3|d68w|Ffc z++291gR{4O=En9{?|ayK2fZX@W4xsOK*T4zGWJO5mIoa2&K_WxGv!o?m*9N-W482W zTM(c8S;f6R>k{FA)}&9vY4X?9P1exiZ?6te{@Zhcphf8o^8Dk^H zAnphnOD%ujX(T&Ok=S9F?f)yRV#7tt*J@JNo49WeI+iC-S7%{B4H4L&Y&b!}l!d(* z(_Cz8GOVWECah+i0T|#yguhf@e)g->ljt_Is_YsLwdtZlP8#<^s*=e3^=EiBXQ?U`gX&t{uWakp0!A zhWEz`RYA+3u^}f*j@hfN%T#ZC*FSv$aFs?Ohw-yhr81)qtYcz-;&0 z6hgVuN($u79azVTGC8Zcomm!PH*F|mUpWKwzN$InJ97j&S4Qn%?W-1FN#q13pv6!d zOCT8Vt0BWqsl?2^^d~o#1QD%8m{k4GIX>1Tu}vb-YJ9%DaFhK5-`g2gDLt!%(+C4g zZN+3GMOCz~45~hZzNu2^Piym{GqsyWVlUsH;a+GoixxI=I#)M*>pS$#m~NbLzU`)^S0m z8>fPoFXLZNWpMm9#@NpGxfAW0hLLIV3Td4-y@#(7CtrRv*61U=Z^Ze{{A(Sl(XP~A z32+E?I=!8)t!^!zBZ{FgUyQ31%)wT5S|>lR z`~{A*iH#6^@fJR)y4q_RGb5eYb95e68X6D8a`+(_L$^`CbWo}x*^X)t!rtR`2P_;pOZW5 zza`=CsQLFG9$ySR;@^Y#x6dJb?s)sz9P0m$;)zr=!#P+;Kypi(^wO$xa;?MlBuoXV zNrcrHHS+Nl;_)MyxS%1i@Iq?l3U)6e|>>)~L9JWt6w`k1fnzair0FV0d(@pBa=VVEHjD%=D+S4SFQR*F#&~2uE>0 zx#YT8={p3;d@_bE%a`2;7v5i8eL=7^z`pf3Eg^gbPmU5$>4Y%4Orp84QhCw2rDX({ zE(~pDn5uD}J2yaQhBeC)F25h(;FHtXS*1v`z3_-tgESAZ&R)X zupOhv@s6L4@6G6!pph%CR@8pHAo`uT&XNT9{hMvmg}rIP?I(_f|B4~wJ)Cz?8|H(# z0xMq0`?uWN$gP=j)`pv+CT%Mw$~6evM||UfRm#}8`TIJgU^{iFt~ZWa`VaBjZHKt^ z?4CV7=(#4C?7Wb{(>AZh(}F-;tb&=oK=cX!SHi`q=S<&=dj+>p68ba*d5_nAqMMjf zJa{jJg|nphSV2kNISSXL96r7~$qyroGkS=G(5l&i%JFj63?l zM!nMg_Db(fH9Q4f(#My|&tXZOHzjiO{dr#3IDg74;;D^@=_miapIsT@fAaqq@5=vI zUjYg7^1Y0hgPTFjD&78=g?d+wWX&;z#6&t-_~8;Vjd|C3RP~dVO&K#-PgeM^Kz@|Q z?6AX=E3+?mo-6Oqjk)@l1m8nImV|1!vEx9YqMHJ+7ry}HYP;DSL#Bkl5Q93BKEgTO zz_uOb0%?Pk9&`wFj^0?r-3J*!zW@@^T4hel@XC|Z)hl5k2}VZbWZCb%)>&QawQ7l} ziRHM6B0z_jU9jNFE^`uV`*I|gu-G!2x7lK?DxZm%;DeA)=bsG+q*Uq?u=vetc7sY>d5*#7A=HJuq4s3bawzocEG{M8iQ1%Q1wx2-Cy) zLa0$k2_u=iJ~r3rRajJohQsXf0^;3^Nv6@rf!PwR?Ek2K6oSdYXLHl8sd`F0Op$m4 zT!Vn65#X`6=dMUdkSLuTXj-PO)Cx3EMlaa`ZLQove-Cam`Lw_3sYyq!dhrj61m0sx z{#u-&ep2+0jpDxyw~VE;%iq~qso3+^vMf6gI4GzzLa&_SbC|Ay6tNS6I2&L9OoT5Z zqpe13*HpZY{qz;VFMz;Q2QuHoUH*2XJ-wa%g9=#UBqb6)&KG-y7!pa2dVYht0EdxWSOO#7jx2*FxHl=1LfQ&^ z3Q`=+nzzmojmldGtge}HigiA2jnE|LR)6~j2IHLYz$kr6NC7=3$yg!~I4V^T%&CUV z_<&4GP*89wG#hkGo<_(r#@)0F*4zK?CDT{aG)46{#-Ay2b|Z3X{E2__C;mA9nIiVK zwx)Lff*+7DB?rueI9QTLPrbz-&ZM`nY8PY*MU31ldM6-Y1T45KsJzfx<)*FND)z5s zWn0vj?F!!e%qOnf%)hfESa_IZIBt+c^xKYhcjV$1>qx>p9jpMvGln1d5u{ZGH_?-ycJWue`3ID%L&JDhwuj!JGy5hcVg}d*SI*@gTUt>am#7q*k_A zRT3;_O*u?DSBrN$J&N0zD%2zgDsfY-_DN2%=3Gu`j%ij7EYiVbVxh?4Bc>l_5qW>oIHFTc{wFd;-J=&a$$ zHOXbMxOW+5|FV-#Yc>hzq@*DTF>guchZCKblE2T2{omhT;W`)*v>^v^DnkS3``uL~ zeH4<3f63L_-GK-(y3uA!L5a?se2x~3bqWhRAjL*kdpEQV-E~p>7ofxxs%x-7y_%+T zi12L@h6=qk?1x3hN`ilUm!n7&V#cRqLn|!XZ4b>1rC0p*Vx}5zz1mEcV6QQz?#yXf z+n6RxSHi;4(h!|zuexAj3@2!BV~5b=z%40SMFIpG?^-DqdsX#g#-P47+YJm&I2G_b zy7oi_8s?^AO^C0G^R*bb1W zNV=OC`0ZQ%WTLK2DAJR#@a8mVEULc?vfBKpI@5N^g|}LpO6{vwM@{v9Ia9WQ9-`KN$(k%6_Rx>K3p-p`{C3@XYN(eY5v(s#J-8H)t^Mr_#T*8KxbJYEk{ zAqh{E4JB{MXk@~cMnIuv(yg%%RDvz7e+3qplif_id;b0lrP2OSjMLw0?rP*`U$CoAj<5jnQBEZc2n7 zcKY&GP>L5(5XAv5lmkH-|N5#lY>3Bay^o#82gS}2-`pqj7H`mhr&s>Ry5+N+h?sufuzg-%{I8(`Bu?0U8ma(u96~xU-?6Z(Q+Q1wFBwFyf%? z<0pH$HKo+c0kW8ivB27faxPuDIxnB-N>X96OO}$;PLq^>t@;q}Tmc^jX5^pA$;m%S zdfdD|R@}nAVVF0(wXrTNDJvDu1r`{t1)gi8{$R*71_uC6DPu8xYE?0kKlP4^?P2H(e@m3qMnliXNk@p5?vrYkDj~^Njh3P><9plP>LY>I zRN$6=k0O#k@@w)El4O0D+UkCrrp0TLoQ}#(d9O}F9^Rivs4;FBH;byS+F^UyGm~>4 z7q#Oly!Wc7q`zy5x)L&6-q#7WlDb7ULtl_}`_Yo+#KHo`Oamlu08$m`>2@q$)=Qqf zi2e(FjdX+l8h%bqhhif{o5l`tll~fSN&A}bT9>2dCalO$yy)Rlb1mS7%uit_Osxx? zhkBpjBfDh&-pFl0x~Bhu_JWTlm1vYiZh&V(jwDStiP*?&7;|5YXP7OOH+ zAr{S?sr7Z#=R|#D?n2@4@O&)eM{rwsOZWTNkq}-fsR`$vg(5M#O>kfX z`J8lIIj;r`O2w_>G3CSHD?4*k$$cCcHw6;_1l8-vLfgWl^ixJO|i83LzKv_%ci)=*~xPb zXWVf)O2m4JCRl|LoAg{*W^&m-!VIwir~x(bI0XDv;rxR?5NKRA**AjwOkHGYqfY5y zMrF;zuNc5AXC0Q(BBpfty5DupT&(+x8GbW+G!Q8ZSD<1VY!73C10GR4^Ju#v!`Qsx zE-vrYOm>m^tpo?kgC(?cI{v!a`<1oYh7*u$Bd+~j^ed3{H1o?#9c+ps!? zPY>&paze9QM*ZWZsI`3rk~e z1j&P65fgxx@%x6C4eA zp>Pl}3ts#^C|~D0c&CAjghJ9&yI}Jdj694bd(|Fei03Z|z#{#x*Qi74K0e9D@%Bn}UEsOUzZbVz2!=R1w|6sf{V#U4 z!@B|)z@Aw9!F+Ho9v|sFwby?N4)JP=ZB zwUhdPdEw&|ct!3+*$cbs2=GTdE4+f%gL#B&N4sME{@q`3?E~wJyf6`}Jvjq(GpNcI zH}V2X2s3WjYg`cYrOzvVExDm?Z)mVARIbFMIFvSWBiWfmGx4?+=5ANqV^@^_KMHcm zx1Wmjp9T5m^J<6mKm5i2NU@lcq4~ch8IY(bk1hB)=Invi_%Ty}UID%t8;oyWsych6 zWP?I1iVT@SUio<{Fm}9AwcKVsrheOluocEUi64%Cr}(gc&~>}nqHktyZ&zJK1a)bc z*qy?E19C9`?%D2kaxH8bhL6&tE{gXi`-SL=>*9D=pB79KxMd-p^U=($^KRn(C;_SY zOjHa-_P{?*!nR16u` zP;YOS#f`USXN8aMiZ>7w4n@1}jL(h7w@y+8))mLK;XFQ7aL^Qj0PiZ{E%z+dT%mBTxPN=ND}Ee3@u2jUq5l+bH6z}Zuy${HRYn!-YaA!_s} z55$-56iZ|6l_dAlF!d};C#p33FTkgR4^rNKY*$-TQ19kcoP|5bPq)~oj|?}P2v~s$ zOw!J|!Ub7;(?!tpn0~gCyoPaFg}c`>6fsg!0$eNo@ZD^r24C*9dQ0MiSlOt)Q6n9v zvT(tSDQ646L8}#WRp%~5`+4|oLpVq!=Qx3X7u^BzD?|NZ&gcl5O|7l#P*@SC* zKS?k9B>g|7A)E7(lcnq9^%V@v1Ew0whGMqgK;;8z zTfGLRjkT!_(uBd-sTl~j@LB~t7VT!}c@)$)`{fFj!w(5f7x7h(eW;A;>^+v7 zYjLs?SZKZbpf3s>6|{TMMQqXP(W`-9_bh<;tH zL>s<0-RSeKBy&AGOjw&v1z%*G`ni5c{6#kf;YF0Zk0 z?W<3CKJeN$=i7|ryWM&!l&E z4W%*pufWlwXQXm=~iMbQRQgJe2KTnCh-cl z91oB&5mVIhzs7)kJGY-^J}?r}u3lu0%dVVxm6ZO5p{lkeF7E zbOF%l1Jf7QY(P1pXL`rSnByAlPL<8%DT5xGy6>7T#^O^}mZ{!*et6A!6L1 z%`uyttjNgm4)4;Ob^E1lKi`6we3(?2uiaf|$ZLMY{_~vv&?ybeiBqEzZPM*QOZ6pd zKpA0mv}GLAb@_}nkX}&xu3kc~k;!ve_~cbykHhm&wDqTF&-rm=7b5Rat@K<|mB!i@ znohMAo3^iDS19xHPX+C5ZVmGHD9p2oYJ}^t`^{6n^a#R(aB`sy+p-Ehrb_;Obnfm# zMNBrlwqYF%zyWP4w_g0NO3!;TV9eCaQM1E5Ng4RCmVqhXxsE_49oe*kLm_fDZV6$Uqh z)ApG5w4pcE?$$!N6E2D4Xn4M4>`CrP-iLaWQSsn z)DJsLy^w^=N_`A5GI)OBEWFbXrbx(zhOZ9uyTh1a;P`>zNyU@9HTW*Q{S;r4{Cgh- z_a;o4phx~Kt4{3<7@?~1AizwtY@b;^D=OQnoL z>}`&pUGV%ibN_ffP2DtrEY6qQYF%~ZWT6=zuNr2L-ZW2-EGE^2u4+N9`IZ8djc4oA1Gv=cKG;d%n{4T#i;<5mHjcanT zoR=t&gNDT-M*-cPUny%*zC&N*rl+_zJoj`Q)Y>tIQrDB%0_+qoYP~@}$xp*J4)aNU zA&Z<`$`+=9OQQ7 z9nbSMo9F&J*9QeddOHlUuAiO`&(61)BhJ|M{J;h~$82}Ut`wE|%_e>d=5*Ck&2c>2 z#4Tsi?-+MCh#xIIi`j83NCP}N$V8W>Cu(xEIJ7N&@Q`bWL3BfQOpBuWU&r=i7k8Rf z=7v>PS)pbF zlq;}nDE#nIN!3s~QYA|POV$Nw_mFwM>8F(X|Jp!s(1P%E15PdYOyG{Z8I+Ebhv2@V zMZfANKCXS+RfDP0`AtGl^g7VDpGVe>qcXxNl{}o zbjcov5&oe3M34LXIZd9Yn@;JB`N8&?JnI8p%^Tgu6054iAb+dyvob@wgdN9CyVd${ z&JfG8%xl3cjIi&UtbDcN+qfH&QzY4eFhN5`os=Ft2e?l7^aDBqIRRr#`huO**WwOz z2dBe{4kQBveH2tL^4s-E@6HiXGmn{9qT~9+n+cDNdsDW}D7U(M3H{0r34|TAznemNmfgdsNuIGUxzP^5liZXzxn2x^bp8F0 zw=~46b(Q>|YC#A4-$u})Hm3jX+UHYVl#vxbOSvixLFspUy27x?5SYj*3AkTO3FAlw zgU3uTdhTow+h=+*X3t|*#r%2vKabCVVca!wisK1(DEJ<-yzw`Ay)WON-`*%(dV$Z7 zFqy{43$5;?R0}RuP37~c=hF}%106S#;sMTHUZuH81TM&!5(JP>i@n==a>L0ClwYW< z+hC5U=SW0_?)N1q<>8bU0287_Wek|g zO0iat=OR8TR+iVf%!c?{Tf5-0#PL;D@ilT@Uh$dEOR~C;aw>A6swVlCe_iG!Cnd?G zRV3(HNoA@-oCSe#au|GYwY2(=K_pkM`a7qH|aZgs#@D1Oq+|hLOo<}gkdRL z2p%e)#DNR%-MDdSGy03B9qNS)Ve&{+u?#o4BjqDraB7bRwCoEox`y0Ah{g~5xY<%Y zsb7E-qOa(DEeDZrDrlzSi)hqV>>pLjaSs#fXO`*=DHVOW-6l)AvJ;_ba2hz+VTQ#c ztF^^0qWPkssyl;%+90U(G{&;npQ9JH^9naxe*9DaeP^g4IIz8Ldbzp$5h2!rYEfC<5{OG1PmMstEd#n>So zo{Jr*v^@{#3oai7PwWfl-$am(PvSMX*!&H**F5lX93M~~Bw6+xOM|43oQ4(=F!9o&Mg@g}Eh{S-GwL{LPI6&Ca|6SiX7aGa>2v*90KZ^S}e? zoS9N9j*Jg7+yHR=St9}7vma!7&M?^6QzXfTys0ir03o-iwP{>EMlOB~9&!aHZFNl> zO7o<+(zsO9qNyT-5mVwCTRlK%R1+-wzH;Ln%3n@8Wg$Z*MbFS>07YvdTT6LyRuTLb zR=ET;^LR6onpM&-KvyG(`@4^<50|ROps}u?R?y3EIa!q;?;-dNHmkFqe2!k(dtQHL z(W_e7J7OcuYpKd+;1DOjpda)U^!;JTD_1*Zt>VtQKbE$;>*$3g@GCDB#bmU=}j1)^+#XSGBB? zuwExx?m%1#W4S-Xo~|pQ80USs__K6nXFpAxZG5^Ye<7CkVA>#}HZx@c8iud^Dts!CE^*-}(xBDA|lbG5^*g1A2OqFme#sH77!ql;y)5Ux?_yVBtO`MHQG(9Bi| z7eta_NBj{eBC|%|{&TpAd%?R|aaU87S0}f^@eF2$0hZp$6EfvP_J`Q>!?nW)nddcw zxl1r<23zhf{hSod|3j^5bU4BHM$YT+riN((BOjS2}bp=}= zx{U;bFCac)aV*7sWJfz|3HJ5mawNo`@THmiLes;*pG13f1m9H#I zB`^3`imuVwBqE4cSmWW^CQ|d>E^I`h-&eImaC7oB3`e*Pgq}QKBUWcXlE_V_o6>H; z$%D945uaxc_4|%$tF8jvTVROnOpr4OfnS?~iKoHO&x+0sN};$&qM?5wUZlM8PM_}b zg}xZ(U6|{q3tE04!hX&j3C&(#uDXXvfa4Z+VQ}HDhk~9MdEgO*UxvOtLE-Y*1LKE0 zq9DRtQ~Y>Lyy8guK%v@DmdrIr_LsVmmwxUi<008TMD`=v27NixHx}Kh$r-_U{iX1s zB=`n;k^EYp|LDx_;PH{wz)yF~#4ynpw5K0jgs7KtqaS}oL$&b})n4|dTKa{Z^urpe zEB>lDxGCr`A89!Fz#X*lTUw=7|Ihn>Y?M@m&2JAswYc+BjQ{iE?l0H$e-rUM^;{W6 z9J4zY4K<1q9ISQ){iPs(P@1qEy;cI!iL4zGi7vcV5}l)YjmaG&)a7+-1&jYq+Wd7Z zckcLN!*s+BcwhL!?2J|4smu4bTkga9%j3f(K^`1_U_{<#Wh*`vMckO#jq37vqOpC< z7P2E35K%Lgv~>a-=;EEIt+Ax`lY|C@l9J*xT^<- zO;NSQyMjveEShQaCy)XDOW5$4)uvPD^B=R<4b<6~TALu+*%t^?@XZBtxrzg9GKpdG z*U#yy?^Mw11>+qp&Jge8R`NdYSV%>Sv$Ap+x{g=WZaOvw0!hE25# z-U7=K%wI|U*-f6hUc5;Ye!Nd{wlzi6YhJFw&u71mx>?a!uZ(|0vUVE&PCRYv_s(RY zt+Q$i6<^%VW#)RT`U#anvk&Zucvo}qG*^g@L5;uMZ5DFXl6hBe9@E_DDN>hW?x700 z&bi80ipxB(F0Aw8_JTdiW*1R6(w2r+%z=W>!-^Eqg(+LDlnWkhy1L(E_UjYvdR>s_ z|6}YM1M}XNe%sh~V>fARtFdj{wi+9at;V+5*tXNyHtv(X&wI|^-RHgce#rMcGqcuz zX4VhsFrK=VnLOXPn%8^7OP;`YFKo`nK?*rK zM;_}O2J2|9jwZmj6^rn+pMG%~3}aF&)uv4$3R=@T$BrXgxBl4j(TQHeaUjwbTcMp? zwjE}j%^01Irn=Mfl}NkDY3dxyE(CoB=BgX(vwk;4mC&TOT=gYa^kH8{QWS73Ny9OdV$AC-|81eMRloSt@yJ|aS5^0j@TS=ZOr4s zNTPe--g5P~vY5g31AA%21;>U^Ay*9NFsu19r>>Ju-GU~&r1`JUN_AxG?q=0`(P^BoJqR5{jC?3Nr}Id99G@zS{C@O7{`D)-Y>7Il7YU_`ai~xWX18ys7 zD#BKWJ(OJ*x5g%S$O?smFa(Q;j%dm&ibX-p`^mU+rPTR*Tlt~#5h(mbP%-;aRxVt_ zjPld?bH>Z->dVXCJn;4ga;xzwE7=Gf8 z9zqiZ%IikcB^BnEn#Fre1j+!)uL{TZi1LQhZYk3%12;sAjn~gQpw>f`(fm_YUBXFz z=sNAn*L_!&yx>~+Oo@lRm~9}#$Zh;?s9MI?smZTcrP9}62a+zS2i9G;U(ce~(jVSd zJ5rRc4i0yI>rCt>cYjwNLnB9ZfDlcG`HCRlEO@5 zvDBA=<1t|Jh?~`*p>w64L8i7AtC<=C?#sHPOrfCUvcZ6v!W_4L@JP8R^%MlNhEMmM zhIn01>U={6@rra57-Ze{75AotONis)D;apN9nm&tH$?MtO7g;HUHngwp$_Sg&b=aq!C(YW%tMmh^CrMev_ zYuSYuJM@&vd8{B$uaQqXQQBe2JIjOF;(0=?6s#+ z5pqYu=!=B!hr(gR%R`L~*A0w)#9%{vMWA84Wy$%y&7Mj~d0N;_WU}e#Z>l#JY5elE z6{p%jG5W1pHizn-hzWfz$+-lul-lQJC?&HaI;h^@e;?3P+iUsWwPvINP))*r90O5H zn?JW?e}$d>p$dYMmIoE;>%xS4C<*{FgCIg~fifYT6Zs(gvRt+hv27tanfo_Gi~8Ot zu%vxD)s-eT`A%y+LrX^o9@O{51&I=O0p6$58fX%(≶SBL7*&h!+Bi$Z6c7YkQ*I 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%0A{fpD^^3H}Uz(oK+DoxufeAA21Y( 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 literal 0 HcmV?d00001 diff --git a/classpath/System.jar b/classpath/System.jar new file mode 100644 index 0000000000000000000000000000000000000000..f4161828defce81984fe41c1a81a453bd61f0226 GIT binary patch literal 288598 zcmb5WWmKK(k~NAu!6CT2LvV-S?(QrI?(XjH?hxD|xVr~;4H6)@+gARtV@-#WTGIsvR1jcu*% zY;6EGPS&<204v7N07qj7b2}$cr@b@=YihuMm%g+mJBZ-0%M%K`{eLgr@y%F*ApEnu5Vv2 zVSPA*bRnQ^mHpX1Y{!oP)x@I;>jbcJHu%MZ-xQ<>P&%TW=7ga){U6)ZI_a%DWk>QgJ6LkLO>Q@VS7LDEnP92D6CAif~a!GLE+BCA*uhbCftMMx~ zX~WR;4H!4DVBOfo>B&OVV#{+KGYtj<;gE&0LE>Drw&^LFG@2>q7*P@DQu=W$q+RIC z^dAXdVx-SVQ5zpP`o^-e@jI+-yO&q#`6w61bs>~%s0y#x0#tk&q78PuYABqMK?0J! zUeR-R3L?{$t;*Tb8n8-yaT&r@!wj@L$tt^Jr=K5U+n{ zUB*r9ma$d&eu%HE#;R!EEnnL+p_XzWvV+LgYnB8LF-NRcH!$XbK6IEcQHdJMO-Ef0 zcd7z;PMQXL8MoGzx{vQL+h0zFAHb3@;*4jfPw|NO%hTfRmg!AGGxhz;SP#C{r)7L0 zcHdEsK(;hLNtoQqC)@8#RSa8E1+Zz+ZZ;#=Ur;fLSox$m){ISH?pA2_jh`YX{!cC z0jWhq1_DC!U-WVUxH&O80L+}N3>^S&b`AhXN1#YJGAaSg0B(Ov#F?hHh}tZ7kA#R+vZ&7GIQQ3-1GD^6OHl*U|3MAgw)GN-88tO<3Y zHj^_av~Dd%-l3Dht2d48nz>nH6Q<|eHxqOL%GYgWiJmY}yWune4W4qzw@_e$cFcO~ z*WEH!8&(tU1@b(^K zCn1PnRNxZ^eBr&^l9?I%k==H1c@R=5$P%58i00}Pf}7^0{2h0#$Oz*S)9|*mh<`8I zwZPbz=EndF#d!0N=EX(|7hYj9cECYsYt~xjtQXP8qJ;S#swKVpD%0mOGqGfkN9R(I za>@D@Px%VP+{g+1pK4eM%1fl>4dwAq>;ajmu;CvjcWR*frfXAlc@?r~5Yc4`b_9_e z)1^o6iq>Mp2|GCBRDv3}hT$PmVj+RsFL#p z5G#!IPLX}7MDmc(sB#13hNt9I%*cy#V!e7|ronz=mi#c*Ak;zti(P#q)GBO&Qy=Kp zAs~JAli=`HAz~4xSrzKo1Q!F; z>M0x2&N3-y>g9zWp?rF_ec*qYv)I~|TgZKFo%k7pq@`M!h?^3=~2PK>PVV;8Sp3-69DeITJg$gFo4Ou73$_@XB+G^t+}-O&RJ}0ksST*Z^_=@0uoS=w$pw)Wy)s+0e<>flLuobEdaw;aA5G10iwoq2b4Dn#mKNiS%xBg z{|BG`J+uxT6A9hqq(&Facr-@FTzOnZW`(J-Trmqt3j1ht6CI7YG+*?J>5o}x6pGf$ zVyYsk!C!HXM&93oe7^p?nh08;dxGrD5!avE{Fx}V6lDJu{X~{H+E7H}h5Xof#=}bn zKL8v_cZBq2<{a~-8Qf_L*`L)5{P!T}I5Bm%0RwOc6nEDDzd`u>ukTdRwVh)^@BAeu zJ+GI}UXB|UGPPQVr^eIiZP z85YKWX)eWvqH23$S)Pj75L3EV*r35t23q&2b))okmJRh`5jS=vFM zyDX8Ze2}G;ILLz7=v8+bG zMpaO6DluuoVXW3YX3=0l`I~laO3!t%b1bNL0g^sfC}{bn*Skh-g_;X)jhKlnrY|gQ zg+Hae{CTmS;=F`^MhBRrSRFEV5_@Bbi6Y_|c@YYxNJt7vE@e;=Mlke{af)niCfk$)1o_OZw**Fgs)A7^8n(s)L_5RI8Say3$rSp{a z+u$NTB6D6S@=rg)n?fY%`O4+#a!c=bd2K}FW0t@i&iGLzEzs%Z3s)89%ikOriREthfH)VELJZpMpB?u#+1<7Tp?XZZKz(KKp%IL6! zt@ceW)G0@K%ZR^X!7KN%8m)*#zD8Oq`k>Fbt3($?Ts*010dmyC)i?KCtld)^x(WYk zi;#%Uhiop+Jogia6lq79ia<1G%#bV^W7a<2YQZQ@)wfU(7)w5ut^T6q(IXjJZ2dWE z;WDMyklMJ=D?w%Iz*<8dUi4vysY6YFsbcD%!a*y7h^Ol*?N??+NLtsYOqDLkHsWI# z3`@t9@!v^)@y9T5kX|Wdtlk+XuGuo7S4h6xXha|wR@*Jdz}V96Jb=AFIH)xxzH?D^ zgkZaYJ<{&cBpFT-cx)D6yO~}(YNYi=)_w|3H3Y!b^sRoZ(eAj+^Fh;#x|Fu$qpw`q z`W<_o7uAs^AodtgARt`-JNEzRp+$g`xVe?#U(ie1nA-jg|DcNOU+`yJf2W8mX`E4_ z(myzmFg>G5QcAv=DTl6@QIky)As588tkubCYHAk?pNIF21Q+nj?MD*Sr7ROkAn=gr zDRs9s&E1@cxnIg96d;DK*@pBlKa~o$);1Jh0edAg0pqC?qz(*|en#*<#ru5536uUk zYP|b1BoRak<1&Y7Xw>taz|3Ahl(myF*+u8(Tq$Ce{PfdPsED^m zVi({BNmC3>R7-8G=tGqnekgZ}eOwR?ipE|VRj=?kr>vIYHojpCinpkqrL}DXz3s{5 zf!r+{x2-D-T?f2d-W|+o#(8M)4S(IY#4gi{E<#aa7Jh4YFml3$L*){5I5~rbN*M-t&>82e^R^~j zOh(q$ZoEuitV_+tDvj4~USC)xRSdgGM~PoqzcR?K%pVWWd}Hh)&(s3rS_u z^M59Ngs#olVT&e=$<`<2dH+K9?nc)QDlc{=zoQwFd-A0Vi(cmsHO5`%(Q9hX~DBG7rnX*u3(K zVm|kvmvB3l#ik6!mb277@t!SL|D2G?E=D9|?vpfmCp$SkKzT+uI42K4LM0o>DV3>C zq9%zQwPOYDxKFMGOVe+_`7P&Sg+4b%_m?RwGmIJeK{e*}voNn-VN^__`?JQz#sAUh+}Um$-XTAvDM%9F3=#FbI&)g|YWbTzBk zRiped%45$zzuD#h0v?J<#c@^MbMj;!m*d^j89kKvQ7IQ{b#RpAU2{P3LQEcE?ujCC zVLUKe6B3b<$`JnX<0%Z_^yjW8PI7IHJ>-JG!$&r}mtFi$YpgYn-_x=MkM*?|7-Uvp z2lVNGPs@J<`o9bExXm2U2b-Ir2ySjHRyZ1~bs%GPfh|s=f=3rkUciznphJd{U8h|- zGoiqVRg8XM*0=!mLjSoeiJt)ZlVrkT$~(*R*WP_@Zg(C8qhTmW^qj_C&w^W(Zm?lr zdf!QYA7@Na*oFvsqM@=wj6Zt_3o_VA>M5-w7h1mOV)v&QmL!!cRoPI9 zZBNS55?vA5HPWApK^apDJd0GDbsk^q!-g1czf>K#&pTF{_Bj{S5;M(4*bhI&-MFLU z8LlR)Q{lY6dN*U8cCPS!?G!sd1NC1JnWv9OP++FV@W*nwbz8SmK#$h-MOEgIOr+pe zlmLOT3i;aoU{8tB8=OG$6mke5<#J2vpFEMwwW?$Rrd`r^`UT<80dpZKe8xRT-k^*+ zRIFvE*_OOhJ)+pTdzJLUn73tpUn7t+E<*hEapH#yb=ORj`ZWcII1`Qh&@ zqL$w?lz?9{bqAcK@&j?D{0`zW-34i2i|G|%eG0p#bn& z=b?q-ePoCr#cQd~iZ_ukeK5g$0P&)NT9M0M);T8b(&*Mb?xw9$>wbTEfz)(^YKL)o z41;%2L@}YFiCXT8#K!*cacBLKs-`haHiSM`sqe}UxiV|V_S>$Jg*v~>bQt+noM^2H zo@I8)UHwX zA{$ko=`4TpVu04 z&h2RXjNYFW+yPYUR%N7Ao!9&5sGHYmGR(5v{ad;aj(fgxYytca4o%7WoVQSF=nBn= z#E6Cu{=+Ey&b%=D3_KF14RjoWkLrC!>il+)`~zmqpr-0NEMIg3mD~Y)kNWmZy$MDhKh4e?_L#pfPW?K3 zL6-lHJ`Zs&(||V_T=>}iyD$}JfS2h3g=rqh#Q#GD_{V(uH~*>Qg*l zKHhyW=L{r;p!7L%tXJUDbVx>_&4VJ9jz!sO(H&L~h2l8m63k5Im_Y2nD?&-KeRks> zDBOgQ7mg(|@UR+0GaTF-Sk>0H@Aw>#noJ@K8~K#Yy=YgMZ?2(SWFzR_BQZ=2Y6hiR z)NUYV-5}o={w$LWnh${|Qcp`8vL>}0e(GIn^#h6I}0o#03 z)!%~36+&JKb^HVMb%NpWsyS)PWt6=C&WY zR`_x%hJtMPBjJ5JXWL;r`-agv4+b*G!EV4~_|&puyzMv)*@l}d!)(SmZTni28R&*iZJ4Zv@1u`4_{?+ zlQ@L}x3EgxXM{NhW?hWX*H(2gk29__QlCK+Pa%LsG{Lbja%a7=5SqeoIXM@Yw~OnE6!U~CzvGZG!3gVBT!wfJ7f^M6XFMUCju?22L=sVr>SqOvm5KIt@X=>agJ{1 zg!;AbAK0^Cy$J*Zu{Q%|DD(f`I{c%fm;c8e&GGL2nr=ziqv zme34GN<|jvsVE3qt5nOnvTGE{?Q?$hZtC0yb)y>K1T;+rC!go0`}#cZU7zzLZKKCv zXPGoiY!{i^S!Cc8@?zqYmJQJ*hL2@{N7+5a;lN`UAgFagx6$OPe?nq_3&WyQf*c1t zKL0H5neN^^JgS%`2oef6DLF*~Eo|PV#UWjYO@g8<>ayhr4Hsf@gwYF!hFLAoy$||O zo|-)4cY~A&d&Rq>9ViwLW77tcyW%SEremM61Dtgh4(H8`A%4RYK~)g5PI&h zPs)MbUj>tKEZOPk&}uk4q&f~RxGzKcop)tJTf%%fJ7hcdF64K0L+``x@%f3kogTir z-3;#f9>f2xy6vmvEH^+bS%A25{2wv>yY`D28Uu|%_rH+^OpQzRD1r zl_JZd8dX8gMXSW^27fZ~MGc9=ksoW7sK4sec>v*0N^6c}$XXD%m>zz4@$n9XRJRL2 zE|)X+xMyoxO;yRrkhWM#NGiw4e+iPZKqG}1h1NR~bNN8j_ZZ=8*L_fMqkn~gu?h;m zb86pPPkF~Huen+UyW`lppt*WNw{m>lIrVmu)6R3b9<^VB9!h7Rhz>z_vF@pCNlJ}r z(zxGr*p<;y9RcZVel12aMTy4GFvPL*sjXz&C4#A=;&ZVX%;F#*#IEh#PBR(CAl3kc z-3n?J3i<=+&ets{yYp@C@rDF_K(%>y)tan9!heRUDe zjLG!(k(^(A2g&C_$n+LiYP#*_fsY?_SP*(~3| zey8DW&Mns(FluPP=#l>G=!yQnh1=f|<}uWJ~i0iG3_91c_f%gpsBPgI&h?iG75Pi~I2i`-qA&^&lw;vv~EOI3)$0{NRWR zB8lRM3592=TFAax>A?y`dM0HCMP>%J7EWdnj)n#cOYMru0U%|WnT5&nO9(T|1&Iz? zR{GRPPfy-R&p=Nf2w0ThbS)yKb|b5DIo+T!?_O`%fkJ zpP41(VCHNMJYY})m;xLCHpYNIgH;v3B?TsgJ~aK+{Jk-#4WXGQ*xCian#clkO+pus zeoiuU+bDB3HFj!6c4MM@1Hz~Bsa;G`RDE*Nwad{b^fAQU+tbIN?zEQz6lano*#Ace zT2_$%!lDOFC#PV3^#vw592QTka=7Avh$4uv!C`8)D4BGg(`Ip@^y&%1u^qc8n0%m9 zD8;EJ=aRrL=4dWk9MUAoLe)ZcXS}tNX7fg4{XtZ1Fk|v`Tbx(7Q^%Ib21B>l0ydjZ z8XHxF^BGh8tD&-BkH!q&xQXMcRKy`7^e>^-NkJf*X|Y^S{WbZ~a@Z6S;LoXqY6JqN zwM$Sw+faG)ztxxkkQQWfB{B1aSLTEbDwJNa#R_L#(Ornaxg_M|d}TmWZl%>4$9pV( zF7dx|Q;VQT!C$jj(M>rwC8<5S{?5^&-IG@U5Q7{<5D29dtGR~(aw`raKGK6?$GcEkyifiMB z= zsDP=&U>oa75HA6zc$_TfM|ft7;??dG5r&X*G;{)Td=rSdfORt#q>zMlaHZt%0~WL! z0L&;i5f?7A1FKl;+f(kU+xOdc5{Z4Z#@e}~9m!C^NKMDQp{N;;$fSJ{*vCfFN;>2VGw1rL{SEv3;9L*%Qs|Wsq{E0 z^U>z$PnV;-4US_GwFJ1b3%~ zT|;N&#u;0+9-Lxogs~sFV+^&Ki6HJ$ix^9SV99W0SG$T`os-MoU|>5MDcDr@_12Wz zJA&zCl>ueZGCC^oD*1y21qz}m7CFZr2o;zf40AG?9v5vT`5d$AXP{aOHm#M(H{T`m zm3YN|Gzt@W*l2`!3K3;ZHo=k|BsN9ygA&;#cWRCvRiYtdo%=8lKd2Y!so(wNeQORX z<8PBMANtmM&R_Tob;QIGwBUl}P<%xwZeDn-S3pXVh+*WC{*#jv3?9^F`AW%XS(qj< zII#w}J;{sqfI&9n9F#cma@O_(f^GaKGNM*w`F{2yQ_jiD52sUm>ya`YM}N3+UlXzx zFo6)k0dek*fyfYpcYGNi^9+*+~TYXjvsh zIy!9ZWh<6z7U|QE*lBE^`afd3p~S#W$(WfKfV464HIZ#nvNE#eQ8Dvz#K#LMip)Vr zrXovK`e#JUexf!W?&a2wktyOUM34#bkUtxfKOQ)e%U1s(&3-A>iqbnav-u?Nrj022(`Np1msk zUhi>gcq?hxUTtNn1ZBh@gc-kU`M_+0<*a_U864!JiS+9CCiqU#R#8L}z_{a`&Xwh8 zc7SkwdwmHj#2Km_l#~V|S!lI%tT>GgpruI(-cH$wvlfk`@Arc-vviVYr5Ae-Z~CZg+k z#VxW~HCuFyFv8#r7wpoN**$?V3C~g;0h5nw>b&OOoBO@%6&^N%#yuQ3rCLS=x#Bm_d1B!Y(eoe)BRwv z0*8_2%|SvK#HGiV*m=n?>}y3B4Z2?^^=9rKxQrmeuCy1}hn+7mXmErz7nI{2V=kqd zM_9iX%|ycvjGVuk7vL7?w?n=EYKwu_d4_+T8UQK&2ObM5Ykw(FHU=c4-|ogh=x$`( zd@~eExRFsDbYP1{Uj;Q_Kx!q!#ggoD-0K4T1GR9e z0|fE19|qWo+?wHXf^$WTa|JopqkB(UiGJ#@IF@Ad_3>yJWUva*eSWbp!I!Ues`5p1 zN}}bBw())yPs9o0e2v^@3Oi>@51z{bM+oBo~*em!T?Hnq7E~Kz%w+M;_s?pb$a^cvqT^eQ%b|24lmUV z7jV`^+$q0ot(`htT);>9TGaQ+1y95uebmlH6n^NJiwYv<=xkNmH1f!@v#rLdn&LKI z1|P?tBC~Z&SIlKoHR5g5%NE`d+8P)5pMKY81#_Tzttvj4m1ER;+$7wo@_L?D%MqMM z^Rc=}LbnDRMQXgSh~@?-w#=okr>x@|SPLSH>Y;Me59)3~`FM!503lAwbi7x_5c9;j zIB1{XXP$PvM@afeginK&um$MkV%E8r?WATMvpkT=T2hSX0287%-0A&dgqX2_~g6Y7~r+l=gz z@p@bLoywTQ$$)4Gd{z3nQGV;?snOfh*TJ;UUvh}^WJ^Sxq`pvjEO--{P(m*aBL&0#Ab10^^;FHCOGZKOwtNpR$f@QzmNI67MR_OJ&$Y~xSpCFa^;MVKIH18FxRz2 z@s~tka-TFCw^#2i#{k?kD;v;xz*w`N>Hr;+Q1<9ndtWI93Xc%jIbNd;_I939oHT)_p&NloIXRL)?`8%M>I?0G zG_e9E`oCB7zt84B1Auq)4*%Ha|DEei3Ddw_M<3P~0`>Kix#X7@W@#ZNLG20hg9a~v z4T2{w!JT9|Wp8k%AbSH4ynypQL1VWQ=fxuE#2Q;Uco7H`XdAw+wL+O zQMa*@kPa*1cvZ6=WMoWBBp#u+r=`c>LkAJ@4asZdFS93UTgy0MhEfI0J4^rC8p*E6 zS2Q+%6vY;*v{~-RWLfD4USCiD@|IBXP zM09qjR+jMNlkmNUCwjSnuSJ56^zK?kP_L8!y&6a3&)2R1vD*Nq_&>RQ|5Bv?3A_I> z%ZqnZH#s@f9q>YDQs4qqbUsvEDdGMQjEplziVtqkc3%=KlJa8w-~|#BHXZnpa{DV6 zk+zhozKd@=53BF%QiFhx@AC)pFM`=Z($PsnhI6T)P!7vZ=Ih_NKRkyIS>KQ{-Z@bP z(-y+KI;lYo<>dswYO75i$D8H!u_S$@yTot5|L$|!Vd*L!Ia z*t#f!Az-G{uhp$t57*(3+a*d+GP^)faNm)PyCvQf&TRE6s{4GNJ+`%SvwU0iuxxlm z&KA!^z|0LInU}{-nphwR{Hd~|$7uk9usDeoEbE?iV3GFhPC9%jaQMmieWOG6D}qAz zVN#qw;A&yL92@UtVItT@pf)Q13V;{<+6t?jUK=tB-}-n>mW=#+(UO=oK2~bu+ZHh97I3kE%!CxI+H$FT7c>EW+#HD6C;G<~^Ew;Us0GXeDK&X8rubu8S1qt4B2z zgW{LB?PSvD6Iep-j2jD35etbhs3qWw(@OP%iSGY^4;iL~A6z?666aTypW~lXSLg4a z;~x{?Xf7vdu2^oZXJIa>XcQA8ENHGIXIk%S0g&)UHfPfHzAcH`MK-2?sgAbfRz)8U=D1I_fy-7xgb3j?;W*-1qA|*MBWC zg*g4eA%+KtF$qk$#jsqX4NHWV>Psq0969<*EU-;)0VYPmpG0Cogzdf=^f!`)5#`Sp zEk-+OTT98B1)*>vy2cJPE)BW89wWySMv8U?%cf8P$Ti1)m}n-5b{QejtqqXlucefI z`24nMgVZR8$Rk@Ad#lv0pm3f2qS>6rGkE2WBJ`j%$94iIq#)6PJV4pR)<^&*sDSyi z+oMgv%k4>DMccq$vqaB(ZFIHkg*BC<@e7i6ERxpwif|n2cmCH>50+WN$LK`ez_;Ah zowRIjpMnMZ0eEA&XFle8ADT~+rU}GCz=fV~8fOs^2(ba7_?9;W=aX6iI;6qEpd~>} zKG^^<^B|=mK09w#k(*=8R>oe7p;^dQFdx$S+?T=VKK_uD)NHqn=R8ODMJ~Mn4)ISU#v38(V-+Rw&>Qk-6kW2rr z6Jf&>Q7A_lQ}_+z&f22VNVm6A@so9M?it;WYuXgqeGZDYh6Yv-ZUOHl{__sN<+CGL zuwDW(Q9szWNZw+qFfPqRQVAxbYFjhwy1C6dce5hS4y;$Y>6A7^MCuE)d>YyvdX$Ng zas_^ieO)QrJ4u!c%qbEY1p>#4LhX4<38y2iDA?rWSWgACTlx#CPu=G{uii=d>J^Pv z;dDWO?os%FDh&e(VC<`gXgNbYO(kjj^00Wx?LO@!qdMu#vRmcVwN7h{{#l^WJX4b- zdo5zkesJbY2>tTV3IU!V2)S1}#(hj)RJF&Z=o0VJEaJ56P0peB4dOJ%Yzyy}*w&>N zmiP7}!OyNk=%dga!hAx{53};Tgaxs@0-}VTJ|RB89wDEK57i%In-ZrZ-~Y&CA7>KB zd>|XFfmM?7U$a5P(9!8nzidGTn7zQ24m{ig$lyIG#o!mEgw+%f5}|bvcyJujpepJg zt142`t46?SRp%Sq6Rc0v<)-i$sjNvpnG5TEqgjcg>92|5rE%Ja9f&)Wfyx}fUNaG{ z?E0rwVgKa-la=Dqm~eV&wi_ZM2xx*v#Rx*7{rZWenuimm=$c#ZkN%3zUT$ObINm%2 z8OBfOF=fVyT_cok@$wX+@mO3XGYhBH;n&>1R!fTPCuCdl%N(%pEyUnY9>pj~i+|jh zlVr*mUeZMp*;W56_xA6Z9y)>P>yIig4*)?%Ux^4s%{Zs;8m3qTWL+B}I8M!uF-O{{AK4$0y;r(2ogDjj0fGNpjt6vI}cm?oXfFpOqI-e1R z!jP8J*!84t*dE?aMw$I0cv}|+OEJjkiCVfMV|Se3>SrxYZ+Cw5R@ZXexhkaEjHjT{ zDou>vHY23WGG)5>+(bM-;U6xY>JxCx_T$XXAI4mQ z4vz{9KC-m?Dw}p2`!Bakh%LMOz0UKZ|Xgx#brTF|sFrWVcXSWx|7v7$x?-&_L)Gf_~ z2z)Dd*=$=p)f-C8pi!D1Y7Zqx!40*9+rjx6Z}aKT)nfq3CyhBE5A=aN_%~}E;5*X) zkWcw6ymv6`DncKN??&Er3D)00D0L!Hjzyhx9oHXPz2yBGwb?wca z_|AMCNf{N7#;An#LzVXZnW@ZX-%C`B39lBF(?anA&AV(k_?HQ{md-p5L9p?DIW4V} zJ@KsIzPCpLZb;}0{p$+CSL@G~7s~&dcE3p$C4RI7L&Nus2Uw9$Qr#@oDW^VDSxhdd z+79ZwcXg=K$Z9jC)_e-2I^0>XaJ0!ST2mq|l0;f>;*M_5T*h15GCL>t zmM{0TsNWXHJr#-$RTNvNm9aL#t^_Rb^pRp4wMahRW{wyA2rPpn!;et!{hhHF|C=Yx8^QYY1L$QxXs0;%Gapppd-f*7 zebS%B;NVb+)C5Qm4`6FR^lxOs*80EvdEjG(zs`|!AR)Z`3jAi3JClo=$-^YjkpiFt zYl1?+!_%6rEb2Dcygy350EQmGUlfO|&5@HyPXPy+k1W@dr(Qmt-hy~{hPb`eJPv$T z(%kslpiRXT4YC$%X>QblFfOM1wvEJ#n%pr&p0k+k>btkCBs7JTQQP@wSA%{_1;)4w zXy5GRw%eBNWuo^L|6=UnCFb}e0|=^nVT=ucsiA{7hs<$8Q{ zOSI4${Nx ztz*!R0IvIL@YrKQ8?JNz47Cd9X~tGoImM=!N*iZ7PM5ROFOBmpYOz!A(oD*kg^`|@|IB{eU#NDI-&SWbvTVn zyRk_N+{tj%+CKt~ni}P!`)LB^>-zER1Bl8nS7GW&-+RrlG)euu2O4P zU2Fb0tb+ESl{RXx(3QI2HUHqZHB*Vnp6x(0?FQSS9XZ~Hxb4zmY~!GSf8S6WEj*!neb z>TAaP{JOMcs}P|2l~qHPw3yjCgr!fSL^fAUzl0iYV;1ymW*}8EAU~#<3fx$MQRj`J z;$VJXGOrea97@`2?X1&IpZ}z7W0s%g;XC$o>m!Q%50jUB#TYhb*GdA@2+UQ>>ceKO zoI{V8A4q6?6SeeqUmJ+*E0_9xP(#-^e~uwht-el98j!AkYiV9+i`;yrhl)fpMmZ5j zka8&;&f`{H3WrAU>uq~|=}x#Lik+*ym+2U49wP$vv_15`90R%KkpsnEX1D!V2Dmtg zRbz0n+^AE+7^4e+c0?>XipM~0MGFJH zwHr(ohOSdMC~z&i_iB}&#a?GV#mvEaK4fkj@11?S12B-hy!_-V4uTI(FB$z%a)p+0 z*zlLtfEMSg>Jjso)o_9iv>FJm(*uB3L-Y1uRznu;Usl5*e3zgX3Im2$x*K4~_lLp@ zj*fpt=xI&wZGbKER#v#Hybw0#A2x8e_cZo%xBWn?|q+I5(oTV>qj_J!1n+U6H{|tg^9V$h_ z$2Cw2lIHYsUWyNeHb`@Z4>3Y(=Im!7Yn(ML6Re*Yv>+C1z82}Bji_6af)%@Gf#Rcq zq}xU97TZ3Lkgkc!Z5L^^YunFH7zIa-UYj&agFUa@p>-KW`A6!7I^F<^qKp|F-qgQM zkvf%49zlCN2@ZVv$OwwWx=l#Ygvub?OFbcua)G@d%$>+rP%(6&s}ky_^P=YvnzB6xU&ouQ!0_fh!W*o8>(wZ~14!IZB^mWE00nt_EW+6ZS)c&2ODs-nBjc#1fv( zD#O{$F+`kj_e0bX))^Y5Xo=YJJ6f!gf?)N?{^S>GS$tpYH4JTgX+%&ot}nTVarx!! zvOZHU2UURxKvGLMW*NHSx|r^^3r)z4zfs>I{*I%7A?3?95Jxf~j->y(LjGlF$O5d5 z{_{;omC8SEw6n>R6yWk3^XaY4vvZ>MxnqR$V3c5k2}t-OlBhUTswT%++&Yt8b*W3= z7(W2tIjam-qOHJc@Z~+!Uz=`5nQopw{o0zOPuixPhp`&VSBYBUvma%EtZ}_KMe=P> z9@523^+#T&%6oCDNh=*wAW=9bSPN8sJ{8|u42P)pq~2QWAZ6yhPDo*f(;o60WIfX z26me*IJ%C5tTEbt3<eWO##GYx&vV|l>=>Hx!5{A<{SMm%5m>hIjri6nEfkX@TxD zTc~fcW3^$nS6l^L1C|$HDWw;1Dl8ol59F1ygZjNE2khy&+4{VFe{ zR;CYVyVC~y_Ki8ki+y~*6XfuC`3odadV_!jA^+C|`I{X7{BHoNRQ}<7@#`7>XpXU8 zfjq*Yh~rkNmnn`aLl0_`lF?RdQuEGs;EICbsm3cU@QsG{0TF;RELD{d{>Dp(R40&q z&cmM@dv-3+tGCQI2+c^-D8@4_%vZH9GjHYVQxAT0Moc zlveQ3)NYhV$J&5L@Qn2+H{r2_h;=BK<{W&!MoPQMgOh&6>mGsDda&pmvpv?sPEoEg zMzw7RL9eYUixt%VYjn8qK_%XV-99DiwWA!T(!`he)i9A@bxQfWhg7{XGTP$BT3I~& z!apcJzP^y~x;jTPU4!S(X>aeTn+W7wp1f@{c5n9g6OPx{zAOsGFL-yTUfC5e@gpIp zM#WE%yg(mi^XCaDAXVa0b8Iomx;Nhi!bI zlc*PbqHMU$ zD_Dz*L6JRRuU0EGR3BnSpbS^0=$D~Z>2azcEW*etRjT<)%xC8_}0$Bvc!;&SzCRk#P!2k5QH&9fj}s3SPRs^iULjsk%n z+mLGwWA-LG2@2P;up?QZnsNb&{4&Amo<}w#G!?tiO)Ryb9h@a) zl~cqrp+3DjF^tTy-b>tp5Yiha&symb*1P?#M3W|o+2=;R@`>2@O*u9x8&dtk9;Spy z_77TEh`fg?HK&BEI(c<9zpu2&uz^W|O>~?M;llkET6uAji z!OKCW;#7^4EfNp<8mOoT?Q&WAoyXla7l+!pFrjG`I*aX|mE9CwVVA{NaY+%}*zGH? z_1Me8{$WDax$&Tzwg-#qp;rkENj=QP5dw4R#+fq<=ullV+SHe#ocA;T+w8mPRL-| z(WS2D!5G0F=@IRC;2gm&i2|BjGd3X9@G2NO956p0FN-_AzD4}|DezV4h*AemK|Ao= z{Xb5DgpG@>^6L!6)U%@rBYbrNN-2}PI>20Y``WtE|AL;n2j z-m95RHYFet&;(O0L#@khw%?>&GIa?viC{e{MD=Dbw1fQ9Y~EE`A~`4%jE;3@3{==G z&wG_FBjGz~ch#*6KwHTYOBIN~knJHXv}Pr-1=2hs!(5le2n*Zak-k?HL0yR_uZ)zj zll!We{1UcG%2=Y*o#G6yRBf9{jE%f<#~kR)##9@^l*iwa*M7o8I{nefHxO(LT4~tD zJO|@zMzKf{gXLj~%?m1^N9o8{U6|`Ln>o?BlFXNGT66|B$na!#qi<3+a$$=^H7)Ft z`w0u%lT9vwRYVWO4`aid6jAb? zm(CI0_XG0hsAG)~G}1IO&{AOw65SVTimfkjD>z%XntSXpJ$|v8NuDQel>_2*0K|#* zzt=($bB8~CE`FsF{>_})zk#g%CvzI7o{*vymz=rv2Wtv%RLR1~%-nAvA6^=e9P2we zsmU;;Iy%ZG!jM-*^dUBz469TO$;yT=p_f%x3?xTJjF`3-noQ5~lWCTzm6cwWX;$v4 zR*Is6MuJ6Zl7@yt!pGcPWVw`t_~iX`4eYqg^c2%nBbyAf6e|l8Gjppr3qAd>{p}|4 z$w}%4ef{mF+1S1P?csJRb|ze;U|^*FRY8SFI!|i;tU6+jt(qnlGO$YzQ_ju^iCHYOs_OM7z*ZC(W?i z{>8#V4oUCBr2gmGVp!1(=Nq%lB$hhu`y?GI-R>AW%c zuLBh+NU*riyf!%yo!KTkOYjo&sInS@xZ9K3EB22ai<%b$YKS%pyWSRHeW#7?2wsZY?t%4T%+&NyXRV;IHSS&xzIJf z&ASKrUOKD*9|x2x<~x@ar`vBst=?b0_#w%x`m|U6&|tqLn>N!Q+?Ei1DG8Q$iO-i4 zs?;-~xFRw*0gv-SXw^=q6TC-^$VvGn?yNzr66T%_AxuN9e$=)-L!|tDQAm4aKj^H$ zBsUeAsJc|Xk$M6|`Sy05p@DCXVQLt?UA>Ir>=bj8b8dLe*pSJGh!jrgO$GCWQ3vq* zEu86nI8Z*maU$gV$NH2RiWlG$o^vyXcw9M342L4Hac~ zE1X=Zc_}gM;go`&>l?2&T?+;x#8(xt-%hJ%U-~$+XXC<`wQnNZMxL;T3Lp!iU7^hR z9%G4NeG6-4WK#t&c0f8Id$Je+ulNp48C6L_F*3*-phM;-5g`xVJNs<5UD@s8~Bo3SJg&n)+&^Q|i-;J+cS9D7xNKNqJW^aCHodgA|rIeq}>) z=DLkg#O2<&SAmy8*#%6up*FFvn)3!%$Gay-s~rf^cTf0v=DW1MleGIE*d{>Iqf?}C zabpc0aY$ck$OUq8fX%BGK-EB#yE~1b40qh{>Gr#|j$%5}SHse_=ch5Csjj{qQ4Iw! zwlrI0hNvN3o&sIpRJ%R3d+UbyGwpQ`Quiyd8IKpec4_XWPy^n-Fx}wkOcT!|cTB+B z?>BeUzt6@m2v381FXcnOm;Z5??aD}A6dmU$UIXN|V5CHY(9$M-6WePwZYd~rPFa^rV=+nE8Dexw z-$`M93zpGq6qIl}hrs%SJRP$2b@ouyOZ1e()=XUz5(iS>82i0c%7S%M?&;gDV6fpu z{sbBf!FSuyHx~fePu>bcX{;mV(W7(+`S-+Qpcx>3_rI&>8*^hTqyO*K6B!LG2c>iP zqe5>1@Q0BM(Y%wPj&5@!77dU=2Sk?!_ZPh;cRX0`9s#8!2!|gpXmFzC{2ZoHu0MV* zLxTAiIr+V%b+D9AgKBO#AI;Q@vx9w*=Q3j%Wnhp)v{Y_pFmyk&-V|^PfZBA8uJIl= zGDYxXtbHU-Hq4c;iRhzCJ;B1chqO^#P-Yv zdF>w?rt|sIdpVTZr{&*PQ<%fJxU3w#T!0hrM5Xnw_dBl*)cYW_jUy^qakvqQ@{NoM zTG(ix4^k5ue+{c=v4X{(Ft{7~Xkt064etFflc}yGQdYP?6qkXI;=i|XVzy4Uza~eF zv}D(_#R0DMgN5i=YOt#%v5kU3ndnA-n0N^^Ok=3k1bL~A3;8_sjU~Z7$Rm~gD=5BbR8!Wcs&wN&`m{)J0Wnw6FFIB4dwr z+tuJ52f$m>>t9pD}gTprNvx9Z<&zp4b~NQHgxV!kdzkZMRIf?wIdx9v(piIRe1~ z3_Yy4S^F(ilA30SG*RkIT+BtjaN#&qSuZn_`alQ2V#ZYTgFewnXFHk~RyN*s6^H)9 zRHUzSSjJW>xom8nLM|!2{!Ns#4?S`86|ACB(K=4lV3Aea-ffD>Of|!gS-&5?oPp>7Ag2-GKG*8pOWwq@rzFBTN}FM!9>}my}Me!Mcr6; z%{!I7n|gAo^L{t^MXis0Bd^R6g=o@GP(%~vC5^@2D|jeeKFPlg_#`G{m0cbiX}8{2 zVt^f#wLT0>4c!juN=f!!1f0Ex*%}7j=Kv;ZLWktpJ@m23sA|Z$nOPt^9l)2&Q%+s{!!P zJ<9>5^>&g+6tMfW@>Z;$ax5_x^zddrVIupH2Fqy%;0k6by|jF{?o~;n0>vBQqZEW1 z8~klM*YR}EULXA>ro|jl#8*mD38{QCiG+2b?9UUezGpaxin-anfx+(;mA^9+C|w}V zoDNGB#3oK+ezn@glT1F!pr1J z4^QJv%vo`8;={5qnA#aX)g7~uOEgAedt?Ich3cJ#wh_=IR6$o(o_VagQT8y#8-HlK zz)%>!N$hI+?cy-w_a#4wKM}I(CsiC%fW9bRdD?5(Xg5vD(M1 zAzMAH*X#Fn>j%B-JNGcW_2Q^-y{zN$JiGs;55OG86g2^2{Rm{?Z(c-F`UU{DWb{j& zYl)Zrb9Tq?Q_Nj;b+u5CN*Nr~SZ5%A#hCsg8^_@I6X zX|9pI^H`)DU2Fk*d?6e}J4>@H4V&2tlDC#Qo8{=76yi zazcQNuJ?qoDrH@%{0i}QjPMF>FcPzEImj(-y$9YTpDO1$o`$SOKx9cqd9l?jAgxxS_s@_V<$6CvahI2n0(R z2o~vozmcTOZH(m{^i8ex{~WvcQ|a(y5=lUA)kMLTyvVu|U`pXECVa#}*YcPatIS_6 zl<*?`bVLl?nLb*3~`?3!{9AP_A{r%1fFQf4X z#7Dal{U?2pzxM$f1K3RxPt77ivGa(dhzjd}tobjs;P(6Pyb@&G8BP8kvNogL%@$8a zyAa1nmLaP>Q#w#wYF!Ayoqras%gpVquUN8kzG@J3m8Gwj1@T9^3px|BjuT*Hyy%1<=p^>J;A*O$ zD8yna#g83ittWaAubQY--ap}h91D6IR-6ZO$8wHpvia+Z<-J~b|Jn(Doj4>MBR%PX z@J9g0%Jd}rvOijgAZSj~_pP)b{KH9lF{U|Th`Hb)SX0iCjtH&4)Eue8?H z9WwG;e|b4a8s+dv0Wlf?8tdNx;eQY+Ki|)P1gY#_a+LcQqS8FmQzB^My9$0G;3s}~ z&;}vMyvIGOm{i0O{gX4xKy1O5hr1xAp*<`-)`g{VU9%!3XePKJP%J1%*IfRonw=z$)Tg5 zRqa-i3%}hq$5(>Ib(WC`z6c)OV~K-o0akd3<*xi^!M;zg)=;v%)f9&H;fLFmd<{d| zalk1l+NAJM9ky^$MfKwI^x^6-?NAk1zL1_AqGsx>16SF|;NtE1OAn~OXN)yDDR%)7 z4OHMN`=8PH*^mDbjp+ny$$<+-m}3Vep0S1$k!YJH<7rA=iM?@QJAOiu%C`7o%UO@yaqh}2ns_eDgtsC z*JupsN*QQZp*0fU9T{%7e4!W6WI{<^j5f!J2vbH<+TSFR6huDMyd|^64pWh+G$5p> z;%)8d*O44fe{R+Kh2BE6z63Y z0Xs`!AMk~MpjzA>aZ%WQtlSFpT96`;+7MBcsMB_^ekM#Krb`hPG?DjZNDnXgV3{_2 zIw-UWjt7@mu7;8w%MVf)b=!!+!u60pV&D@qsh!SYXH@DH70-adr}SH<)y53UyoC1M zA*gXLbe0_!3KGzNH)?^W7R>D|`3{zcNdRBzozfK6w7AeZ-g|-M!FT`ce{8pCN6CKMjUvn_Yr*! zz1Yp?~pAu{X#_S{b`}~S&jh`J@ zsfAg-qB-32ZA(E4#lT`Y6s0BeFcSYmxK}9pGJvX?e;}4^`Vn1{U`g17uv@2z#m5&}p1mhm+B35Fp(g;Q` zXnPphY9IIs$tFbIS!~!r6-YQW-?4ZM8fK=PY}ute-5jN4MiLa>rdSQ|<_!`1dU-=E z!NUd>WDBM5>mz|yycmJ4l)wxkXD3X=h7~XhP^EBn4#-8rBehracShnzw4eL9Djig> zx7cD@*fUOj4)OOQZ!7rl-4zJ%OCb4~|9e~XM}YrNyanPfv>IH34sa4iX6WxY&VI&Q z>LO|ZyoLTv1i*{^M620C{QeWIZsq0m$3)aidSQ!X9oCX(P*-Vv8AtxoQrvd;QALQ;Uy2HTZ=4V)1cM8QMEpd)!}E?Q##y>(wZhzGoNAykEkU^|xX6>se_cMq z=SYo()m^9^SGEQkoX@*Fy9nGzfI{a%St)xV_AoDx9^0ZykZxEGZX`*^GUsI?NJ+>0S@ztgB%4pt1JONRDz|_O6K<&Ltg5uWy<% zJj2qTD9{*iy?m|1%GZ3R3U>h6-3}(XN=xWr_g+n(rD-4rYUT~FXy>&!7ASIB15y`7 zKrhG8@k=Z3#%KYjh<9@~ZZ&5uzwl-zCpoMHKpfeC*6BBT{J-J|81nzBJ1dL*%bDN$ zVba(Pf^Ufe{!Z%==T+fPlP5L!n-ceqFL4Rf3ARqFoM z>>ZT29b_MP0!Jig`JK&nHfH%9n*3J3uFw&T8!A&gw?Rb2YhPrJqAbY(ku0y*xtD2t zQgjyixMar`n=ezc0kdBfzN`UFg%8#d2K+|7J{C%|Y>^hlJQARSxgCsav1p%84XdC% z-vt$6l*eF)kN0CYb%l4R@rxZeNM+d{uwdZyfD;eak2ANb?wb+N8z;ai)m5oli`q=P;TNVU+zJv8XN-)#wXvg}zTsaB(BctCy2bE)l^YLr4oNIqq;1^}Q~OXA$D*Z>F9>W`j{wLG&TirQ=%^v#sP^9B zvgyX^!_SK<9;^bO4>KR@S>N?gT273!QYcSuXF>B4OqC>BPg~Sk&lik`e2f5}0gK4X z8e%2Lr9Vs4#H+orh2LGCRCM3R%TyY*Uwq<#3b$H#vv*!`&+^2!U|NE}cQMXUhZ zc8uSTw;NOSMy1WkHyuB=x`5w$hB%g57vQg4gV8&=aCcD#8sN-k>il?%%^~yoT#m1= zQk&0WB$-OPMJQNaFu%#jpQV68N)!`(=KS*e(i5xr8!ONeR<#)f)-1f@W!p_b6n&vm zOV?YK;aGE-~0}yZbavNm$6%*bWP4@Big(;KF%ng>*LY2{L(*>GG=N$9|&QfPyP)Z z@UH{$KbfNX51&t3oW!Znh=-{RrR2z_e-rw@_)d8DPF7X|Xkq6)pOUfs(R6Vivh&I9`4-zqREj;*|n8U_q zv(er3;0M(A+w_&O1HPQ1ZxZ+EMcFPyzPp@BZPGrfi}n4sH(afFFk7|qXo@hn-KYKr z3{4cQR(Y|qMEU0^ICtzJnHt*McqpcNuS_9!;3I|0-YH9%hNIWA=GkmUsf`k0?wON+ z$Nm{W6+!r?HBRWs;ms|;X#)Ceu(JcB9ezoO#Nmh7wU#V1rSdzd$&t!2A+LUkm}0}z z595zmp4WR(z!;1XcIBxtYT$5DqkM9zaeVYjDIul#mYEWHfp+lHAd$EvBZcY6>ZJJs zO-z}#Y|S?6OD*aeOjtJG;VUOMt$7M{$gAFT_Y9TOEpvjq+<4v|H((aL=^>Wg7cMZg zV1&N+6i%kX8G)(`KF#<1)kJD?FeoqUR*1=}XEcKm&*DZnHl{kvrXJr0#5yn$e$9M> z(U5Y&w%(I{6W(_&X@_rv7v2U_&)h2QX)1nnIOZ;?2mh#eWTpG-EcmTDcZfD{G%kRu z{2NQ9e;o}8V4b4FpCawDxS!)NeWrpPm{hP(wt6u%E(%?tq+LZ&k}IOM(cI@RrGJtkX&g@t~*M*F>cn$}yYx3#1;;6Ohq0HjWQ z5y7(;03Wn$)-r}+Dn&R#i53q(`w&4xI_g9=LV4@I{-&G6{7k4i!vozo|xTM-&E|w9Bt67dAsB?Z{+6f9b%S0sv zdP&4beNsJPN2KCNr34zadFxsvD~HG&qaD7riawIaMS0Gtbsx4hdlp#Ff2FYU_Xf!6 z$oZla2+`>88=zl8^v@jD1w0A?C!Q{>JtfGE=rC9LmP)ex11xB8F#{SV1g;3$W)Pu~ zrq_~Ui`6xS6fEzV>91czR-C!{viVzt{sckS5Ed6 z!pK|sF0*qFz}8_Wt1_0|lQQUxA9ruJLPIqbtEWc31S?U#Oe0Z$r#StZIke)X0SW2h z-fm&u5068pOWbwXE)C0fNkP2+0yK_nYHNL!Ob^7wQCqFv@XJ zG2q{l`KzW>pXlGS;W2Ww@y4SK>?W2&R=y%xv6PmqMKw)f9ZnWeaBm>IqTXF}#VkC~ zW^(R*DM1Bw$;sMkU9Xr*(GvUZ39~GwdlVf;s*F2T$&6KJd%mY8f76=KO(fej`jkov zG@=2$o1u0^!47k1&gl&s)G?@u-pdKGY<@uy^tTGJW{?CG%sc^N*rRAkdc+%|>&&zd z@@??1us@^n2T@}4lk41-Jh7+|GL@bBPcQwV)R&^W@puO7mwx}c`NMtozY7DW{{-wY zF!<%~TR#HGQ%LO;5sBazA}D`dl+GS3#a;2@eDXz6@{=UD2S$p^ zs=EcZ>Z4(o&5Mm4Y?2BPN$@zCKK(hVol6Xz`C+Bl&y*LqQ|hrfy%pzUIoljT)}p@1 zYKSmL2%*R!_z52tVjZ-KX{Kf1V3t(1qDTy`tZfnag%ZEWsTUJtFuGM?m5FLUF4xDliWVS?Ek*eV zpP(E82|^_I7B7ecwQ2MihgyScbF@ncblffWmbZH&iMGx1fa!aK!1zs%Ehck3$CvTL znuH?tNTopeE{k*J#QJiXXc9?jvHBwOsV7qmr6*Gq)kRehohDR>wYJ4*wis=9x;?f_ z3z8ehE4z%d$od(v$mh=6EW+ z(GM6+l8=Ja8|vBM%dk7ii#DXsI6Ywv6~;4hzqbDv$VoWr_~k`-$GcWshj(3>f6w(n zCZ!HcpcG$2{np4u4lv%aHU=1)|1fg-H<|w@$fa!gbC;P;A3Fj5E2}SkodhKDLMg>g z#x$Zr2QkHw<~ho3#0dpSG_=uQU64P-TxYLXCH z_#K+I?sE9h@yzbCp-T{@_^L2>Ig_ysi_|w$24gdc&}`?}0blp8HA4>bRZ@dvII(@C zVHYn*WvoZXqH;2}xb8Ui1NyM0Jf@4a?P<94mrsRj2o6v`dhA2edkYa2+P;JN?#*n~ z3g@0HX@C~C$`p`I-b@qMmg356}7se9}k$je(@!I>4G&aUt}VS`aFv{@1t zjjN1X6OOwWEYiVkfMQ{UJ8dL;bUCXadPLZQNb!YZYW-x#oT=){)SHB%Ljpn7{-N>~ zR4dRpP!+rta#r!|^=d(9J$Ng^JB70nJJmHdU$%~Sp6~J5(Z8XOH*%!h{eop{fc$X( z4%8NN;H(M$_h-WYK5M^TGd;?S!1@?qp;H!25_)IZlKnTWuN?H*(j}gWtwRWx#9(#;-s7(iwiY!nT&0$lnAes4tp+PEbFHU!hsS z?(cPn(;618gbI1%LebrTh*nRUxxRWF*gbWBAm*~%gWn_!B$E~GjHdptNGY60Fkuyi_l9ly9=#8#P)?l>;j*UB2S1p1*qv)g^>+*E!`tI3n z^gpKBz3QrmSxH`P=0-Upz}j%Gl8OH4{mYwPL#UnS6>x-J043`;4+}X5b8BFb%;g_) z<+9kHWO9%xH9#?vxw95wf=MhVDv9J=0QP|IV+7n;3h3fZ>=)x)B)|WuG}7FK3dKtw z9O6D3+8iFa>)XSnvIF%1kBS#1VH|5Q^NXh%^I%3Ye973cIp>|+^O1}ec2EEsuUuah zB~akNMBsqieT8uH#2^T~+pJNT<&taQEai%gZHslfm%~zx&O#0_<+L!fVE922IS$K& zQQ5`j{IMy`4NNN{*A$+ve$swH=dN^`O?I5)2$f2^Dkk(5=-XU#v`>!=3#{RNG(kT>bJAQ3c3CFu&^wkO?(lPbS8q-x4j(h_638GkPc34@yFV z)9>n-w52DfKeQ%4+`D@~F{E`cfpp-gvF}RsSQ#!(#gm)tP3@D`LS0=4pd&yGGI)h0 z=BGSkV)5jh)xzsnJRt7>236-RKCRH+gCVf2x33nWa`o5EsF0oNdmdd z_}R$^hSHZrdnk~@DQEgSCrSfrCY=qMCN3W3o0uBEc2AR~4;ux6A*0v=4uBh<+5y2u{SYfz-tpPyhg8Ab2A;q{!*(q;-pU zhn~N*_1yyxS^GfhNC2Va`Azx#lg>~yc2WTrw*KCcEAnpwW?$rYuqT&^b2EPSip?=O z=x9~4lk)Thjcc2bL*EJ=)_}d01{3s7Yn}6HEL}GrlSe;aA#pUc6YDF@vpMuMK76uMi63m3U$Z5jvRHgNz#gmR4p#z+!0W3 z*A>F!?Um8|gXgu$B%+^hOy^ZT`h;VlvdOD1!I2Ma5}S?T7Rg0UBhGoc97u^9 z_RLbjxgFhYMs94xPM?*__Ypccxuapk_)UX$lV7Ce_~X}z4j1z7`&O(t;p4H|yxVB{ zGAc_I>Dx?A_thLQ)9(+|(~HqglQz!G6f8?9%BC1(ySztqv)#AGI!&C`|E z#JCj~+B3XX0Q#ct7!NEM(D*(|g$E_BlBqn6J4Bdk3oH_dDbi-g-XEBOi1O&W+YM7D zKm78<0ViznFe<#;f-P<}lWmVEld#Uf6~?0Xeg`SbA}TgkwwB~AHOg~zHaS(*l0=QN z*cXVe9t`D-pI^N1MsxJRNufuy1&iXhz@Pz9%9&+ZnJ}=BaaKd zZso^p4e+V!uNOnRDwcq68k4XJZ!q%)+$3qf?$j~gXPI|7dW}s7A8SqkAKD>-BN{Eh zhxWZ!7T`l`^I8LYKN?!4^}+O&ED=8RXN8QoY+<5VF^|s+HFv~D7uBMOWbEE2?8H#Q zjPM;v>qpkGZ*GLyddHC?f?Y`fPZv`X9*HVOh)LdiS|fdm&T72Tj(eylP}(=Hp_-)G zAAa5Nd;j35qX?wRI&jteMz*AAV{T_>?DWU7$^RHAFN^#0rV(ssD5lbpW@7RMygM*r zsdUEtK@cP=UmGS?nXA+-77$Y0vmrgc0LmR)P*8L?oJX=3%P4np`utHQ;33-yk${pG z9M4EnZsI}8e5zGOn)9{jDE`w%BcJj2z{yW9pgVSOoP58M#1J$WU*z%e!%uZ|Q$h?XXt z(d(KPpYgQ4jJpeGCfT&OKM zOlm%2{{7YByq|AF4aDXi_#&1P#k^ z_aa_#<$!=Ck-8mJl}YKjLUsudFUs2DgX`p+LWD^k=zb z9yuFu9_*(cJTB_T=|Xg|tKZ|8tBjDxDN-6EE-XSNtBPoL!l~2~!qmAJxv^U~iq{$A z-hprGo8rVL`jo*kG6msRHGhf1`d+$`R^$cr<&{|A9dF{?+~Z=|`6rR0pcLZnM9=bczhd(^w0Jl83VrQG9rS+Tq>{c7 z#0gfPz0rm96zIZdNeS72x3KI>S{ADlCsYHN5)}_2Djqn-C3!+*Q zvU7^KJYh9ycKvFUJB#NPc5kIpa(tiv_ljqmI^X5l-UApqhc7Q^-&c}YEF2xyJ~won zjZ)3D9;UY*C>!X)n4q~uXEJ1ZkFPrd@mq)p8~uGJuWn73^^eep&xm~& z$Ty^PP~>VxA_UG}C-iuN3akn=d${P}liD@dswprY>VDteps}f(m}Wd7L1DaVi(~Ul zkY#U`0KL;N;wl3tn?6Uv1$KI-sdC4ZPiK)b4M!6DTD}rrU zU>~$+O2WZPsnnGv1(f#Xri`s})|vfR1nAEY-xWL+C5}G$mh76HS*(=Y=As?`UF?r=rNb z^WEryN19V?gNA93%SZ=OP{9v|NfC5qL_x(oFDt!EW++eUh>eB|E89$u`3yHadEv|z zZY417Rl}(eePRES*uJT%(-@uav_zzM{NTkNE%(W`6YeWXw|2x8?;bKadi7Gfs=kGF z?o<+h!sx_H^Z!1$!IBY(*Xo5Cv$<+8kihAxZ0reJG|4%%PGAMG|~UV z(pJgL-0`>Xp??o%0!ydgC?!?*#=8kC`XH922~VnqxL6ep^a6MG-3^^@@lrK(&8b;X zGYPP{;d^l08}}~=j8b4)g{{sK{#vIi*{QF-Gf)g>gb1h}KE%p#bS{Bte-c2LP(*v0 z_VF4&7080cGikW-j~0j07^6SN=b%3|SRAao{e_UP?Qv+d1OE02@M8bXXt$Ds+fO&b zH-M3_m6h8szkB&l93KQwu9nc9oa>D#n?!ReIM|g%B>oBH+-U;j(ufX zkgvG=?&An{w}HAqKv{qf7HMQmo7v#l68orq zY}O^pu^5dS%INB`BGb7;j^cwP1ddKLp|5o^Q{JRrr#uLhs2z1tc3(rfBQ2EA!6i^3;#aJ zuP00s0i7mU$IeaKb3y1sf)&j7EMGf8GuuU@6e`fye?Wf@;J{qtfvtcI#L|5MGDC6f zexH%K*`>Jo3(5tCKjgCk6cus_xg(d)Pnx!)xQu(G_slIYl~>w3Jgi;uR#_D3>H|ks%}VarVU!)x!}}m zA{sB788g#;Kp@e&?-JF@FD03=WvB7jD&KO}o;FI5BUP8{58~p_YVy}K>TFeKU3pDzt|xre$%oK57aBvUT=CKpky=FQn%y3i=Z%h3DBFqmelQty1gX3JT>&!tBk zgG;~RrJru)gw?T87|_ls2q19!a%Sg{2o` z8_aSmEX8liRLXFst7eXqk9kYEx8b6zs$34Ql8rOg^Y$J^3cvAlWZi8(Hn(&wXqL9} zlU3YzIMANN3cOu8KXo^qu-eXKjKy)*CZNBxE&ZN-5OqRfiz_iY zE-|=WP(}0-Rv100V?tJlz49nr56KOgj`{>QujU)pF%oO9Mu))#ke82&4>J zAkM9g#|T7H`IvBDchF$LWfXKNByNqXzdXUTE3qLDw&WhBp#4f=4}LSf@Gfm@LT!KO zm%3JJvQuoW(5ZrrMO0T%hu8@RaCgX>m1U&o7_1UQ-W9I6>Fx!%QH1Lfl;S zw_D$wABt=EeTY#t)FE~2?{pvH{Q3@KcexRlVB4GSW@9*_PqH<^uI z9zk**uT6mE@34DvrKa3R@tHT!t)J=RmZr;I`op92q|7ze{T%-7yhMOBr^XZZSLQb} ziQ}DWI}WbLY{jzM0{si_!*a%TbgyRaYYS^5Z_%7G ztQkLgp@hk`8O5-?qJ!+IY*?=7dVw`HRR5$H1}vvDKQy+(N$E8;ZM$+?P{-u@)MiIw zblm^_1FZ(O=WT8RoVK?Ww)V5X>!RXho`Q72*XqQhZ5~nlwB=OucNAAN&~XJu1DPz_ z+F!asC6-P^g5T7y{@ige6vsc&-&iH~9iC>sAb`?~ul1UVlVjzH_u1aH{;G)R+1}OK zrQ%ha{-!})7Ceg`Ox$O22EhOGTu+;r6-+hQmAe(@8njDB}>^ptHLB;EW47xaO@_q70y2m;t| zov;2lB7gYm{8@+ooYcSoX%jfvuwqL|`04SA%=-cg3EXX zC58P2F6;6U52x--bP*=N({!kmzy!M@_dT6O!=S@h1x7KAEg+Ot!$q}KokfdfsWM&F z3^~>^1Tl(OuoV`q)@qJgiAoR#jdP6>>0v*tbM7W#fEK+|0#+TnhBH;~2xaC_wpNKSAz+s7dG)1|n=>{S=Y;vqat;;3Opr@D zQA*osz!|8c7!fSjpJpI5d?S% z5zRr8eFxh@v=FyB@ifiUWsauJejn%ihg-ZwG4J<}exN)-H2z}6C9J({x{z4?4Vwy! z`vj}(z1C7%*rMBea$zKlkkdTZD;32ELOLGLG$Z3^{r*hd!=v@&NFG*RbBmh`#p9dH z#V0xg><58CGyNR1tV)Z2EJ&sGLjr!WAG&k9%>(Dv2kTv-Sw5agclDRKFt3Sr`-blYysDE~B){TJz z(;*4KG?bziuGXk)u#UV>L3-~)XuKb&WI)zh!N6kbFfuZqesk~svB{V#E{qeN$r(B_D7QCqAvFx>{2;}F&iLM&U`SPDSMy*T96N@6 zs>jy<$Jtv4^|>usph1EY+}$;JaCdjt0Kq+Yu;32C-QC^Y-QC@SyTki&&YU|F?%a8= zUQsEE|MKmY-Mf4BTAC4rk%abgpuXx6rqzm*OobHkD^;2@UqiF@pjPCwHOh?WvjVrm zoXv@R%g;ThU{rZnB3_6P7P*wzQ(1ik2n6(Dyulp( z`|Wgcjy1&ek1Dhrjc!v|SefEpdB1!R)oU#$mm`b&6GAfkgNTo2V3e!qL@IizXMXuq zsV|Jz2o$4@xMVk6EG8z-R2E5KC7>wIvOw5b94Hzsfo5(-q!n$|B~T)u-wl_ht3YVp z4X^==>$aPSXtX!0!{X1bwN(U`8yJEJ{Sz1NG}byly1ji;!4f5QAtV9}I7?PDh^@x3pd# zF1N_Ot+N%wJE7VWg?;Wa!2MBdpsH8wu%y~c1QXF{T!GIU_ZVX62~XaC{W=^0*x_0Y zq=Uu)uDU7Je$4lHke$)D0+ps5;O4q%>-w?fv1u%BRgybR7vhsqU7GZx!Z@+9fI^vU zJq2C@nS;{~Sn7V+1hlm&c!lyYta^HW@jLT!=*ac)rUa9Y1ya2GmO@$^$uGG%Q&2;& zNlJG>;L=UETp2jwaHuIA64enyFJ)bn;)QXnX(HXLXs{BOXD*s$*(2Ufs+cHfyJk?7 z^c(1|v-IYYM+PY?acFJ0%g9e2_a%WjnB;VDh}hTO>F_r}0W1i7CS5llmTbVW}Xy#a=q>`#h%&~rvIcXGAzm=_vIJkb5KXL}N>_z9EdjiG6qQ?Ngn)=H%S?r?y;-f7p*w}dv_TWy> zQULl(2oU%$FO+p!Bpf5u?-RmvgwbC1o-ByX6xQ4wA3f_3HHk^cJ8rPsKshd-`ZZ9r zci+W449x8gHX!DcpaDBP|9h@i*8Ogxt7rZ{A71YpS+UP%Q@rSXX&UxPv?&gjt z0y$oo>VhBjE|f3Ew7_mQclE9=gA${5%nQ?+Ue%-gE{BTWCnSmQHqEe_#^`a1o+#oM zagf*SOCF?0CjHbU$G)3!URlpfA3AmD`68CZfKF=N=wrTvjd`8t#O#k+u}#OOr|e^U zPkbG9?!0bJ`blEcYAn?s#_Pt}V0peBAj_L7iENgK!yUOIM&9IW>=Cm1;K_P(jv-Nmk+{x=-9Z zjI<3hB3YB=By-k2mx}h*uzpnZV5{l+!=%_OJ5d-!j5K5Hp|-T6jdR#Az&I`?u{*z~ViOc*w6+ zz>-3Rnnm5WB}{;3$6stcTea8T`RCi(hN}rim8YYv&D>l9xDFZOKm9K0_}8qxD5OEw z_KS?^2NI4q0MH7-jrDb1QhFF?d~pCB<|{*0z&50!wEr}FURH;X^e!Xu8LPCBac3&1 z+S3-U){v7{l`N^Rc2r{Z41<*EZ*`uVOjSLZuh?R{2a7`HF3T93u87MS`wJT zeUt&o^Px_nd={5=f|(!`Pp&hiGyYd+spkra$B2N(Po6I91c_I`8RlEA zu?PvHa!PwVkj9>ITr7j6B>4BCDZF^a3Ak7)V=7?%ph`?C2W@qZYAG*a9RgP~pz1E2 znTL#SUkK|3?#sW@1)zYS+NSH#1y!3$(sTgsUW5Yb zK7Q8Nz|??vthg;~vvBs{Oun@*j@)%5oqc?E6`dwXww7pAQbu^^-kU!Q`6V+#x0^;n z&|$F0GS;+9fpL?ePjV-ib!U><2we%xFNw9X^Qk5YJVTKab+#%%8lV!15mVZe^<2!D zSjU!N(+o?=3Jq=++B5)M+C_!N8E7ye)+gO1ki}rZ3#6F#TbQi?FJI-6(tW*mbfPXe z7tP-?<6asWvcyGmE#tyxa2bE_q({Ot76Aj+nfz5LHD8R8$;>|wworcLL=W%h?)t~R zvUQ<8iL*-mCik9m%I4j>FHgPa>`$}j98O*5F8khF;b&xhX&X{~xXn9GSgSV8(5Gyx z{&3iMiymHsQKy4Ce}V&`Ln$I|00x%;l)`_^b^yy#mH_b+3#)Gi=CphkdM1EB{zCEn z%ldyCza~aQVTu>|=a@Vw0-x-ed!SQ-U5JTkM*r6oPcL;K``i@n^nLS9X-G>4`UTb~ zjMsSXH_ z_4NnD#`xZEQUT+R`h2}W1n~pRFe9<>l6AF5Ct=TqRr-#R0$%tgMRZMU{Y_E3H8LY+ z7|&ma_9N*o*vu3y`buZ8`y8mVFcc8{vpYno`6WMZX&uS0EfI?Odk_?F(9mv?SQTh% z6?{hAJAm@4Fyg-Ku?2yJ-2z%vUFQh(8!!<`Tsrms;05$qbL)l-_z}y(dsfzO$qHU-FR9L#SEc1D zXK_DHyaO0=Q`jIwLDIldaVQaJ;iz?O`4x;a@_(});N8y~$}^TCMyNxKOsd-*Wl zH97upqPM3sV94YEV7T~4&f!>fNq$Nec|}A~Fm zEdk6hNhYoWvBiuq?)z>3JA?-Sk`X8@y!8PojH8-MY7*YWq+KJevQg$n+vV8J^8RUS zi_F+tOqGN#H_py`H#0JEs3?SiD9=)sB#~mrMBYHC5JS-noIic9K@~YGsGm72ymGAI zTeSd|C-3jn@*7^K`ecM*6^Ld@t4E|rB@Z}B53dm&Vntj)l*&oPDb>=Ebo)4Qq9W-< z$X2GKe+;FeQ>SB+3-rN7s|=!E$;t_N@T&AZIx1!*8qrKj440XjU*`18V);gj6%4xA ziDU-aXqr5EK?;OFo;o=vD71?I3ueu6jMYG1GqW*!z*%7CS215*NVl8OVqhQ{XltX< z!AVxlL2IcGO;w=J_r`3~pk#}fc!8p6Q7pnG_!`*hV%}>zf;5#1%luVE@81dPE$pl% z%av-YkXca2n_QJr$6EKeGmS%TUH{@bUa^(*3T3+oRBAtp0oUzH5s!u90AextK z_exc8mYHFsMp0oJiJryRBWEl^nZ%w-o^`HAQPe$tk0DE#Dl!=d9n51O)2Cl%CGkgv8Zs~t!> zWb7*t#HFB>t96Ytl~wex{oGO1W-29GPP{z_bLse!I9Sta%TAe6m07?B+~c;7Gv^@o zY!w$9Q_V=hF!ZB*{+ra0EtU+2&Zk!9mn-*lQpJX6mrnbEvd6)%oXt)5Kr+wVgc^3D z6`A=1gk9d*0F5&6p^LOt>LGpgF@~up=ZWAOE9>iXiHh``(wXlMjr}hBPck$Y?kLPB zmEWH(6v6Ob)}sdT*QT1$$m0yEC!<}|d7GCVY)u~-`kM14{gWE0E1p{`k{M`2YHCcJ zB{H2E=cRj!iF^78_FfW$LriVTg9|n`OFo-h-a58;v0U+GoTC~mtxuf!n8D41T{n>g z0fSCDnbf&wIaeuQrc%$Y)cWLB-tV@WIJNBTp{_iHlhFbDuhYf1N%-(`V1qP|6S0Gm z!8`Jj^=SowjE~sBZKi|Vc8X!0*v>!d{@hXs0LQtRqU%1#=BE-F>xxlojlIwtg$*5$^~t4r6nD!B>YPqN`&rin&=YQA%Q2UMzJwlP(aS_@>I}o|dK`O+8E- zcvIk8RpG67Yv{M7$2Z=}V&;dzZojPzEqCh}ZQDlN9`Em=uR4IK3FGxidMgk{)r313 zDh6N=(Wk62vDj-_=!X`v+dnTsLv+k1IF$Mp818ntJYbC4&|VRokvL92S2s4rC{^VE z3%4^`ihq%lN^p=b9H9{K{H{q}9&RB_ZTqEAcv_VjQI05sH&fJkqPK{Y=DS8#M*C8x zw3sD$T;;5bdFk(=p+$Mi10g$-+5jS275J+*LedR02-0Z9P-DNP?%_V~M+jk4K}O>6 z^?U%v7!eAc+GrtqKZP*6dfg_z@X93x+FN^jKIf*UNRfte!C<5-Yc$P4TTII$W*MYR zi>*=vf0V2-84HPfhu{ermRmHC8HRkGo!K zc#CN_ZB}yp5h$ND1mG?qyt0txOko-m zvW&o=b$A3QT#}uJb6&bAw&2tm%r42N1}8o}rFA+%%+0Jb&CU2b$Cl-WcO~0Je6OWE;I`cb z1koQ%B!2|aKRYzC6^?M*8d7i5RpCym!IY{LLS7R07BvHIVJVV z{Pe1zcn_@^$#m-t+|`;Q4u@eyM}EGzaq7kK==sR#(eT6GX_?VDL{xb;?{WdF7iyGq zk&|A+G_uf8I%k8K2H7-fOx^Ur(vhI512j2Qh@gbkM5gp{x_K6`Rs!sox*W^06~dr!rYj8@&&tvY!Y3h)PUny zyU`1*>PjoO%-u?E9vQ>SDqDr%DMqH6@_gP)M8*Wwj;~uuec6(c$??A5l;?-3v*DPZ z9c)gr=2%a|HgTa-xd0Cvul2I&`M^l)~9 zHut$f)ttj_Ce{+|7QDLeqI`8VJcXq=jIO{DUl2Jxm73O_HqutK?@GtmmqTtVMTIki zwtq(gTjW<6(j7@c!3^QOFRT8 zN(I?Cr0H`@U1PJ!&I%B(UD!j)Nuptb0h5@;ZK_t>hq-2C$;a94_~Ocg_UrX)l@#$2L+1d-zgvxKJ{WQOy_m>74g zK%MaBo!9OyPOC z?}@^@ins>=l>>xl3IQ^d#eUuohZ*RI<(e#Ha)tsQc^|@bA@OJdIk?&cVdx|+vzA=Y z>sbX4(N|Fam!B{Bg=W^1_{$*j@Z85!!_5gSRxNF>$**2e&3=0jHFoism`)X##Nv@m zAoil2W)uYE%$ftSk!0Dh2hO}cDmw=&y8K9iC_XtipAL^J;54*i!4X;=Xeiap6dU4H zDkFtUZ4-X8b4?b6G93;Zh5DT`uGy<6&?V&NW)JVn){nZQLgJo|mluVJF=y4(Sd(b0 zm6Wlp{(5!5(Y5rJuv{fjj5Xu(`Q0|a-W>h`g0^FC#?C#}HOgh=e0MeiKusbWV*7!zH!a~45no7I(GdXR z1kTYIP^PaN)R=H&CL$+b0Uax!cQqy+*7M zQ*W;oyS49aw7Q+4QYU%}1>?1M(U+<>gNFCsRrJWLAE5J8<+-5cHMITt!p?@8UhM-o z1P{O={@5=0!y)*-f7f;XH(%fbR6NM;;e-LIvP`LS-5^r43l@~XYG(X}1hDDE7C(MD z&1`bowc#$xT50mQ?3cOt$?4A;Zq(S4?ft`E`1*( zlT=Jh=L$>`fHM%;OLi_zun%*Hr$#boqX8jc&P3plfcE%Nv7u-Ss2-eDbO5>B){Z?m z^FuT`rjD_1J$VzRX_aoXqsf&r;-{!jIz-(U&s@0eHc68qkxos=Mq}VW&9My=c5HDC zKK^DLaHwv;uWj^~1PmeAl+zpU+WeOjm;B z{;QAs-9Ip~Lm?ePCO8LSK8C(p$NFBHES~p3zS~ zQ$!<3cM6dgSk-r?K}$DW_4xeoyQXSFA{CPTAV@xTWVCc<&i&;i8N=QH2-!#e)Gq{l zyOx76t0$Szrz?j@>@!#(k(%vv&CC}qtSS6&UXG^S#Mg}`hzfJX{ZVqf9Ld5A`G`1Yb1t?}F$;e06Q-c`Eyw3YIgbvg+_yBhBvNo8 z#SEVUt2nH?B5T|SKLdKk_VM@WM$}fySg}*KWxBOFX)!W9Iq?ACcAEX`Aa?4}%!06W zgmEZD_eHhk_Kz;(M8bIfG5d<`2S-084Pz6!wzyt> zyCxe`*c$kbxS+f?U8|nG$s4rDZmX8F**IKN@&oa=pq!L!CJLOzA>nyvJ5R2*f;)lS zWo_-9Ut2{(tHM3+$<3$EX-jS zl>5de`=$@MMKZI;vOhRsEbFB)I6z`y&()M=N}c042+%C{mYYaDw)F{>hsMSbJhmOF z(z*aDpd6yl^cwI($lo;NjwDdPR-KS;f;|fMb8WZ6Lg`p!MA$y8`UU%z84 ziS8Y0ep~f!;Yl`mwe;Y>dp6&n1Ewl7PHm|rUKa=h6xhCU-EJgJ^Ksrf6aGk z6viS1Sg|cfEP?734B2_NcgIyFP7zw;U@oC}5$rV1-k5JN^kGJOs}?RU9X`XB?>e$~ z#(4L#C|vU+x(NoEP`ZhIfC#}!=geN!5^^IUS3xOz7D;31apksml}R)9lUp53R=YJG zl0RKhGTzQ&a?;-V9L;C85`j2H5?5+Qs z5KJq)Lb{6%-fs*RKD~N)rLz1ZiyV25K&;;c{SS&0YOLAS_bUcG^KT>#MWo++GJS&U~z>!=;^;%h*n@d)_U3AO`cf zj04W^7=VfTgA?P==&-UfH_)|wmlS&xwcZMr7J(Xah#*2z&Juch__vA$zhI|hhX^*E z)Z$@%>F8z=W^~lt&41k0Q7>rWv(GEaR`zN`N(8+(qMa%(B$a;e1DDvbq1yl z@08-8;9Y@Mz$%BB6Q~63d&@`RDG4U-Ad58(;Ol8l9JR1R!X`k4pq`U9z6^2aG#}>_ zZJ%~YV5%SF9AZ3>LYX_b)XWQH-w{`qn9kd&JXKh;_mYg4)K}6oWI`&vWP|<~E?vGP zyC$pLwW3vvV{saNGiWv8Q74jqmnva-1BuMcoCF$MI#hVc3}9q*56QKRH>Y1QWr)yK zABz-R&{GE$$I6?+nftB+9xt3oCTyb7)&Z@s@Wj9Vy>aRQqw0p^VE2p=QEiZ;7Mu!+ z5BQ??(<>j|l(kP0 zxEr2Ra^h`%X=+^4KzV)Fyz70)`}oFWy9}sY{&U}fAFy_1`@bg6H+T!O@7T{ET2~%N zt;~WPP#r;fIVN#eQba_tX~{#BA_4HF0>8H1&bJCSOjT~H*SDx)Z+AH5QtD*raEdWf zwH-!))Z8}+Q%!|Aagt2u%Qi~jkupA>zzM{nBIQ}Ks2o&?F0q5hAt{F6;G^>OIQwFO zYK(FeISU^Iv;pnUbi`xi-{N`hsHQOmHboh-YY!3+=g@!5a;+&~ z!DuBcYQ_*e_c;8X!JyHr{!H=FW{n9mg9>DW?bVk~`O_9%N^~L>ql*5Au6CLNF5J~< zCfO#f*$BrIA{wUKZ@cTUJZHu0?p!WM%aKL1tEVY_2|Vhrcx+l^_6zr#+rN0x(pk(= zDXxB-qPm?gtZ_G8Q^J0gz09r=|0;T@8)A2rKdX=ann6alFri?MJhk@R_}fy2%j(a^ z2;?fE%?=<#(gN)G2ORa!=ynE3wf;G&vH$O7pa*Ymfov@3b7qoRbvi9{O;HmB%u1O} ze6%Q+!x1The>4UOe|M^-c%JI-HhVO;c659UgM2P5ppNU(j58}yU6AMbPTy=nEuwBQ zugc5?uX_d;uiRWb6f#1)Opv@fCR7PO)TG)yk_bs#ltP$4uaQ-#O=d^~Xb8{M zS#^CB;FU)P`mLQnq>@eSa7Bpd&LGAI|ER{OOr-$6DV zteVQTzDpz<4rhP%iW__GT5qYScK6*?Q?Xybln-!5NWcOA0m=Hq8UM!ti|Sf_GdK87 z{5R3^-}C3tF#1nkyvTxkAVKNA{^jkRM?mHD-GYcADbG+m=$a0&5~9tCzTjuF!T~9v z()0<#Pup#Khc7>aQF1|bt!j7Fw{u%DM;c3{I>d?P_lj$^bdIuVkXeGc>Wmcb@H?9$ zQ;@}f%VSHU?3KX9(Mj7I(MYO_EZp)XKR;EOh-1-)`=}LuVF`?!tt1hAMSO_Ag2$8e zUvKZizknbL@ds_Yh3`VexAS`U>EA|)?3w_doej8U|8p+=_fP-(n^zQv0DQ;DEg;0v zq{MC{2b00nyx%Q|BIQ8^Ge610k1NCK@!*zXlXamxgDGaa3%BJ4yYo8UXB6sdiwR|C zqby8E?yzNTG2Uw8t);cKzWyX`_5=ZDV~cghwhDT)+u_`z$@GMP_3XfreVrppPN)bBs9#RxoMc+(68? zE=k6iAOoEN;H!#M}?J? zacxyVKCVeiDHF36SBEVdfl|dRY{87L`~CVDF3GhAXSA7e&$q_i2dyus+_av@x$x)> z$>M>LKcC~e2w=9Bk;|C6M^bL`uH}P;8e_yuG``3E#H5B@qy-PhdZ+kDdFr6zyfKkC}K-^WZhex zy1BnThwLl|qJo%ht|codQd_pL7EZ9cXR~M8iiGg4LPd!TB2Tvhp*jY`fq)?jK$EEd z;S+vK$yzUHt>r$}Elhh!-rt?3o-a-&s#K`x#B6j=JZnRlbEv92{67?KK1D*vELkg||>;|RvD)MwN{h4-05QUa}jlQwAb`v5QL=%T{v z^?2R2@jJ-VOY65%45qL^}cWeu5VRe|#u|0Y< zL8!N_r(`%#yet`fqYB0Haa%Oq@YgtQUNM5uesSm6vgRq~hakmwB7I8ZN6z(G+%k%>CZ>wiZCfzQ4k`3gvHZ~Vl6toi+!-mJ{c|5jTD znAy_EfKUHTx$N&aw+Q=dOC7tf*l>}HzX=_;;)DdFi=a_-)yNt)ka28KV)g%g4^ z+@%z0hJTL4H_}uqx6>Z?ux8#6n!WpX%(r^n@F_Xw#!Iq}&(60H}Lr$8#!3+Pz z)B1}=%Jil~T(t%w;b5d&piz_xP0{ zD++c4Ob-{s6lzEY8A!)5xc}pPN`XAt#TGU^@XpM5c(HK$^x@R`Pi;pLJEk;3&%KSx z&_$hP?~g3cQA^`fKmvIB$VESzgg>j7X`t4?tVqX85}Mmee%q!KXp_=Jb#hA%?w9i{Mr}K4A5nMQ@`1c2w%r z+!tcn)X!|8cI#C=P$|=Hw=UCB`95X*5k_n{{6-+!vpwj>uhUhU_q<@ zPwVn;=-B_5bo}Mte;MR|yU1h0=%D~9O>nPo-?aJ+9#vhmUAmZ<94aW~Ing!5riyBT zWPOI3^QS)|%lyK9@J81#-RZSE=R_y6Tj%m>$rgjOlrv*u4V6?crF4jVa%1p>7|nHp zN=+TJyirimM5>l00}vq$wAzsBy{IcIN-M!Qpx90U+QlL$dZsPrYDQ|P!8mmuHiyB&MnU>flrz&Tt2?sL}v zUuW^p&AtR^E5fJ1M*oU^EOEOMz~lXn<8K{2i7yA(A`#VyAwNAF=|^Wa+g=e2>IT^q zxckL@2J{5{>cu`j$VWm$gKW6Tg zOWA5h*%<0MPdag|con3KllP3xi{kV(>_firiL>?DUpUX%Yc5>`)S>8$=?-;kF3yI> z(hArFq^R5Z6>guNTv$}JL{V+mjjvGA#Xq1Ijvq13>+oP|T|H7JM&-*b=QH+4D`jOa zb_;OBuoPkMhavLFDHv$YAK@W|b^Y|*cR7PM){y#~sp|Jq|E=}jhc6#&N2>%c<$`%k zd@}0!BrURell(W_CNCUPqrHo!cv^7P<{Fk2r^!eCIkye2`Fawaka6|Emv zhMH;^%fN(ly|D?oCKKSCm!HypR}Cz=TQk6r0TVyarD))`bnQ4K=iJ%tEWF&?v-hV9 z#IRtevU5xAiExFv9YBRMtZcMgLd{2$fl7NXV1@cDYl?HHnoH`iSd3R|Afw%`wh{B- zs8#zvXaS?m1iphe_KJ-LQ-&8#NqD1szfl8DQY%^v6zMl^l0-H*_BXDzy zi-wneBXW~L5s_PAW}

{atjrbT2u>0FM6#Kwv`hA5HMy0#R$erFljjsh?l`YnVik4v_r zCyuSj&)1KAGNK&{PJ-#=WDLAzn-EsytQYM_Zgy0mZSg?-U#Y}R<#gP0PCB-U+@04r zL_~ZQKri~p9O?|(IDXHk48TJ=9n-Ca$(S4n7#U+sfe!6w(k855TQr%@nF0u=YeIVa zVTHM8*}{O+8e`9Or)?8#y`sYI{F+ynR&qE-M=y_C78#3M=ZVUnB5hEc34bvRX#qDR zq%vax6ps@GNd5UKEe^SV=!F6rZ z-D0(Z6jIu-FWlEnDvY6$di;ow>_wMpY;jf>M%=Bds|26=%Kg*VKjL=9<}bzSD@Z07 zz|kYbBq*Bmmyb#&<$idkPRrps|76yMCGO>Xkh8mi+>P*U#&6sLa!v&&1Bn&AL&9a5 zR`60I%f8dgo|A=Qr-8yH#=RO-cN$6yG=a@^i3y%HcwtYg<)tuV3d|7B zloh^%Py6%JXz04Tc!4}=+FA0>L9N7aM`6I={chGxaiCz9-@IX|Sm>g><}<}yw$b%h zu^xv`2~d%tyP`0dmnSO$o!3dMd4F``uXczVGE#h)@WuX=U|1QeJ|&_?z3f;A7SY}~ z{X#!jLS}pAF5Xq+ELrMCZ{#jJkpI_<`(L{_@7iC-$|G;w;Hef88st(MFBJG6ZA^r$ zv3=zWe~I@;51K*;2(m>_^(F|@yx}rFNzGu5bxK$R1_d9wr|@S3>BaOv@qKDopK?!B zJ7rmOt-6`ROKWRq;`DL>oqY-`E01v$974iwFbxE&eA3|L4_xMxIAa#~{r(BU6MSG{ zCTuQ^RSIAK6r%=YYX%@j=nc9Ub0XdKP;-gDYyy)OHFU#ZyMQUahSID_Cz++nB-J%+ zQ1Y{YS1&<7o!Fq^C@FnZ(+G}3=^WR&p|wGbB{`JLw`5bFDa{fg-fj20iUpFY_EPw~ ze~9?u7vSJFHRWYePe+6SI)XTh`>`j1}*fn7)A?|#ApD-Z7{r#gtPe|!kD z<08qP72XJ>JjR=19$_3zx}Uq&w^KBE{?&f_%^+$mqO{RyVuFkLfU34JxGYb&=Sl^)RuMc=NojloaA$wgN3e+9(Cn=?+ zg6n$GN+_V*VWy+xO#^}{47bsKr^6zY3s0iN^N!Z|{#!n-iOmg;kI)PcP~uCcTPe9! z6{{A|5w_am6O(bzJZI9GT1T%AVyCtSa@^xH$8ATZR*l!kj}{jz82*nN+>#wIK4Z-< zB8lG4<0=Kh3f6(c?ZBF0=b2$fUnzQrFw2s=RMvlg05H|-y9#Up=*4^MzG(Y$(6<(; zpfwgPr2W}v6fAn|$DV5)OTaUsJpnZtkqCfLcI2(FcUj#oDt&Z0X zB`+jcq$h!fiYC?^JMoQFlC&p)XPx#<*pJ*sZ%I`LI%B=~#M5PrYj1e?tzrJ{D+($k zawlu_K0wI|JeeShyN$n06{{W7NDCQJ2(`a#WzQ)TI3ZYI1q1lfUqrabGWm!l)DRor z({jsgS#;rWp#Iz~q4b*FzQmU>J6TD>&m?;2uQ6jVCkA9OLr6AY%+C>w9o{H{&T{{` zvaqT`S7mwdhe=Vh-|jA|=YYjv{xZQYTI}1;#6iU|sGiIIgJ8$-#zu^)8O7*U!Te9W#9rgI5zg-6gJlHPZ#2MA{J1BrYs|zB6bfI)eT5mJJBYhJd zL)2X4?koS`!|QB1X5qcC%y^PQ(OCIG4LCk;K7I%O}3U9n4wyLQ+D z9w}n>d?EtVD@`7w@-7jYT`mZNL}rQbZ@tyn{Bp`}GazsuT_^%-8uL4L{~(qGU4cbq z7YCRZ--Rmy-uOA=;_IcA0LH_zMA+GRTaM1GLlI~rs$eNZ!s(GPmj@U|f5*kncTlME zG8(&dauENZ@O9Rm%uU4PY)$FAzShK>NQ(E1)L2WK`CHldbTlFTo zL%HF*pW+gVF@*^ZQ!^h0=fO4l^lQ70S?$<3G5kUs~Q)7)rf#vM_r@W>Y?= zigqR>Jw{+aZL5jGK&ffgm8vmyqCA3B)}zf0T(>M ze=P9D6in>?Q4IViF0F4ng?eI=66=5d)j@?<-uJ>cmhbHBj;~E+@;;8PRzT7o@r7F# zz{vYa(v8jnh)YJJm;3L#TbIW;lh&V$r?kUesc8sjLw~OG>lVNXVZKl@B%J5$`Jm*_OCWg8#LRVn zWOMAWWfbt!CQl;`k=*hLW*6Q`^RPr+u?O0M$2#HKN?bZqv1G=B9pM98b^QUR!0wq7 zJLqK1gSTC1-6J;|=@vRXYZOr1llbI}RA~v;OMUIGD8dR5cc8aNo`_^Xpo)_?A0OsCS*>f9U*8qXaADl+^DKTplkR$_nFanHwsry)Yl#<%F*?;<*jXD$q;m!c#rNd1SY)qtx z)rdLu(Ig3puv6Yu2iK&~R6KFkiTiNa z-OdA;0|RwS5=nFmdM<)s>aSr(G7W3qm%>UiGM0m7h)aQ;<|snm{TGnq?ZKQE9U3I8 z11|xmU9S0;p@dH-weBt+ny==i!jC<{-s0PEGGC!Ac`FUC=Qu&C9>|iWjG-(|IN_iv zw5RLuumG|?!e@xoc}_xGfLB(}?HBhCa0*YNTQk(NO-V*g2ReV&PjzeGq%r|sR1X-8 z{AY}opst;PqKU=(S@p4!7y!$I{G2;1kZ{XK{egm&@1wZVU@lA{I!PwYJX?pNUw0{X zUSj&_4RtW!CW<<3NV65hgsMo6(k6NA8)KmhHBD(Oe6y|H-1&Kyb#?)$zk#g{0FZH2TKurCsl57Bh^5)qsj58;!>Y!nxNIf z%{==NG@A1T9JxllA}s@7oxshBFDCFqWX*;tjdRn758VjCR61h_KO|3;e!#X^%)4=1 z^F_i-!!v~9U-?`mEG_wbzTiROI?*iCCqg%)GSUh9z_*1~9Yq;8*rxe|dGefLBk=a? zUJ|3l$}u!(nFmi&+Jt-UQoia-`3JXgxO2?$*KT;2KH4H-H@d=e^bp50pI){2qjieV zJl$Mxqc5ovdM#LY^RTV&-qYqm_P;#ej19QZ|1;zWK!<2zp=Ygat zuzX-M<*nsoetC$^dkTyEe>i*Vw=CCf{afInq!Ex%Lb|)8ySqbD;-NdFySr0qX%Ok| z?k?%>Zg?M@bA8vIti!eU@&0iC17lnkbClM zuhasKqK@?F_=izdi}1!b$+eT)lCn=VXb{G%yM+jflO~T?wVI6NJDqLFhAgF3rM0vY_!;U9O_FP_aqCXy=DxZH z<+vD@dV_eK9|mdl=I2dJVjbd-TkHffJH_Iqx(x^}+j-pB%z`5DpefH^D3BN4TehX& zGzX{KSuB*puA4}P!w>(W4aj4KNdWwMeERj2DOdIyE79|_!CJ&;Y{O`kw^{H5oldwm z0u9J(LaNxS8&0e@wJEnRw3Q+la!tCRRJMXH;y>HT1q}>!K^MSZZ!`ab3H;snlz-MZ zW8PyR%L0T}mWxHO`=3$4^z@>!$62txK0);JbyIpEz`w+p%GvU>z;Z)`Znx8SeqevU znla_*d7wgDWB`@70PQhbC0w0;#Cu_^>NIE*bee6#6Y5{7FdM11(-W@J`ku)9G&HUX z?J)AiEVvHVrR*&}I+7@Wha?2nrcH-D+mo-h=s*kQYyx|(aovj#%GUYW1*%Hf{>OJT z#f-bQ397+%Yo_t})j&F{bn-&exYy)+8*6%%c7l^J6y7td=zyCcp4~}gK^ZB-Q%1n7 zrjZ~?whoFR+HG4)Xfmf@q~^tb3Y2%f&|s}ll$ng9X8**L60x<|4f;ts_6HVkD`^XY zS5Xx=t8e=6qR!ND_fcK zn=0REWGcYCHz`5oI>-2i_j&YD2GhQPp*_t=7rf9*jmvThWu+k~Uwxo_{R30-kK6k1 zeErK(_8*eizh&vKz=OY&Q7zbu#eAgZ6ZB5RBoe=l0zB7J!1t!*X?h&Yz}1-A-{Y;1 z2{FUq-Z)$>u-k5N#N0ie+(OB*dA1U|g?7Y&;DqT$YnAXYX}>5PP3twjpEedszCj0yaxK}Y z6?-ykQkh^+q2<$*0C0=xv8^Izf+yhY<{8wP-ql3JPM+H&#fFZ_Wyo`8&sjtA={+KY z7e6#dDgjR3gs9!4@zE^dMj2(!`6iiYVc!s3418!znS+xGfmV zrXg!4dKCB+Nf@}fK?0g89+u}h^N2J==_ZJv6<3;|SuwR+8%?rN{b?2EyP4K=-aKJC zblU2WU`2x|NR*A=vj~zBD*{;jq}!+XEoGG98Q>a27|Nh$H)_96)>HQa2N={%vRt;Q&C!uv)PhheOWlGd$KF3lB28OOyy4#BDHNf95t=r+~N(ncj9$pO_^)TtG&A|svyK<-s`1q#NoVmd$&%Yh8JJd{o!mYR#ZJ`-HigsAXl=f z3rsIl#NE=J<bV1esMNZ?r+@Z z;GQ*EBL$L%JKrhHx{zw4p1cmYv!4w|K^xF}1J)B?d&1ntbT9L%9)_e&&{feKr!`gW zo)xB#4m>MTbNrN*9?{5P4pxlNO8O*w^DH=z>1vJEI9-mj@FsIrB6$mK*tDtWI(V}% zxD?n_a_zsF5^M=`((IOFL*t8_^EB>|6ORp1R;pwnoYpCA z)glzyby0#0f?m!*HtnV-S!d`{xeGHg+Wfkhb{CMs(bHmdEvVCJ>iq5?^Kd2sQJ~9w z3%+=j=1Xqt$U|SPsR?(tE; zLi}9#j|stP#`ntP?@{`pyM|yr4!=Frg$MQ?na@{?P{cXL9GR@6KnBB}9 zf2wH-UG$T9Wthh#xdyaIJ|3rPjb{oa$rXi|4a`t5!Af!}sk0)0H)rzC)BO1CQn#j_ zJ5}oa)I+Ho&G$yet|mDoi1*zST^7WnacGYIj0h@B8CqA2@kY{7)D{M74tdF zKTnpu@>fWupf%wF>T~^BW&v^)|6UUy=)ZxHrH%88wNVU$mx7+Lxg^NQuUx#ehY7hIwP@;RH+XO<6k~w^L%qm-Dauj#zyzc0(71 z@LAfKY!hA(WBG0Rn0I8^vR^U9<1P@}&l}IOU4Yq2_H7uu&ndr=IrObVW?4)v z^&Ty!AFweSD@>R!Wvv!0ecA~uMBqw76}PRljKNoh*#)oJ@XX1l`s!-VCCWwL@t-T8 z#9w>sQ$29dU$-|@NjSR@PwYW z64NQ+FfU6|Dww4V0`X^s>FY%XzYkpl1%k5k``MxKeivO;WWwj|cwk zO#h*NQ%u-=RX1VaB9}uPw90aOeg%k1#n`K7){RF#a&ewYHk_+vcE+$b;(n^a4l6na~pO{Q~n@@s-PgJr$_T?%MDc& zC4D6OtsB84JT`(B(jLd;;+({(_5s7XMifIlWc%Lu6Qgc~ZY?zwg#6{?Puk*zdjhxB z_&D=JIN|}YcD>q)MI+T_`&>$Y_@$@b#JkI^nNh5@Bszah=etxec+>9BbDAC&Y9_#1 zef#%rQZ*CsDI4Q(k6|iAD(!vs%ijDItZ8?Vu=V7%L!tq4R6=qYi>_#uUAni6Z#4^>Fz7Jled>MdD2tX*C3Z>inA^Bj*!stm8O=3CBgng-*? z%x+!?fAK$7SNVW)|2zlq&u_UJFtRjm|Wv?47BFub?QNIq|c{<@i-!ss|;X>)Y=wD2K} z)oD;R+&?OpuT#YbhgfXa>sC0^MpSMj!JOP5?nJsoqeqL7okVo#^l+R?<7c^n7=!=52kCn-LA4|-c^)l8Xccb;*+vsX+ zm6L)$CHjA|m-=yd01^Uuxa8HTxJVqOMsfD` zpNC}_o5xZRD4#H(z0C5z?B(ZH)$bGXKV60Lzl~E|<9cw)$jH7;2l`S|2OzXf8HqX! zi>opJH~^KoVy_Z$XsBg~N=*A+_cueWYv%`=({ihCp3Cq9=ho)q&D|brpHuip0b}7V z`LR%Yn_P7Al=}U-u^)pOQfziy@G3-u zog#$Y9()u@=Vq4oPHy|B3|+$7R8rtK^cmo3FP~J_F$8vG85OH{a2z$TC`$cMFA>@m z*Vbn}buZQTqA(d;X|kKf)lpTN5)xJO8^cS(j6zS_5X{gGK0)feYb8ZG zYzx-MnRbWo)U)kiFaZZst#T=sj`?>lUSVA9Y?Pl%M-Gq(^3UP=%hCSg`f6016>pbC zPAfmO2QI$##SbL!V9F_(ccp<=w_gKra~WnvPayZ{6FB%%2H-VIVzNEJsEJJ%T< z4%BYZG2UFex&^?PwJ@|LFP33V#sue2OZi|+vQjEaY*JD%77KJyDLG)i2Nw*4%+D|e z=w@0`#1D6dxQbCR@1nKR+yeCuQ%)B2(LCs1YcfhL_f7mz8t=&!6qC#&7K;VYY#9%f z8&7>htOz8rQVB20?;|&l9dI%Eypi_)M~<0!-sM<|;@qMD=7I`B+3dDB=uLyt0o7Z$O}{jZvXuHPxOVv=b^T?0v7O1Mzqc{KjOzDE2f8KLR`-3q z^yav~fetWcX`@?u_f&I)Xu=tjH3)a%&8l^nnXvKT`ygEn+8ztBMwAq#b64b5z z#k%@v=C3oeIE(W3qV;=37B%PQ+6hMb@yTbnZq0WA`Bk^TL|}Ub-E`X-W&orr{9?B* zdFbM}rw8`^3u7P?yjh?$DDM&fwTbjs-v4M1j9QXzqeBajxuN_3gMH)(+q*CPEX)=U zL=}|6*G$A54}%I>4E~fCef?Pr^s^9a4KLV0cjB#;b~HUcUb@r0HP0ca`?d;2$8wc? z7qiN$t1YL*ro+=Fi(N;&M}a(@OV9dCX6)zqQe#wP?}hsTd2FFb^GfSxAKDbIg_KZo z?`ew}f1IT(j`7j!($PAWz3EEsINUcDjlNQvG$|x3_Qfl<8xrb{`K3fFbq)s_sk7!E zcY>gIr;1JD6!-hw^Ob*PZSo=Ttp?Ugz9$SsSMTKZ9*EFRKtO4@mnbdl*`3Vv%SXl+ z{VHcK-Eb#tWsg}6ezch-Qxl+*XGQ-(renF|J+{hJ;M{JRlh3jP4R?s$;E5Rldxf$= z0=B&U+a?@wWG_rf70&A`q9Vwle9sSMxHex}alnl5??PA!s+T$Y+NVz4Wjos`PG0=x z@0wxQL4xw53Hts)CjUp1??rxoldu1(;>{zPpL^_G!@iGRV+<7r!mUzh;yaNUf>;Fw z=<-?n(l!FV`YJ(-ETvs75XZ7Gq@ELC5~Oa;XO2s=&ogCoK!9e{n$i??UG~AxwF%&U z6b9k0*c*P)lM=)c%o6N}XbNnv_;t?rB7A9}bx;zErkG|&%+bbcW(mES z%ZN~POykp^p8AfrLoMMWhMmUDckSz*5dL}X+r&^N`Gb~wHQawQ?g&}fo4>dh4wM%y zQ2Eff$EWmw5*nS&x7F$%_^aTp>Nwe>_$=9qptZj3rxqV&wwTtss3xEM%X!P?8ufX_ zd!7J88OzKZ52DdNtqYf2_NTJj4mmd2vMss0-@G2T!b63bDk|wLi&D-X%>}$M93hz+ zC)UFe68|{!c_#D~wu$OqRe84+HeXQ#;k4t%+ni)7L=@NWmll1Ut%IMe6)w4=qHl%C z^TVYiva%!N7;R`0dV7*$6@S_rrquA34N3~hh?Va9m*jKQXk;DHlQXff#M;|G=sGZ7 zy)wcz|B&cbgc4>>pen7H+9E`E^Z9-2s+^}%vEOpoo3C`DV*WWGwF$Pa(2tR}{bJEi z^yNXk;zX1*F?HceU0=(_HoSyz!SDlPi|aB?r2w6R#EI8GV6L#L6ctPvO>9W8ei_~L zrH{#t)$yzEB!qDta({IVTNbf`^>*{s2oc!Yac5dNtFH@FizCm@Pm~%;3M(#Hv{0a- z7}9bOyacE~$E;D1rHnNd%*Riyg#EPHM+U=UZYS7m;%?gV$k~1!S7Cz>h%Ak61RcFO_FOBQ^eazq}7j}DHGTF=NtHDtNra%X=THT{E36r|XG4W1B0a=KV z4hfD1V~bwZ=7gb}(yFV|h&}wtf4J2duj$p>Pp(00N;Uhdl<&0tOlg{i%GcYiaSx$Ti1}mxmU$gym~-)}qYGDmNhkNWi(_Ki$;|L((k^XV?Dzndqdb<; zipU6CMnL?Y(U-h@4^H6T0Ur`9pm;`&2bZ0d(8%pWcrH9Jd5jNN_N$Vkod9hDf&eSi z2AiEwPkA35+JYVPhJtbJ1a~{%e6x^p<>l2Nm&?)O5TEnguq$Z5Cbz6`Cjpp}`9h*w z#d`7pc_kokAYYRu+Be*nvy9&>PA{NkOnZlV7x_Ah4DSmt`0Wh0V?OiyFzx7h1%w2Z zaYQ~H33JX8M{BT-hvMXQ>3Wrb2#g!vHTMUk#iwkK!mp!ecbK&O`Mq^CHnrhvgl-m# z->50F+9pPXx+Ql{siWA|IVWF5@JsXD-OE+GuwFFytk&T&Z75B!K=9#bVbH>OF8tb7 z8NhAgaIR#d5Vw;hkPXD#2^9>OMM(6b5?tc!>*_S&M?5C{!e(vDy2OHP_GlO z^!jHjW;iU&ZX(Lxly~_H7V%Q!cogNWXQxV&#@H~R^ndiXqN*GEPDnK<+M&fRBtUCE zev9iNGyBEbKuCjznn$`gH^KO{m%_^{RSn%9Jpb*d-~nVz&G96fbkslz=+-Ku&DQ|o z6x{WT*I=ZPkkA3T5)tdQ#4rt@LUy=gHhA~PC{=Lm@Ji^4#3^B)dxzk_Pl*MFb3Ik@ zzHJZ2#nZMiHR3l-1(taQ*w8g^QA;9YJoe_4NvDU_`sE_r2qNQ$+TrNBtL>CY%19nc z?09rU;m>QObV^IyVr+d(s9dl7DHPx3I{~~Tp$Wb3tY#GOUitQah1dcR9%zir=Q z*iVk%Kx{{yr5((_UL;HS6wW8XWl;_hcRAvsj&as=YOG(6)n`M(ls3C)wM2@nq?Y79 z;}+m1ZNOr$A*3l^ziqqZ;><_-)H&PAII(KaHTa#aqzSMl zu*J;O%@n9I$eeggXiQX@XvlPXY1e%k)@t-PH3CNm;f4%W&8rna2ESsA`qix63hYT! zZ~~ezP(*j2JAsq)a19no`|>0^AkWnm@`OqA;)HoM-M+n>mCLoiySf*!;8jn3M~d&y zIebjD>SEPw{s_itmf~^V{{?o_l{113F{tVWGID-M1%DRR)}XekdObru zs0{v0{r-u;Ol_n0YT!A; zn)`7$_hyS8Q#ypfacRWtmZKsMW z*1rN4RmaCKdqPv}aw}S1Q64ScEfb_xsMgGri$=@WR7Mj+@iXLATh?uF^>n5{_q*fG zivQI)JK`psm!VW>B2=21V6wreNMiz6O^DYorvxwOw-&^IFgMjn0(kOf8r-~@-ol6> z`H_d{k=i1b@it6}_OeYINFbzOph|j-nX6tE=zXwPB0_Dha2fMBC;x~f(fn?ZvFV{O zFG1GrVttb_uLOZ`0fxH_`sX6KKRoe_w|U}MHtj|L(akGIc5}YqbsgHayGSit|1+@9 z)2VB&m@8Rd2MW`K{)0zC;87sp0m>94NH+3cjwmN95H;W#NFt+W0y5gZsK-E3>t~-; zBRabF^-K-!nsBtWA7(2%OOSI=T$+f?yrdu)TGrMbmR&^m>-d4)HZ$6Nx-;H;`A4 zbdf0XcDGd85;SgOP(hKIoZnoAzMxx|%#M&6JSpw0x1939m=qH-+Fx9BvA(UuW?Th; zOVXV-kmxWq5kUkOBz$z-_j+MiB$BS58#Em1>Z#H}_phE}kse=p#9FJ%wisU; zY#D$kM8mb(XXp3FfqPe?E~O`pMC*Gk1W7g_++Plz9n>g|f(vo%@OJ~V$+H;HP%PSWS(f#RxnxIfX-)<-0> z0vw5I$6^{1#mWt_#hka4c zfOS=Hf~wGWLOj!F^E<~GDuc%;V>?Z6MUC%1eytp!s-~@Pdivt;^sbPk{VOQxWuQlB z@;@eB#2)zh#nydB@I7x`$R)C-EBysy28XU?#y}iJ{#VRiWmc?=%JONz@^ONHAQ0a3 zVG>%vZ+De^8N2&Dq30H&n>D4p$YMeQ}$Ngi6=6vJ301q$~DRYkZ|s(2Ep zUqFph!+=cGE{gBBu{#IUsh=Q^H1yL(i7&6NE|xN~HFaaHlV2JpAYr{jUPAdvQ!7goz0h+WU_ZD~wK1k4Q4k#F5a;aB zI^HQ!sEbw7Y0?$sCYD+vgGt|bd+{zK;FsI(Cjk!}hiU1w5Y#pl{<_T@#J4n+cPV8; z?y{rpLl+z@-hvwJBHk`A*v;S4(rtR5`g1G^(+1S3PI332ta6L%-Tap~j*J*F4@m37 zUT+{tAo~!&Oqbmf3hPq2s-XwB`nJJ7tNZ+7=oVWUUk|rwLJw0mAa|Ou-Z$YmztH$O ziR$T#gSIsaXwC@PAG0j>%w_{x82$T7?Tpp1M3qDK+(OH~0&jGx9{b|+xn8z>DwhC? zyi1Clh2QBoN6jJPe8OOObDy91w%_#_`g(u(m=)8=5p0%P%8Xw={W67dz-@Eu>k$t= zxW;j4OYUOD`?(>L26O9}Wa*T>i5?2e#C*mICEY4U1Yh1&$%6tl0cMTat3k$s3?_aZ zG4@y-5t~ZQggveh)H0KF_F5)7$MxhGkwGzXZ4q(i?`2r5aPv0OLtD1&?4&v3@7s4^ zKsfqtlmddBa1JRSV8Bn6t*`lN#)pRm9CJhc2aWe|$u5;5I)@PEXEepuE69P_j0oT% zPi+O|&uV}pQ}SHtX5<-hm98x}sv04`agJ|QB#9eVpBrAMGwhToCMeFm38abBqgogzVR|47TU|v%)vM&NonLZnCkTK!X#wemg zjh|C6BF%Mk`+WhauJ8S25U>ExbU}#>8;a)&2?MtxBfE(4-5F+8x%e<_k>2@Rheo`d zya@GjghX$$n&!{3EldN;)Tu(r-t6nns+gxAPEk7oQhBA@P9s$!RRnHLt!#U$BP|22 z5f>1y>!Gh8?q5wo+|zaBBU}19F?K}o-(#i&u6MRM+!v3V^gLd)_RE3x5+b1WXbiFg zvi^BJf;_>}pqFh2&`h|0O(Xc5UGP`MHyQWI64cJ|AO2Z+EjOp$tie;I#gx1Zu4fL* zm!*-JP0iwk{AMgf7pr$_7V8Ix1i#+u(HPuXB~PCd#!OC&Nwz#{?Oej5^Hr+R5Xarx z*0d!ZlFTxJ*Ux!t^op3=pBnOX8D_IOoN7$jpECPQD!4Y2!zbm(oF#;O7)hagTXtd1 z$BFVXlU!AJK#-4CPOpvCCM~WB3BO>iou8Oq!y6^_7J$Qmrus+dE8i)N=#hc}YijaS zAXknNLw;~Mf`#N(A9vLIXVO0~>5Wgnf z-JU4w&F|82Hb^0JZgzC)ZY545TgU3+L*HlmxNtoJd0j_zW=&9k{5mSV;^nXSr0{W} zKql{2#_cLoHYsn*0GEl=+ylH5W+?BGoU$8ra6#LP6o(OPk#gsW@I4-UZKo!9CCq8% zt8+@fPg7i-k#=_1J`M0~K+&O50A(k#>(^c_UxTUeJfwY8aLS+My1o~WOFH)E?g9nK zx^5U>*KCQU@%`DvC%P)EDSxb%k6gmCP!+}Ul2B|v&^#RRo~zv&km#>Bd)&56mECb& zdabtMQvtLj%jVZm1U^tgOdm2K_oTUbj#$Dzz?08ifAF7=O=}tG#-KqcVzXcTm3R#; zjfpTY(hpnIZl5RYYW-$Y2kd@n419=C6Y!2+(blTAu+(?1882W%w)C5pUm@Wa>WOJt z?EoBsTaty>7v>X=I&RyKPz&$Ju9hDo-RV~IS~q#uCjAa4S84y*T%7x%MhpP0&3RBi z=FcKpF#&s9J4^HDsXG5&oPVv#;(tlAyrOYYMaj9orWQg?vI&#RqUFdN7^7RqqFTSX zXk(f0nYa|L-&1t2)H(;dl~w9S)Y4B@liEgm0Ojj%Zk`))Sf=7) zi@0zR_7WgFDrmR&xgx#M*3T?03G=Z(h_zWMFKg6I6s`a!`{$b~ulEOzJBSkYnrlx> zf9SqiaKpZuVa2XWJ1}<{UtE}*0XAiA-o2+fVT>b9MIcW&Ik}3o0ms{gj^V4QX-J-$ zYgOvN@DfLyxqt!)$J{(tr7&v}2b_J!AIZOB{23u{GaN)DKP0q%(GZs`jP^SDE@3>-sS+MMuyjEjr6*<3LNXJ-D?t0RN^ z?NP`wCUBP39|(nQW1&7#(KRJ^CLZ((AW z9T>E~Z!Xj9y2vWmMeh>lB*>|(xa&5*ru2J&E-N?}n21=m)_sEIq~10OsJ@%X^zkx~>mMOiD`E&@ z>6R43z_~Tw?3SZzUw9+aWA4GE*RJCMFVD(F7GMj{>ISZ?g)==8CDo@ z*k3o1IA7k!DxNt}D=s@@%hSsg0dN;I4YkLE@tS8|veB?gn#(KZTQ+R^wi4ZR2ZpeV zfJ&wUO?V;QWnh|ZB;3zAMa9^WIS^~t9V^$Z8_LjsPLWXv{wXMqrlxFQm;zO;q2;5j zHrb-_$Bh`LQ-*Z*;T|m`fIn^meefj=*xCv`5#8;bSzHdNPq38GTh8Fr&J<&QxX1@x zHxfQ-PGL2R2902^GjlwNrTw%;^68!Y(F-jF5A{Y97$|&^pzqJ7AF&tze}6k|i$U)e zzt`#9+_l$grweC3GAjtV_<`LISG*Vl`wKFs*;q!lg+srg!hrVKGSaa~5QsZ#VqOELF_2O|84NsVEiKfkxWt$c5 z4hiv4NGac}mB7KSeRt&Y@0uw5^otB?*MsjY6ZetE%l9(%hefyYqxk}y2d=mxoWtK^ zw4o(~i}{UevR!5e3MGer51nA_#Xr1oV%^blynRG>W_9yzjO@RKZtJ~Sw49v}5%F!u zn+4YKI^Eh7Q--H)1dV+Nd+N@2dyQb+YFOr{(@LY5JPG32P#l;k+`SNpf7>{Xfd|D= z8}yO>c{x58+6tETHu``2BBdf^o}<`llrnc+x~j&RU5&9y0^)oU<>AZE6-18syq)bJ z07X&a2g>Or=QY^9>}qc36ni7|(SzaU-PRc#e2q7amzG+>Yg^K&8}-*{6bY6+X*-;h zN@Rr}_};*HouS|9AR2wJPhUkR=>zz*%-RVjz}-6Gjegf61jDj0X1dOz#>RdSsVFw@ zsESvOO+tH&teZH~OsK0uSJYI3yalIJq61+9lm;q3``Z&O$z7i~4G8yc8?v30j5ekr z)8WZ7+kq!Mw7OowZ`}$@(N=$5K-oUDM5u^;g<@l=07_n#LUg@|HuA*p>imW z9)6a^b~Q`k1HAyEUhydrd<-}uz~m3f;*pswtEI7I@=>qVQ-Hg62aqmdXhEcVUeAhY ziPhZm7BKR>Bugg7y9Z@k$EPg}HM7Re3VCG#L<4cw<{=Zn|Kp!4<5h z?Keo_=v2JPwoqQI{J|bojxk$Mgnw>IY*l<|K2KNWA-8OY1v~R_l1H)H9un4!hoe;`V)$0OrwTSIT;-FiUN;{crk_u@7^+M~6@J$JiWaBX2M5z3r0vI|9Y z69;vqJcsn4q$YOVX%{&J=4NyBBgv{ZwGT5zb=XE@p`fThvpP3sps8FGi2vA?Js)Aq zBR{-0c252FHhY*%;$iT?Ocft|WRo~0A;-M;f&{mBz94%n_kZxIDq&8(&EI(>5rIMq}>%1IA zLI`$6r<9Y@@~vu?cs%1yQg!_Mq}5~*Zo*z1VdmEflx!Ju!!t%O$}7EvGHn4Yf*xv7 z=qkTu8Ylz^#oiOC>8z@9is;M*oHZ5A%tW;uFU$lrluow=`%QJXC2yhY_OTx*>jnlu z9GF*pm)^$``#hB$SNNiR;vz+*52!l3PZ`ZDT^;KP8xDLtw#CV~i1=Wp$W5_qHSk4n zLkJnDWmr4=nwGF>J_of!0no4gh#TQtY#V8d6hG%QzxZ!k8dsfumSoX`()M2p84Ejl zmKW`!7m6$ZEmg;X|IC#4N!zjzIW@n)VGNrn-7IxVSft8tkW_>Q| zmR43l`fbjOQ}KmzC52(iMP@m8weJSwE=Z%4Y0aoozyj&+zcxsiYB4KyZN5E3mfnJk zqUzk`#;~r}^;SD_MRB|9FQ$FeI$facvwg#2pP}^sM*NzH&toE@vrc8_Y;iNGsjb6V z=szQjbH_mlW70>e8a-z&!gI^lN;4YXw)B!9gt1I<2r0OgKkjAO``wR-R^w21lKQqJKE z#0NA4`P%;}S}aWeJzBrlR{VMQf2^(lq4xV%wEj~2VdwewKhv>&BolCc)3L4T6Dc{c z{M5~7pYhlkPoQWiHWc*7{^ny3zx|*UJSjkwY1j^^_;vT;{xX3>`@V6#Ry%g9%d9}N zP=_r0*p1xpe4&iS%$!YDQzHf~{Z2JcD4n_{=p3#PKTSHunk60H=rM3(Fm$AGP|4y5 zW=M&LB=v!_a_B2;3*<+(7K`r(Gwmoa-5|A}SPJ6Rf2jRLqp)4iK|7HPv=je~xDoq% z%7T~$(C)WS;O|1iAbj}ww<6%V(7?kZ_(jnnC0=BXRIB<0CWnG(y-6&f%)SCAT8e-> z85*x80JQ>L&PuvD;%2bh)wQY72eR5qT4%v7tZS@aNsT;6KEWiUEo8N-K(Uyxm)Xd> zITOLRWitFAi8PU_A5{Gys$@Sv z3^86+(NJK2HpaZhK9X3N%qIU8cWn6xhKJ6IcYU($(E_c}5GaaGeD9=}19^X4nn5FI z(TC-WMSb*V7fyS7hpU836=O0?HkNb~NDgvcy#bP()Cnp*s;7Tj&BmA3(KfSfr*WLb zwt}z>1{gj-#+nL`9KP!-=2s)x{+vXhUS&8Y9QCF~UJRMd%OJVrum*#LtS+2*hst)X zuP*oJD9*J9P@qIH`-eO?<&=0HnW8Bm8Z4fdc?lM}UhutAZ|m*%vuW4HvM)mTXIxU+ z3a||U?SlC6^k$tu?WBH%?j=OxVPYtqtfvN0ld)6eDK0b^~(14 znD(jdyQc4~j`z6W0wmJT3X6r}UP>MLLJoK(K}llEa>@q*5o;p z4mU8~xr0nGyr?waBzX}sd6zQ{PyD*E7|NF!?M%DaPLH7=`W-o2-O?DnN*Lo zB{ENcb}*+)%Ob891Bc8KA$9xW!I67H=2bm9mbly8^)Qoo$f6vFr>;b@4UK5D_;J!W zD+8#gfX-znw5^|@OXfPR6sOB(|GqF_G?9mJPg;Yz0?1std(cH}GHni?1Ez5>`E@;? zjWDuEkrZ&cCm<3o^$I4o8W=DuaqrcVAGT_}ZHwfFQhXHOs#M@uUvVel2(zw-=L6aH zIbL#0P}*{fEZU4b6+ZB8_n za}+ezbNbjRb>$s{xR-<`l=Br>8c=TJK)E6L^DobHZa)9}fuZjo{PxK)-sf+X@8QM)xiJ`R}a&z%;_WO`35ax-V zSKc)O`x#Tri$B07XkkZc93}w*WA8OGxQOXfuVW3Z^3a1`nAa=@?7PcbJC^LMdGf)1 zK*wi@tVPQiI-k*Zh8kZH=aVynlhD#W+5@vz>Lx0SE!yle%LwU4`?ji>r7=<=-6Zo) zQG|>}O>PFiIjdQ+(u_2kykTn+SY&J{k4B*wq$64WIs`p0;8r_^6AID!fjZ9d($rMN z#=<*=#|2>GdIeLi040Ku9+!wja1PXK??*QM zR&Oq@>w(f~-?GyxB9;e2Y8Ij@!lbSvGi)$);$c6I&n&j@<_)WWl7))OkhR_vYQFiv z21U8PHLOYZrVlH^Yu0SRA}Z}cwqlnTfDD&@$OK0iaSvqtb9I~uQW(!~i)S`z31La; zk*W&cdlpU4d*2fz|g-XsTHs7_J}ui$zhnATr? za9+U6=Ds1e-w@dqymnd(OhFEy5aIJISrHU?8~4+k#XbpFM znXSI~zjTcX^>6@XbsN;k|Ff3p@2m*D8|!{VcM zBD8kA0?qncYl~$7Ld}x{^Po^YB>%#{hrFg?BC*$9H8UJa<1iYkjj1iQ9HvDMAe`Z| zG9IMzx0joK5^xBf_a|@bm7XOEI^?y0@BB+ zJL#AiLdAPqe|Qe5xz|j!t)%YtwHz(0<$4+UE%M_5+t8YC&E6=&!Gp zoJLoJg4!b?U*FofvKSsM{+zCGTv!hqkf9nmqmrkTcOZbHj*23MP1Gx}o zg>Cv;8LTzgN3ZN~B@Ef}3)}nE(0~epp$yJ-UCNI6A&Qma=%at`N)}5oO%%{=LJ4{# z{9kval&*!Y(Z9HVi*e@9!^*pj@>SD=J-xk=-Wey?uQzBZpuht48ZgI43Se_cm^#cN zq_}DhqZ+yN-!vyS9Yb8VMSM;*?Ki>W4Ny$T5dpH5YozLNOV zUV=g40;evZ!-DeLMk*9lCs^^JXYSN50~2%-e%j@mA_I-uRCA50eD)|>!q(GNA$i%Q ziC@CZOSX=x74P0Lrk9#qj3GF#0Uao>d}oKNV5laNT#p3#mz||si0_DCx9j9M6+Rhl zv3N~bqStGsz0&Ei;uRI((H6{N7zi91lEaA#4x51+UAtxihg~E?PILrhMK_+ULlbYr zMWare-=)?CH%fVKK5WB;ttF}x+Ci+iRF0C}-qT8g6h@{64^!|qA*w3<#p8tYw*}By z@q#)9$PR#atH`@FVdXO4zIgegiej|AzTo1#6_QaopS;}X7VGYfJ||XeP8-^|RQ@vY zv}{Lj6){<+J^_9IiA^7gMv)Hb)qmfP z%oLeJU(Pr`CjOO-LH|#ePOLgUH?hc>kI_BqIC`uEKd3V^N~hr6M0GoIjN_9!DbV}% z;xnrCv^xoyAv-DTTm_CgONJX+2BA;=1jmafQwvlYDOf2IK2PGnB$?9G)bWYFl@Rc~ z|60I}NBD~WFnvND&{i`u3v3|v%TzJ2tU@()FyeT1SNju3pRnmDRNu|08Q4%PLz0QW z703w!OD@v7ptpF8TIQVhqTF?v8byL5M2lxYsj+F^cgivFNei&~$1JfoZzINk+I%rD zaBGX2I~J6N6i^!e+|zxLhL;^c`M=L;3JMCcCY4Y8@&TwFB4}@X1cSa4A!A_c&)Ia2 zGE6#{@UJHPCbh2pY!K5-EUVuda7(k@dhVIGl|jq{zRx>AR*mH@eyL=)8U2A{oYOxb zc&OJ|zN7|`rao@MSdrGs&w1FT-fwLh3HrgVazyV`nJzf>dB;- zBF$APKWmL4Ws6ZUYqls?PD<%@Ix#2o**mV4?f3#k&UU>`;wsZixYLI6=F+W!1+7iG z`jEL+0H24CIdKvYME>s_U6u|9jg4jp(}fY?B!z%}#CFuKQg8J^9u$uShRsNiUBv z>cEzw&=02(!>tXUGg19?2wFni8qk(i@w$n%{NtkT0Y%}<%&JFg2tVLcY}B7V36JwJ zoxRv>H#Wa24?)%GM^L8!*IQW07IcV9S$_Ukxf)I{1F}B(fyP1_lXGa0(NX5=vE0K4hDKL%x$V45j{|5|KmIz- z`~t48H_ccU%az47#Pn+iNTlvQ<7ZbXi-_|Ic0$!W6BATv2keg2K`40 zyVvz7Ii>p3D>dQefR>txKGhPl!JM;@6>98q1VuDYcqpso$yY-J{`z0xeK=0s&%3`u z-O_Dm$^7&7D;t(R3`Ck{bJA{E|1}`?rUk{s=f7dW?k-sjVK@?ZHG;D0!XzcIK>R$WZt7ol!&iCVf{y%eG*Sx027(~7w0aaMKoeb9N z3&)r47%mihm5=t_2Q8V8YXj5isD$!j(;*~3hd7PWF2ek!1f-U0*j#G7p%(N$1{U-B z?;4Dhpq7=^R4W`WB6aEQl}-I_iH1V*T!2gsMMLZoBOz#YaZ?jK5gb|U4 zo=Vu{wg0ek+ls-A6I+HA+qI-Gd(LN4vZxS2%(a3Qu|yqA%9VI*qdJjfjmC>h%bvBe z$SSmZGwoSzw6*+`!Uk>=A*qQYMaY%sJE#cWn_MA^j{!N5jAPmaU+hK(1014DaN`PR= zu|Z9MbQU;Q(y{QKxisB2Lwf@It8WYFhs<6!Z5@Ef_s=UPFIDoNYN`Jt?5zMq1c({n zFyv1><@I;=QRq<{P^thCK|H1yj~NKw*r(xT*7bRVcUITpH#2vuI6&dioCewBpGeFx z>>itJb#?Eksrk&oj|@{KqOEor-)lLRxRilPYOl16PtBAxE+oDv8@1t74!RwHNzVhd zx$g7DG^Rth{J2&sy=Eg}V*fgtryZk?bqeof&wD2MyP2BRaQ##-4=ouvQJ=$r!HXbC zNuYYrYeld?wyeea5&kIp_Q=PChlrf@qoXkmuWK=r_Ex{+84r+tSa~p*i05cW9$Xt@ zXnoJs4~($TdF`~F7kEDMPqw8J+H09z#0Q9tk>J=O{hwhL_Q|y%(p)BFKM%}6xJ~I3 z1i_@}RFZ&lTEHrqk~`|^e?DVucK{S|^`)er^Y+_JGmzuWdMh{jqHQdOvO#Q;GTsN>3 zSnF0qh45!>7BC~C&{0VsqzK_#Vr(1~jzCVV$;N}xaMIs3jdk`oNEn?jlEp}v`e{>4 zj)Sz97mDjxAeWvA@Ux*3aq~n!h*lJuL1}spQ5NacnQ)5hk;e@N%EVd9v4%nxv9Ww0pAK+YR<85EVDYYRz;e(}m!#rF3Y;RRmi8wt7IK~a$ z{0RfX6Ae|tzNwk5N0+=FPYaK9rSoPQkA?PmA-Fs6S$m?8aTdnKtv4h(mR>R&?vTf| z7bDZ@9Si%9HqCe z+De%0AEAAHf&p!%59H-y1znhb7);dvL)#WE6=7nsP{A# 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 *xfeJ3us2doreCavc1;# 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

X~GTHSxm-sTcV07E*|;0YGM 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 dTp1Q1|5dTy!-r + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 FileInputStream 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 checkRead() method of the SecurityManager + * (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 FileInputStream to read from the + * specified File object. A security check is first made to determine + * whether or not access to this file is allowed. This is done by + * calling the checkRead() method of the SecurityManager + * (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 File 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 FileInputStream to read from the + * specified FileDescriptor object. A security check is first made to + * determine whether or not access to this file is allowed. This is done by + * calling the checkRead() method of the SecurityManager + * (if one exists) with the specified FileDescriptor An exception is + * thrown if reading is not allowed. + * + * @param fd The FileDescriptor 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 FileDescriptor object representing the + * underlying native file handle of the file this stream is reading + * from + * + * @return A FileDescriptor 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * This method will block until some data can be read. + *

+ * This method operates by calling an overloaded read method like so: + * read(buf, 0, buf.length) + * + * @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 offset into + * the buffer and attempts to read len 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. + *

+ * 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 FileDescriptor. + * + * @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 FileOutputStream 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. + *

+ * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (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 FileOutputStream object to write + * to the specified File object. The file is created if it + * does not exist, and the bytes written are written starting at the + * beginning of the file. + *

+ * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param file The File 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 FileOutputStream 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 append argument is false or at the end + * of the file if the append argument is true. + *

+ * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (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 true to append bytes to the end of the file, or false 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 FileOutputStream object to write + * to the file represented by the specified FileDescriptor + * 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. + *

+ * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (if + * one exists) with the specified FileDescriptor as an argument. + * An exception is thrown if writing is not allowed. + * + * @param file The FileDescriptor 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 FileDescriptor object representing + * the file that is currently being written to + * + * @return A FileDescriptor 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 int + * + * @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 len bytes from the byte array + * buf to the file starting at index offset. + * + * @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 + * DataInput and DataOutput 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 RandomAccessFile + * 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. + *

+ * Note that a SecurityManager 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 mode 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 RandomAccessFile + * to read from the specified File object with the specified + * access mode. The access mode is either "r" for read only access or "rw" + * for read-write access. + *

+ * Note that a SecurityManager 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 File object to read and/or write. + * @param mode "r" for read only or "rw" for read-write access to the file + * + * @exception IllegalArgumentException If mode 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 FileDescriptor object that + * represents the native file handle for this file. + * + * @return The FileDescriptor 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. + *

+ * 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 + * buf.length 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 len bytes from the file into the s + * pecified array starting at position offset 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 false If the byte is non-zero, then + * the value returned is true + *

+ * This method can read a boolean written by an object implementing the + * writeBoolean() method in the DataOutput interface. + * + * @return The boolean 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. + *

+ * This method can read a byte written by an object implementing the + * writeByte() method in the DataOutput interface. + * + * @return The byte 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 int value from the + * stream. The value returned is in the range of 0 to 255. + *

+ * This method can read an unsigned byte written by an object implementing the + * writeUnsignedByte() method in the DataOutput interface. + * + * @return The unsigned bytes value read as a Java int + * + * @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 char value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java char The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

+ * As an example, if byte1 and code{byte2 represent the first + * and second byte read from the stream respectively, they will be + * transformed to a char in the following manner: + *

+ * (char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF) + *

+ * This method can read a char written by an object implementing the + * writeChar() method in the DataOutput interface. + * + * @return The char 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 short The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

+ * As an example, if byte1 and code{byte2 represent the first + * and second byte read from the stream respectively, they will be + * transformed to a short in the following manner: + *

+ * (short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF) + *

+ * The value returned is in the range of -32768 to 32767. + *

+ * This method can read a short written by an object implementing the + * writeShort() method in the DataOutput interface. + * + * @return The short 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 int The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

+ * As an example, if byte1 and code{byte2 represent the first + * and second byte read from the stream respectively, they will be + * transformed to an int in the following manner: + *

+ * (int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF)) + *

+ * The value returned is in the range of 0 to 65535. + *

+ * This method can read an unsigned short written by an object implementing + * the writeUnsignedShort() method in the DataOutput interface. + * + * @return The unsigned short value read as a Java int + * + * @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 int value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java int The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

+ * As an example, if byte1 through byte4 represent the first + * four bytes read from the stream, they will be + * transformed to an int in the following manner: + *

+ * (int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF) << 8) + (byte4 & 0xFF))) + *

+ * The value returned is in the range of 0 to 65535. + *

+ * This method can read an int written by an object implementing the + * writeInt() method in the DataOutput interface. + * + * @return The int 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 long The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

+ * As an example, if byte1 through byte8 represent the first + * eight bytes read from the stream, they will be + * transformed to an long in the following manner: + *

+ * (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))) + *

+ * The value returned is in the range of 0 to 65535. + *

+ * This method can read an long written by an object implementing the + * writeLong() method in the DataOutput interface. + * + * @return The long 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 int value from the stream by calling the + * readInt() method in this interface, then converts that int + * to a float using the intBitsToFloat method in + * the class java.lang.Float + *

+ * This method can read a float written by an object implementing the + * writeFloat() method in the DataOutput interface. + * + * @return The float 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 logn value from the stream by calling the + * readLong() method in this interface, then converts that long + * to a double using the longBitsToDouble method in + * the class java.lang.Double + *

+ * This method can read a double written by an object implementing the + * writeDouble() method in the DataOutput interface. + * + * @return The double 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 char + * values by treating the byte read as the low eight bits of the char + * and using 0 as the high eight bits. Because of this, it does + * not support the full 16-bit Unicode character set. + *

+ * 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 String + * A line terminator is a byte sequence consisting of either + * \r \n or \r\n These termination charaters are + * discarded and are not returned as part of the string. + *

+ * This method can read data that was written by an object implementing the + * writeLine() method in DataOutput + * + * @return The line read as a String + * + * @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 String 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 readUnsignedShort() method of this + * interface. + *

+ * After the number of remaining bytes have been determined, these bytes + * are read an transformed into char values. These char 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. + *

+ * 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 byte1 is the byte read from the stream, it would + * be converted to a char like so: + *

+ * (char)byte1 + *

+ * If the first byte has 110 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. + *

+ * As an example, if byte1 and byte2 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 char like so: + *

+ * (char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F)) + *

+ * If the first byte has a 1110 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 10 as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + *

+ * As an example, if byte1 byte2 and byte3 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 char like so: + *

+ * (char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)) + *

+ * 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 \u0000 which is encoded as two bytes. This is a + * modification of the UTF standard used to prevent C language style + * NUL values from appearing in the byte stream. + *

+ * This method can read data that was written by an object implementing the + * writeUTF() method in DataOutput + * + * @return The String 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 buf starting + * offset bytes into the buffer. The number of bytes read will be + * exactly len 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 len 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 len bytes to the file from the specified + * array starting at index offset 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 boolean to the underlying output + * stream. For a value of true, 1 is written to the stream. + * For a value of false, 0 is written. + * + * @param b The boolean 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 byte value to the underlying + * output stream. + * + * @param b The byte to write to the stream, passed as an int. + * + * @exception IOException If an error occurs + */ +public final void +writeByte(int b) throws IOException +{ + write(b); +} + +/*************************************************************************/ + +/** + * This method writes all the bytes in a String out to the + * stream. One byte is written for each character in the String. + * The high eight bits of each character are discarded. + * + * @param s The String 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 char value to the stream, + * high byte first. + * + * @param c The char value to write, passed as an int. + * + * @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 String 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 String 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 short to the stream, high byte + * first. This method requires two bytes to encode the value. + * + * @param s The short value to write to the stream, passed as an int. + * + * @exception IOException If an error occurs + */ +public final void +writeShort(int s) throws IOException +{ + write(DataOutputStream.convertFromShort(s)); +} + +/*************************************************************************/ + +/** + * This method writes a Java int to the stream, high bytes + * first. This method requires four bytes to encode the value. + * + * @param i The int 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 long to the stream, high bytes + * first. This method requires eight bytes to encode the value. + * + * @param l The long 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 float value to the stream. This + * value is written by first calling the method Float.floatToIntBits + * to retrieve an int representing the floating point number, + * then writing this int value to the stream exactly the same + * as the writeInt() 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 double value to the stream. This + * value is written by first calling the method Double.doubleToLongBits + * to retrieve an long representing the floating point number, + * then writing this long value to the stream exactly the same + * as the writeLong() 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 String 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 String not the String length. Next + * come the encoded characters. Each character in the String + * is encoded as either one, two or three bytes. For characters in the + * range of \u0001 to \u007F, one byte is used. The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of \u0080 to \u007FF, 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 + * \u0000. 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 String 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. + * + *

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. + * + *

There are two methods of creating a Thread: you may subclass Thread and + * implement the run() method, at which point you may start the + * Thread by calling its start() method, or you may implement + * Runnable in the class you want to use and then call new + * Thread(your_obj).start(). + * + *

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 System.exit is called with + * adequate permissions. + * + *

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 + * @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 + * Thread(null, null, fake name), where the fake name + * is "Thread-" + unique integer. + * + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread() + { + this(null, (Runnable) null); + } + + /** + * Allocate a new Thread object, as if by + * Thread(null, toRun, fake name), where the fake name + * is "Thread-" + unique integer. + * + * @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 + * Thread(group, toRun, fake name), where the fake name + * is "Thread-" + unique integer. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @throws SecurityException if this thread cannot access group + * @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 + * Thread(null, null, name). + * + * @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 + * Thread(group, null, name). + * + * @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 group + * @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 + * Thread(group, null, name). + * + * @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 run() method to + * execute. If the Runnable object is null, this (which is + * a Runnable) is used instead. + * + *

If the ThreadGroup is null, the security manager is checked. If a + * manager exists and returns a non-null object for + * getThreadGroup, that group is used; otherwise the group + * of the creating thread is used. Note that the security manager calls + * checkAccess if the ThreadGroup is not null. + * + *

The new Thread will inherit its creator's priority and daemon status. + * These can be changed with setPriority and + * setDaemon. + * + * @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 group + * @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 + * Thread(group, null, name), and give it the specified stack + * size, in bytes. The stack size is highly platform independent, + * 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 + * StackOverflowError, while a lower value might let you go + * longer before an OutOfMemoryError. 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 group + * @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 + * interrupted status 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. + * + *

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 + * interrupted status 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. + * + *

This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * checkAccess(this), plus another one if the current thread + * is not this: RuntimePermission("stopThread"). 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. WARNINGThis bypasses Java security, + * and can throw a checked exception which the call stack is unprepared to + * handle. Do not abuse this power. + * + *

This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * checkAccess(this), plus another one if the current thread + * is not this: RuntimePermission("stopThread"). 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, + * checkAccess. Then, depending on the current state of the + * thread, various actions take place: + * + *

If the thread is waiting because of {@link #wait()}, + * {@link #sleep(long)}, or {@link #join()}, its interrupt status + * will be cleared, and an InterruptedException will be thrown. Notice that + * this case is only possible if an external thread called interrupt(). + * + *

If the thread is blocked in an interruptible I/O operation, in + * {@link java.nio.channels.InterruptibleChannel}, the interrupt + * status will be set, and ClosedByInterruptException will be thrown. + * + *

If the thread is blocked on a {@link java.nio.channels.Selector}, the + * interrupt status will be set, and the selection will return, with + * a possible non-zero value, as though by the wakeup() method. + * + *

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 interrupted status 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 interrupted status 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. + * + *

This is inherently unsafe, as the suspended thread still holds locks, + * and can potentially deadlock your program. Hence, there is a security + * check: checkAccess. + * + * @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: + * checkAccess. + * + * @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, + * checkAccess, 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, + * checkAccess. + * + * @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 + * currentThread().getThreadGroup().activeCount(). + * + * @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 + * getThreadGroup().enumerate(array), which may have a + * security check, checkAccess(group). + * + * @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 + * interrupted status 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. + * + *

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 + * interrupted status 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 + * interrupted status 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, + * checkAccess. + * + * @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 SecurityManager.checkAccess(this). + * + * @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:
+ * "Thread[" + getName() + ',' + getPriority() + ',' + * + (getThreadGroup() == null ? "" : getThreadGroup().getName()) + + ']'. + * + * @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 RuntimePermission("getClassLoader") 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 RuntimePermission("setContextClassLoader"). + * + * @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 assert Thread.holdsLock(obj). + * + * @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 + * @author Eric Blake + */ +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. + * This method will be removed in a future version of GNU + * Classpath. + * + * @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. + * + * 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. + * + * @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. + * + *

    + *
  • 'Z' - boolean
  • + *
  • 'B' - byte
  • + *
  • 'C' - char
  • + *
  • 'D' - double
  • + *
  • 'F' - float
  • + *
  • 'I' - int
  • + *
  • 'J' - long
  • + *
  • 'S' - short
  • + *
  • 'V' - void
  • + *
+ * + * Note that this is currently a java version that converts the type code + * to a string and calls the native getPrimitiveClass(String) + * method for backwards compatibility with VMs that used old versions of + * GNU Classpath. Please replace this method with a native method + * final static native Class getPrimitiveClass(char type); + * if your VM supports it. The java version of this method and + * the String version of this method will disappear in a future version + * of GNU Classpath. + * + * @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.

+ * + * Note: This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,, also available as boolean.class, + * byte.class, etc. These are not to be confused with the + * classes java.lang.Boolean, java.lang.Byte, etc., which are + * real classes.

+ * + * 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 + * @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 Modifier + * 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 true if they are equal; false 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.
Example: + * public java.io.FileInputStream(java.lang.Runnable) + * throws java.io.FileNotFoundException + * + * @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.

+ * + * If this class is abstract, you will get an + * InstantiationException. If the constructor takes 0 + * arguments, you may use null or a 0-length array for args.

+ * + * If this Constructor enforces access control, your runtime context is + * evaluated, and you may have an IllegalAccessException 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 + * ExceptionInInitializerError.

+ * + * 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 InvocationTargetException. + * + * @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.

+ * + * Note: This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,, also available as boolean.class, + * byte.class, etc. These are not to be confused with the + * classes java.lang.Boolean, java.lang.Byte, etc., which are + * real classes.

+ * + * 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 + * @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 Modifier + * 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 true if they are equal; false 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>".
Example: + * public transient boolean gnu.parse.Parser.parseComplete + * + * @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).

+ * + * If the field is static, o will be ignored. Otherwise, if + * o is null, you get a NullPointerException, + * and if it is incompatible with the declaring class of the field, you + * get an IllegalArgumentException.

+ * + * Next, if this Field enforces access control, your runtime context is + * evaluated, and you may have an IllegalAccessException 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 + * ExceptionInInitializerError.

+ * + * 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 o is not an instance of + * the class or interface declaring this field + * @throws NullPointerException if o 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, + * o 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 + * o, or if o is not an instance of the + * declaring class of this field + * @throws NullPointerException if o 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, + * o 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 + * o, or if o is not an instance of the + * declaring class of this field + * @throws NullPointerException if o 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, + * o 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 + * o, or if o is not an instance + * of the declaring class of this field + * @throws NullPointerException if o 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, + * o 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 o, or if o is not an instance + * of the declaring class of this field + * @throws NullPointerException if o 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, + * o 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 o, or if o is not an + * instance of the declaring class of this field + * @throws NullPointerException if o 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, + * o 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 o, or if o is not an + * instance of the declaring class of this field + * @throws NullPointerException if o 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, + * o 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 o, or if o is + * not an instance of the declaring class of this field + * @throws NullPointerException if o 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, + * o 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 o, or if + * o is not an instance of the declaring class of this + * field + * @throws NullPointerException if o 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).

+ * + * If the field is static, o will be ignored. Otherwise, if + * o is null, you get a NullPointerException, + * and if it is incompatible with the declaring class of the field, you + * get an IllegalArgumentException.

+ * + * Next, if this Field enforces access control, your runtime context is + * evaluated, and you may have an IllegalAccessException 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 IllegalArgumentException; likewise, this error + * happens if value 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 + * ExceptionInInitializerError.

+ * + * 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 value cannot be + * converted by a widening conversion to the underlying type of + * the Field, or if o is not an instance of the class + * declaring this field + * @throws NullPointerException if o 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, o 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 + * o is not an instance of the class declaring this + * field + * @throws NullPointerException if o 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, o 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 o is not an instance + * of the class declaring this field + * @throws NullPointerException if o 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, o 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 o is not an instance + * of the class declaring this field + * @throws NullPointerException if o 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, o 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 o is not an instance + * of the class declaring this field + * @throws NullPointerException if o 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, o 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 o is not an instance of the + * class declaring this field + * @throws NullPointerException if o 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, o 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 o is not an instance of the class + * declaring this field + * @throws NullPointerException if o 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, o 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 o is not an instance of the class declaring this + * field + * @throws NullPointerException if o 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, o 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 + * o is not an instance of the class declaring this + * field + * @throws NullPointerException if o 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.

+ * + * Note: This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,, also available as boolean.class, + * byte.class, etc. These are not to be confused with the + * classes java.lang.Boolean, java.lang.Byte, etc., which are + * real classes.

+ * + * 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 + * @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 Modifier + * 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 true if they are equal; false 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.
Example: + * public static int run(java.lang.Runnable,int) + * + * @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.

+ * + * If the method is static, o 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 NullPointerException if o is + * null, and an IllegalArgumentException 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 args.

+ * + * Next, if this Method enforces access control, your runtime context is + * evaluated, and you may have an IllegalAccessException 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 + * ExceptionInInitializerError.

+ * + * 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 + * InvocationTargetException. + * + * @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 o is not an instance of the class or interface + * declaring this method + * @throws InvocationTargetException if the method throws an exception + * @throws NullPointerException if o 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. + *

+ * 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 new file mode 100644 index 0000000000000000000000000000000000000000..d72b2d0d20412cfd7cf14060f831ad75c3028616 GIT binary patch literal 518720 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 X@_dp7UccFew? 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 kQxR$>i^x+tXTeNx@St~WRr{Ux__w1xZRHzM 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%0D18`wXgXK9S z+x1OgST!-Xg`Zyt`0&KpxehK~OEC4^2HBS`i^y-&vp?Z~|JD|i{+J;O7`i*#>z6#<^cRqkeTo%DdkDF0HfcwohrnPff z&3RDzZCvqLrqx?U1#-!0jQ5m^PV@|vCIRJfp-c^eyR4`UIpKrsu+89`U=~1nptl&5 zQ*7Mp6{2Oqd5}hUIPJr^ZH)y%8gns`x&>*;_dq#K!X#2(194HMF)if-RdSC_{US%u zYf$XewTY@VJ0vST7+`S$bSLR7?(*r1u^S6=i zPuuptSKfc|H2>0HmEx}hbV1?a{_Ary@2Mp;BwzLcCAnEjiGVolzb0}L;Ydljehf*! z9pb(Pc~BTckMq~&t}OvvpBwK=TX-=HaX}<_C{z(b(r8Iwn;d4W#KF&E(vZ8Y`QOsw z)f63BPsyr5h+;Ez(~J^8>rErBkH0_4q__q!BD)_-8()5ev>8g;S2WSVgId~x$N6rF z;8AFqSSCP?)}I?(&FU?7kBdWhAFmZ%-Tdg+e}oRMa{{(9n5)`Q?t0azU6YyXph^+j zrcalU0*X3Da|vl|7=Xu0dU{X)p&9nVP0 z;@EG{rs827R2Vx1+R?Lx=BXjzQB_bK{;l4 ztbMGkLW#bOg8DGcX6!=eu|Zj2nHh5->#rgXHUZLf;g=`-S-_c^`p<)zK1_d5v91^~4R^^Cz z0j+)cwOOUm9#@$;gS(sAKjh`r)hUDlZXb+zpov4}CDTV`VfjI-JXUk0{M%E<`}{P- zLYD{cY^(xfQ@AT2BygJzK4P7~h=KSbgrhTF1Bp(uz&ZD|UY{dtm+$(tZ?d(_70R?t zvoqos?+S>vXdv0m0F7c+HdO`X_;;3Bp2x$ct*BDv36?@8i9s9hX$IN)Ef|P-(}*u^ zP2P^-`8zeW$Rdp+l-7BPx?7Mu?w}M;KEv&43qs(|!?Aui>1QO&WGM$J8FOu1d;dDl ztge*`x(B$n`QN%W{{JOb{A~mIub@iB*BNv+8tN$qTIVp*3n8qCvNYrd|3g?EsN7&8 z6H&#F*&|z-1Cm>De6KeI!!2Zs#N?UF(_UO_M_z!x7g2*>>u2=TMHULKC4LX~FZ=8z zxWW8Zqo-(LV?r8qS**G`;V@b8|B?2VQFU%hmvDfMySuvt3GNo$-95N#aDuxAcY+1C z5Zv9}-7Po-hi{W}Z@;%spPPI=`o|t)|9-00s#&$>oaF~E6v#;gIIKBb>Oj&e4_91V zI|$u`^PM^2Jl2~mk&XWOg_7*dB3dt(-zoKbg41`L+#gcxNCdc8JA7drbES&mvlfXW zZ*!Lsc+p|39N-e{cyc}Q5qNepjizA0yMx_sNxG zRCS4&MXUGg#)u&dW^?_~@+)iE%w+PDkC1x{ zOuWI-(pqD&(Ja&qsJ*H?48)n;E76zdGcReh-6XcPAT221DZLFGHf{vU1_;$iS?Q}C zc-yr#exi<*U(ZqKC|v_fyJ$+EIz`mw&VqW=l

#y)99W%)Ig5dj0|gEH7AHAAtcD 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

7SsX{#t$I1A_wfM-%O{Y!fUo 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Ν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 + + + + + + + + + + + + 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\. 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 @@ + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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] [args...]"); + Console.Error.WriteLine(" (to execute a class)"); + Console.Error.WriteLine(" or ikvm -jar [-options] [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 "); + 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= 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\. 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] ... "); + Console.Error.WriteLine(); + Console.Error.WriteLine("options:"); + Console.Error.WriteLine(" -out: Required"); + Console.Error.WriteLine(" -assembly: 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: Required (for executables)"); + Console.Error.WriteLine(" -reference: Reference an assembly"); + Console.Error.WriteLine(" -recurse: 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: 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: 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 @@ + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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\. 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, "", "(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 ? "" : 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 @@ + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + +