// ========================================================================== // This software is subject to the provisions of the Zope Public License, // Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. // THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED // WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS // FOR A PARTICULAR PURPOSE. // ========================================================================== //============================================================================ // This file is a hand-maintained stub - it implements clr.dll, which can be // loaded by a standard CPython interpreter as an extension module. When it // is loaded, it bootstraps the managed runtime integration layer and defers // to it to do initialization and put the clr module into sys.modules, etc. // The "USE_PYTHON_RUNTIME_*" defines control what extra evidence is used // to help the CLR find the appropriate Python.Runtime assembly. // If defined, the "pythonRuntimeVersionString" variable must be set to // Python.Runtime's current version. #define USE_PYTHON_RUNTIME_VERSION // If defined, the "PythonRuntimePublicKeyTokenData" data array must be // set to Python.Runtime's public key token. //#define USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN // If DEBUG_PRINT is defined, a few System.Console.WriteLine calls are made // to indicate what's going on during the load... //#define DEBUG_PRINT //============================================================================ .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly clr { .hash algorithm 0x00008004 .ver 2:4:2:7 } .module clr.dll .imagebase 0x00400000 .subsystem 0x00000003 .file alignment 512 // This includes the platform-specific IL. The include search path // is set depending on whether we're compiling 32 or 64 bit. // This MUST come before any other .data directives! // Why, oh why, can't ilasm support command line #defines? :( #include "clrmodule-platform.il" #ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN .data PythonRuntimePublicKeyTokenData = bytearray (64 e1 4e 84 5a bf 2e 60) #endif .class public auto ansi beforefieldinit clrModule extends [mscorlib]System.Object { #ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN .field static assembly int64 PythonRuntimePublicKeyToken at PythonRuntimePublicKeyTokenData #endif .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 1 ldarg.0 call instance void [mscorlib]System.Object::.ctor() ret } .method public hidebysig static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) initclr() cil managed { .vtentry 1:1 .export [1] as initclr .maxstack 6 .locals init ( class [mscorlib]System.Reflection.Assembly pythonRuntime, class [mscorlib]System.Reflection.Assembly executingAssembly, class [mscorlib]System.Reflection.AssemblyName pythonRuntimeName, class [mscorlib]System.Type pythonEngineType, int8[] publicKeyToken, string assemblyDirectory, string pythonRuntimeVersionString, string pythonRuntimeDllPath) // pythonRuntime = null; ldnull stloc pythonRuntime .try { #ifdef DEBUG_PRINT ldstr "Attempting to load Python.Runtime using standard binding rules... " call void [mscorlib]System.Console::Write(string) #endif // Attempt to find and load Python.Runtime using standard assembly binding rules. // This roughly translates into looking in order: // - GAC // - ApplicationBase // - A PrivateBinPath under ApplicationBase // With an unsigned assembly, the GAC is skipped. // System.Reflection.AssemblyName pythonRuntimeName = new System.Reflection.AssemblyName(); newobj instance void [mscorlib]System.Reflection.AssemblyName::.ctor() stloc pythonRuntimeName // pythonRuntimeName.Name = "Python.Runtime"; ldloc pythonRuntimeName ldstr "Python.Runtime" callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_Name(string) #ifdef USE_PYTHON_RUNTIME_VERSION // pythonRuntimeVersionString = "..."; ldstr "2.0.0.2" stloc pythonRuntimeVersionString // pythonRuntimeName.Version = new Version(pythonRuntimeVersionString); ldloc pythonRuntimeName ldloc pythonRuntimeVersionString newobj instance void [mscorlib]System.Version::.ctor(string) callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_Version(class [mscorlib]System.Version) #endif #ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN // publicKeyToken = new byte[] { ... }; ldc.i4.8 newarr [mscorlib]System.Byte dup ldtoken field int64 clrModule::PythonRuntimePublicKeyToken call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) stloc publicKeyToken // pythonRuntimeName.SetPublicKeyToken(publicKeyToken); ldloc pythonRuntimeName ldloc publicKeyToken callvirt instance void [mscorlib]System.Reflection.AssemblyName::SetPublicKeyToken(uint8[]) #endif // pythonRuntimeName.CultureInfo = System.Globalization.CultureInfo.InvariantCulture; ldloc pythonRuntimeName call class [mscorlib]System.Globalization.CultureInfo [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture() callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_CultureInfo(class [mscorlib]System.Globalization.CultureInfo) // return System.Reflection.Assembly.Load(pythonRuntimeName); ldloc pythonRuntimeName call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::Load(class [mscorlib]System.Reflection.AssemblyName) stloc pythonRuntime #ifdef DEBUG_PRINT ldstr "Success!" call void [mscorlib]System.Console::WriteLine(string) #endif leave.s LOADED_PYTHON_RUNTIME } catch [mscorlib]System.Object { #ifdef DEBUG_PRINT ldstr "Failed." call void [mscorlib]System.Console::WriteLine(string) #endif leave.s EXIT_CLR_LOAD } EXIT_CLR_LOAD: nop .try { // If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll" // from the directory this assembly is running in. "This assembly" is probably "clr.pyd", // sitting somewhere in PYTHONPATH. This is using Assembly.LoadFrom, and inherits all the // caveats of that call. See MSDN docs for details. // Suzanne Cook's blog is also an excellent source of info on this: // http://blogs.msdn.com/suzcook/ // http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx // http://blogs.msdn.com/suzcook/archive/2003/06/13/57180.aspx // executingAssembly = System.Reflection.Assembly.GetExecutingAssembly(); call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::GetExecutingAssembly() stloc executingAssembly // assemblyDirectory = System.IO.Path.GetDirectoryName(executingAssembly.Location); ldloc executingAssembly callvirt instance string [mscorlib]System.Reflection.Assembly::get_Location() call string [mscorlib]System.IO.Path::GetDirectoryName(string) stloc assemblyDirectory // pythonRuntimeDllPath = System.IO.Path.Combine(assemblyDirectory, "Python.Runtime.dll"); ldloc assemblyDirectory ldstr "Python.Runtime.dll" call string [mscorlib]System.IO.Path::Combine(string, string) stloc pythonRuntimeDllPath #ifdef DEBUG_PRINT ldstr "Attempting to load Python.Runtime from: '{0}'... " ldloc pythonRuntimeDllPath call void [mscorlib]System.Console::Write(string, object) #endif // pythonRuntime = System.Reflection.Assembly.LoadFrom(pythonRuntimeDllPath); ldloc pythonRuntimeDllPath call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::LoadFrom(string) stloc pythonRuntime #ifdef DEBUG_PRINT ldstr "Success!" call void [mscorlib]System.Console::WriteLine(string) ldloc pythonRuntime callvirt instance string [mscorlib]System.Reflection.Assembly::get_CodeBase() call void [mscorlib]System.Console::WriteLine(string) #endif leave.s LOADED_PYTHON_RUNTIME } catch [mscorlib]System.Object { #ifdef DEBUG_PRINT ldstr "Failed." call void [mscorlib]System.Console::WriteLine(string) #endif leave.s EXIT_PYTHONPATH_LOAD } EXIT_PYTHONPATH_LOAD: nop // If we get here, we haven't loaded Python.Runtime, so bail. #ifdef DEBUG_PRINT ldstr "Could not load Python.Runtime, so sad." call void [mscorlib]System.Console::WriteLine(string) #endif ret; // Once here, we've successfully loaded SOME version of Python.Runtime // So now we get the PythonEngine and execute the InitExt method on it. LOADED_PYTHON_RUNTIME: nop .try { #ifdef DEBUG_PRINT ldstr "Running Python.Runtime.PythonEngine.InitExt()" call void [mscorlib]System.Console::WriteLine(string) #endif // pythonEngineType = pythonRuntime.GetType("Python.Runtime.PythonEngine"); ldloc pythonRuntime ldstr "Python.Runtime.PythonEngine" callvirt instance class [mscorlib]System.Type [mscorlib]System.Reflection.Assembly::GetType(string) stloc pythonEngineType // pythonEngineType.InvokeMember("InitExt", System.Reflection.BindingFlags.InvokeMethod, null, null, null); ldloc pythonEngineType ldstr "InitExt" ldc.i4 0x100 ldnull ldnull ldnull callvirt instance object [mscorlib]System.Type::InvokeMember( string, valuetype [mscorlib]System.Reflection.BindingFlags, class [mscorlib]System.Reflection.Binder, object, object[]) pop leave.s EXIT_TRY_INVOKE } catch [mscorlib]System.Object { #ifdef DEBUG_PRINT ldstr "Error calling Python.Runtime.PythonEngine.InitExt()." call void [mscorlib]System.Console::WriteLine(string) #endif leave.s EXIT_TRY_INVOKE } EXIT_TRY_INVOKE: nop ret } }