Inheritance: System.Attribute
        //
        // 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.");
        }