// // LoadExtension // // Attempt to load the extension with the specified name into the // given context, which must have already been made current. The // object passed in ought to be an instance of // Tao.OpenGl.ContextGl, or null. If forceLoad is set, attempt // to obtain function pointers even if the runtime claims that the // extension is not supported. // /// <summary> /// /// </summary> /// <param name="contextGl"></param> /// <param name="extname"></param> /// <param name="forceLoad"></param> /// <returns></returns> public static bool LoadExtension(object contextGl, string extname, bool forceLoad) { GlContextInfo gci = GetContextInfo(contextGl); if (gci.LoadedExtensions.ContainsKey(extname)) { return((bool)gci.LoadedExtensions[extname]); } if (!forceLoad && !gci.AvailableExtensions.ContainsKey(extname)) { return(false); } // this will get us either the Tao.OpenGl.Gl or // Tao.OpenGl.ContextGl class Type glt; if (contextGl != null) { glt = contextGl.GetType(); } else { glt = StaticGlType; if (glt == null) { Console.WriteLine("GL type is null!"); } } FieldInfo[] fis = glt.GetFields(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Instance); foreach (FieldInfo fi in fis) { object[] attrs = fi.GetCustomAttributes(typeof(OpenGLExtensionImport), false); if (attrs.Length == 0) { continue; } OpenGLExtensionImport oglext = attrs[0] as OpenGLExtensionImport; if (oglext.ExtensionName == extname) { // did we already load this somehow? if (((IntPtr)fi.GetValue(contextGl)) != IntPtr.Zero) { continue; } //Console.WriteLine ("Loading " + oglext.EntryPoint + " for " + extname); IntPtr procaddr = GetProcAddress(oglext.EntryPoint); if (procaddr == IntPtr.Zero) { Console.WriteLine("OpenGL claimed that '{0}' was supported, but couldn't find '{1}' entry point", extname, oglext.EntryPoint); // we crash if anyone tries to call this method, but that's ok continue; } fi.SetValue(contextGl, procaddr); } } gci.LoadedExtensions[extname] = true; return(true); }
/// <summary> /// takes a mbuilder for output, the input assembly, the name of the type, /// and a flag indicating whether it's an instance type or not (i.e. whether /// the members should be static or not. /// /// It then munges mercilessly. /// </summary> /// <param name="mbuilder"></param> /// <param name="inputAssembly"></param> /// <param name="typeName"></param> /// <param name="isInstanceClass"></param> public static void ProcessType(ModuleBuilder mbuilder, Assembly inputAssembly, string typeName, bool isInstanceClass) { TypeBuilder glbuilder = mbuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed); glbuilder.SetCustomAttribute(GetCLSCompliantCAB(true)); Type gltype = inputAssembly.GetType(typeName); MemberInfo [] glMembers = gltype.GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); // just something to help us print some status.. int methodCount = 0; Console.Write("Processing {0}...", typeName); foreach (MemberInfo qi in glMembers) { // Fields FieldInfo fi = qi as FieldInfo; if (fi != null) { // Console.WriteLine ("FIELD: " + fi.Name); FieldBuilder fb = glbuilder.DefineField(fi.Name, fi.FieldType, fi.Attributes); // only set constants in the non-instance class if (fi.FieldType != typeof(System.IntPtr) && !isInstanceClass) { fb.SetConstant(fi.GetValue(gltype)); } else { object [] extattrs = fi.GetCustomAttributes(typeof(OpenGl.OpenGLExtensionImport), false); if (extattrs.Length > 0) { OpenGl.OpenGLExtensionImport ogl = extattrs[0] as OpenGl.OpenGLExtensionImport; if (ogl == null) { throw new InvalidProgramException("Thought we had an attr, guess we were wrong!"); } fb.SetCustomAttribute(CreateGLExtCAB(ogl.ExtensionName, ogl.EntryPoint)); } // this is a slot to hold an extension addr, // so we save it. We have to do this because on // windows we can't call GetField on a dynamic type. // This is probably faster anyway. field_hash[fi.Name] = fb; } continue; } // Methods MethodInfo mi = qi as MethodInfo; if (mi != null) { bool is_ext; bool is_dll; bool is_cls_compliant; object [] extattrs = mi.GetCustomAttributes(typeof(OpenGl.OpenGLExtensionImport), false); object [] clsattrs = mi.GetCustomAttributes(typeof(CLSCompliantAttribute), false); is_ext = (extattrs.Length > 0); is_dll = (mi.Attributes & MethodAttributes.PinvokeImpl) != 0; if (clsattrs.Length > 0) { is_cls_compliant = (clsattrs[0] as CLSCompliantAttribute).IsCompliant; } else { is_cls_compliant = true; } ParameterInfo [] parms = mi.GetParameters(); Type [] methodSig = new Type [parms.Length]; ParameterAttributes [] methodParams = new ParameterAttributes [parms.Length]; for (int i = 0; i < parms.Length; i++) { methodSig[i] = parms[i].ParameterType; methodParams[i] = parms[i].Attributes; } // Console.WriteLine ("Method: {0} is_dll: {1}", mi.Name, is_dll); if (is_dll) { // this is a normal DLL import'd method // Console.WriteLine ("DLL import method: " + mi.Name); MethodBuilder mb = glbuilder.DefinePInvokeMethod(mi.Name, GL_NATIVE_LIBRARY, mi.Name, mi.Attributes, CallingConventions.Standard, mi.ReturnType, methodSig, CallingConvention.Winapi, CharSet.Ansi); mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); // Set In/Out/etc. back for (int i = 0; i < parms.Length; i++) { mb.DefineParameter(i + 1, methodParams[i], null); } mb.SetCustomAttribute(GetSuppressUnmanagedCSCAB()); if (is_cls_compliant) { mb.SetCustomAttribute(GetCLSCompliantCAB(true)); } else { mb.SetCustomAttribute(GetCLSCompliantCAB(false)); } } else if (is_ext) { // this is an OpenGLExtensionImport method OpenGl.OpenGLExtensionImport ogl = extattrs[0] as OpenGl.OpenGLExtensionImport; if (ogl == null) { throw new InvalidProgramException("Thought we had an OpenGLExtensionImport, guess not?"); } // Console.WriteLine ("OpenGL Extension method: " + mi.Name); MethodBuilder mb = glbuilder.DefineMethod(mi.Name, mi.Attributes, mi.ReturnType, methodSig); // Set In/Out/etc. back for (int i = 0; i < parms.Length; i++) { mb.DefineParameter(i + 1, methodParams[i], null); } // put attributes mb.SetCustomAttribute(GetSuppressUnmanagedCSCAB()); if (is_cls_compliant) { mb.SetCustomAttribute(GetCLSCompliantCAB(true)); } else { mb.SetCustomAttribute(GetCLSCompliantCAB(false)); } // now build the IL string fieldname = "ext__" + ogl.ExtensionName + "__" + ogl.EntryPoint; FieldInfo addrfield = field_hash[fieldname] as FieldInfo; // no workie on win32; the field_hash is probably faster anyway // FieldInfo addrfield = glbuilder.GetField(fieldname, // BindingFlags.Instance | // BindingFlags.Static | // BindingFlags.Public | // BindingFlags.NonPublic | // BindingFlags.DeclaredOnly); ILGenerator ilg = mb.GetILGenerator(); ArrayList locals = new ArrayList(); Type [] methodCalliSig = new Type[methodSig.Length]; int thislocal; int numargs = methodSig.Length; int argoffset = 0; if (isInstanceClass) { argoffset = 1; } for (int arg = argoffset; arg < numargs; arg++) { EmitLdarg(ilg, arg); // we need to convert strings and string arrays to C // null-terminated strings. Use StringToHGlobalAnsi // from Marshal. We don't handle byref string arrays; // there are so few (any?) of them that we can just // return an IntPtr. if (methodSig[arg].IsArray && methodSig[arg].GetElementType() == typeof(string)) { //Console.WriteLine ("String[] param: Method: {0} Param: {1} Type: {2}", mi.Name, arg - argoffset, methodSig[arg]); if (locals.Count == 0) { locals.Add(ilg.DeclareLocal(typeof(int))); } thislocal = locals.Count; locals.Add(ilg.DeclareLocal(typeof(IntPtr[]))); // we have the source array on the stack; get its length, // and allocate a new IntPtr array ilg.Emit(OpCodes.Ldlen); ilg.Emit(OpCodes.Conv_I4); ilg.Emit(OpCodes.Newarr, typeof(IntPtr)); EmitStloc(ilg, thislocal); // set our loop counter to 0; ilg.Emit(OpCodes.Ldc_I4_0); EmitStloc(ilg, 0); // declare our loop label; we'll branch // back to here after each conversion Label loop = ilg.DefineLabel(); ilg.MarkLabel(loop); // put the address of the destination element onto the stack EmitLdloc(ilg, thislocal); EmitLdloc(ilg, 0); ilg.Emit(OpCodes.Ldelema, typeof(IntPtr)); // put the source string on the stack EmitLdarg(ilg, arg); EmitLdloc(ilg, 0); ilg.Emit(OpCodes.Ldelem_Ref); // convert ilg.EmitCall(OpCodes.Call, GetStringMarshalMI(), null); // store the result into the address we put up above ilg.Emit(OpCodes.Stobj, typeof(IntPtr)); // add 1 to loop counter EmitLdloc(ilg, 0); ilg.Emit(OpCodes.Ldc_I4_1); ilg.Emit(OpCodes.Add); ilg.Emit(OpCodes.Dup); EmitStloc(ilg, 0); // test if loop counter < array length, and branch back // to start of loop EmitLdarg(ilg, arg); ilg.Emit(OpCodes.Ldlen); ilg.Emit(OpCodes.Conv_I4); ilg.Emit(OpCodes.Blt, loop); // finally emit the location of the first element of the array EmitLdloc(ilg, thislocal); ilg.Emit(OpCodes.Ldc_I4_0); ilg.Emit(OpCodes.Ldelema, typeof(IntPtr)); methodCalliSig[arg] = typeof(IntPtr); } else if (methodSig[arg].IsByRef && methodSig[arg].GetElementType() == typeof(string)) { //Console.WriteLine ("String& param: Method: {0} Param: {1} Type: {2}", mi.Name, arg - argoffset, methodSig[arg]); //if (locals.Count == 0) locals.Add(ilg.DeclareLocal(typeof(int))); //thislocal = locals.Count; //locals.Add(ilg.DeclareLocal(typeof(IntPtr))); //methodCalliSig[arg] = typeof(IntPtr); } else if (methodSig[arg] == typeof(string)) { //Console.WriteLine ("String param: Method: {0} Param: {1} Type: {2}", mi.Name, arg - argoffset, methodSig[arg]); if (locals.Count == 0) { locals.Add(ilg.DeclareLocal(typeof(int))); } thislocal = locals.Count; locals.Add(ilg.DeclareLocal(typeof(IntPtr))); ilg.EmitCall(OpCodes.Call, GetStringMarshalMI(), null); ilg.Emit(OpCodes.Dup); EmitStloc(ilg, thislocal); methodCalliSig[arg] = typeof(IntPtr); } else { methodCalliSig[arg] = methodSig[arg]; } } if (isInstanceClass) { // load the instance field ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Ldfld, addrfield); } else { // just load the static field ilg.Emit(OpCodes.Ldsfld, addrfield); } // emit Tailcall; have the return take place directly to our // caller, but only if we have no marshal'd things to clean up if (locals.Count == 0) { ilg.Emit(OpCodes.Tailcall); methodCalliSig = methodSig; } // The .NET 1.1 runtime doesn't let us emit // CallingConvention.Winapi here, or anything else that // might mean "Use whatever the default platform calling // convention is", like p/invoke can have. // // However, Mono 1.1 /does/ let us emit Winapi/Default, // and both .NET 1.1 and Mono handle this as intended // (stdcall on .NET, cdecl on Mono/Linux). By my reading // of ECMA-335 (CLI), 22.2.2, this should be allowed, and // I'm not sure why MS's impl doesn't allow it. However, // the .NET System.Reflection.Emit leaves a lot to be // desired anyway. // // So, the issue is how to emit this. On WIN32, we simply // create our own SignatureHelper, and munge the internal // signature byte. Fun, eh? // // ... Except that it doesn't quite work that way. The runtime // gets confused by the calling convention. // #if !WIN32 // Mono? Just emit normally. ilg.EmitCalli(OpCodes.Calli, CallingConvention.Winapi, mi.ReturnType, methodCalliSig); #else // We're too smart for our own good. We don't tell win32 how to do stack // cleanup, so it leaves things littering the stack. So, we can't do this. // GRRRrrr. #if true ilg.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, mi.ReturnType, methodCalliSig); #else // Win32? Let the fun begin. if (win32SigField == null) { win32SigField = typeof(SignatureHelper).GetField("m_signature", BindingFlags.Instance | BindingFlags.NonPublic); } SignatureHelper sh = SignatureHelper.GetMethodSigHelper(mbuilder, CallingConvention.StdCall, // lie mi.ReturnType); // munge calling convention; the value in the first byte will be 0x2 for StdCall (1 minus // the CallingConvention enum value). We set to 0. Array sigArr = win32SigField.GetValue(sh) as Array; sigArr.SetValue((byte)0, 0); // then add the rest of the args. foreach (Type t in methodCalliSig) { sh.AddArgument(t); } ilg.Emit(OpCodes.Calli, sh); #endif #endif // clean up our string allocations, if any if (locals.Count > 0) { for (int i = 1; i < locals.Count; i++) { Type ltype = (locals[i] as LocalBuilder).LocalType; if (ltype.IsArray) { Label looptest = ilg.DefineLabel(); Label loop = ilg.DefineLabel(); // counter = array.Length EmitLdloc(ilg, i); ilg.Emit(OpCodes.Ldlen); ilg.Emit(OpCodes.Conv_I4); EmitStloc(ilg, 0); // goto looptest ilg.Emit(OpCodes.Br, looptest); ilg.MarkLabel(loop); // free(array[counter]) EmitLdloc(ilg, i); EmitLdloc(ilg, 0); ilg.Emit(OpCodes.Ldelem_I4); ilg.EmitCall(OpCodes.Call, GetFreeGlobalMI(), null); ilg.MarkLabel(looptest); // p = counter; counter -= 1; EmitLdloc(ilg, 0); ilg.Emit(OpCodes.Dup); ilg.Emit(OpCodes.Ldc_I4_1); ilg.Emit(OpCodes.Sub); EmitStloc(ilg, 0); // if (p > 0) goto loop ilg.Emit(OpCodes.Ldc_I4_0); ilg.Emit(OpCodes.Bgt, loop); } else { // just a simple free, thankfully EmitLdloc(ilg, i); ilg.EmitCall(OpCodes.Call, GetFreeGlobalMI(), null); } } } ilg.Emit(OpCodes.Ret); } else { // this is a normal method // this shouldn't happen Console.WriteLine(); Console.WriteLine("WARNING: Skipping non-DLL and non-Extension method " + mi.Name); } methodCount++; if (methodCount % 50 == 0) { Console.Write("."); } if (methodCount % 1000 == 0) { Console.Write("[{0}]", methodCount); } } } Console.WriteLine(); glbuilder.CreateType(); Console.WriteLine("Type created."); }