//TODO: make this abstract instead of virtual and implement in every remaining instruction
 internal virtual void CompileIKVM(IKVMCompilationContext context, IKVM.Reflection.Universe universe)
 {
     throw new Exception("CompileIKVM override function needs to be implemented for instruction");
 }
Beispiel #2
0
        private static IKVM.Reflection.Emit.AssemblyBuilder FromBinary(
            string assemblyName,
            Reader reader,
            Universe universe,
            IKVM.Reflection.Type instanceContainer,
            IKVM.Reflection.Type exportContainer,
            IEnumerable <RuntimeImport> imports
            )
        {
            if (reader.ReadUInt32() != Module.Magic)
            {
                throw new ModuleLoadException("File preamble magic value is incorrect.", 0);
            }

            switch (reader.ReadUInt32())
            {
            case 0x1:     //First release
            case 0xd:     //Final pre-release, binary format is identical with first release.
                break;

            default:
                throw new ModuleLoadException("Unsupported version, only version 0x1 and 0xd are accepted.", 4);
            }

            uint memoryPagesMinimum = 0;
            uint memoryPagesMaximum = 0;

            Signature[] signatures         = null;
            Signature[] functionSignatures = null;
            KeyValuePair <string, uint>[] exportedFunctions = null;
            var previousSection = Section.None;

            var assembly = universe.DefineDynamicAssembly(
                new IKVM.Reflection.AssemblyName(assemblyName),
                new IKVM.Reflection.Emit.AssemblyBuilderAccess()
                );

            var dllName = assemblyName + ".dll";
            var module  = assembly.DefineDynamicModule(assemblyName, dllName);

            const IKVM.Reflection.TypeAttributes classAttributes =
                IKVM.Reflection.TypeAttributes.Public |
                IKVM.Reflection.TypeAttributes.Class |
                IKVM.Reflection.TypeAttributes.BeforeFieldInit
            ;

            const IKVM.Reflection.MethodAttributes constructorAttributes =
                IKVM.Reflection.MethodAttributes.Public |
                IKVM.Reflection.MethodAttributes.HideBySig |
                IKVM.Reflection.MethodAttributes.SpecialName |
                IKVM.Reflection.MethodAttributes.RTSpecialName
            ;

            const IKVM.Reflection.MethodAttributes internalFunctionAttributes =
                IKVM.Reflection.MethodAttributes.Assembly |
                IKVM.Reflection.MethodAttributes.Static |
                IKVM.Reflection.MethodAttributes.HideBySig
            ;

            const IKVM.Reflection.MethodAttributes exportedFunctionAttributes =
                IKVM.Reflection.MethodAttributes.Public |
                IKVM.Reflection.MethodAttributes.Virtual |
                IKVM.Reflection.MethodAttributes.Final |
                IKVM.Reflection.MethodAttributes.HideBySig
            ;

            var exportsBuilder = module.DefineType("CompiledExports", classAttributes, exportContainer);

            IKVM.Reflection.MethodInfo        importedMemoryProvider = null;
            IKVM.Reflection.Emit.FieldBuilder memory = null;

            IKVM.Reflection.Emit.ILGenerator instanceConstructorIL;
            {
                var instanceConstructor = exportsBuilder.DefineConstructor(constructorAttributes, IKVM.Reflection.CallingConventions.Standard, IKVM.Reflection.Type.EmptyTypes);
                instanceConstructorIL = instanceConstructor.GetILGenerator();
                {
                    var usableConstructor = exportContainer.GetTypeInfo().DeclaredConstructors.FirstOrDefault(c => c.GetParameters().Length == 0);
                    if (usableConstructor != null)
                    {
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Call, usableConstructor);
                    }
                }
            }

            var exports           = exportsBuilder.AsType();
            var importedFunctions = 0;

            IKVM.Reflection.MethodInfo[] internalFunctions = null;
            Indirect[]             functionElements        = null;
            GlobalInfo[]           globalGetters           = null;
            GlobalInfo[]           globalSetters           = null;
            IKVMCompilationContext context = null;

            IKVM.Reflection.MethodInfo startFunction = null;
            var preSectionOffset = reader.Offset;

            while (reader.TryReadVarUInt7(out var id)) //At points where TryRead is used, the stream can safely end.
            {
                if (id != 0 && (Section)id < previousSection)
                {
                    throw new ModuleLoadException($"Sections out of order; section {(Section)id} encounterd after {previousSection}.", preSectionOffset);
                }
                var payloadLength = reader.ReadVarUInt32();

                switch ((Section)id)
                {
                case Section.None:
                {
                    var preNameOffset = reader.Offset;
                    reader.ReadString(reader.ReadVarUInt32());                                         //Name
                    reader.ReadBytes(payloadLength - checked ((uint)(reader.Offset - preNameOffset))); //Content
                }
                break;

                case Section.Type:
                {
                    signatures = new Signature[reader.ReadVarUInt32()];

                    for (var i = 0; i < signatures.Length; i++)
                    {
                        signatures[i] = new Signature(universe, reader, (uint)i);
                    }
                }
                break;

                case Section.Import:
                {
                    if (imports == null)
                    {
                        imports = Enumerable.Empty <RuntimeImport>();
                    }

                    var importsByName = imports.ToDictionary(import => new Tuple <string, string>(import.ModuleName, import.FieldName));

                    var count               = checked ((int)reader.ReadVarUInt32());
                    var functionImports     = new List <IKVM.Reflection.MethodInfo>(count);
                    var functionImportTypes = new List <Signature>(count);

                    for (var i = 0; i < count; i++)
                    {
                        var moduleName = reader.ReadString(reader.ReadVarUInt32());
                        var fieldName  = reader.ReadString(reader.ReadVarUInt32());

                        if (!importsByName.TryGetValue(new Tuple <string, string>(moduleName, fieldName), out var import))
                        {
                            throw new CompilerException($"Import not found for {moduleName}::{fieldName}.");
                        }

                        var preKindOffset = reader.Offset;
                        var kind          = (ExternalKind)reader.ReadByte();

                        switch (kind)
                        {
                        case ExternalKind.Function:
                            var typeIndex = reader.ReadVarUInt32();
                            if (!(import is FunctionImport functionImport))
                            {
                                throw new CompilerException($"{moduleName}::{fieldName} is expected to be a function, but provided import was not.");
                            }

                            var signature = signatures[typeIndex];
                            if (!signature.Equals(functionImport.Type))
                            {
                                throw new CompilerException($"{moduleName}::{fieldName} did not match the required type signature.");
                            }

                            functionImports.Add(functionImport.IKVMMethod);
                            functionImportTypes.Add(signature);
                            break;

                        case ExternalKind.Memory:
                            var limits = new ResizableLimits(reader);
                            if (!(import is MemoryImport memoryImport))
                            {
                                throw new CompilerException($"{moduleName}::{fieldName} is expected to be memory, but provided import was not.");
                            }

                            importedMemoryProvider = memoryImport.IKVMMethod;
                            break;

                        case ExternalKind.Table:
                            break;

                        case ExternalKind.Global:
                            throw new ModuleLoadException($"Imported external kind of {kind} is not currently supported.", preKindOffset);

                        default:
                            throw new ModuleLoadException($"Imported external kind of {kind} is not recognized.", preKindOffset);
                        }
                    }

                    importedFunctions  = functionImports.Count;
                    internalFunctions  = functionImports.ToArray();
                    functionSignatures = functionImportTypes.ToArray();
                }
                break;

                case Section.Function:
                {
                    var importedFunctionCount = internalFunctions == null ? 0 : internalFunctions.Length;
                    var functionIndexSize     = checked ((int)(importedFunctionCount + reader.ReadVarUInt32()));
                    if (functionSignatures != null)
                    {
                        Array.Resize(ref functionSignatures, functionIndexSize);
                    }
                    else
                    {
                        functionSignatures = new Signature[functionIndexSize];
                    }
                    if (importedFunctionCount != 0)
                    {
                        Array.Resize(ref internalFunctions, checked (functionSignatures.Length));
                    }
                    else
                    {
                        internalFunctions = new IKVM.Reflection.MethodInfo[functionSignatures.Length];
                    }

                    for (var i = importedFunctionCount; i < functionSignatures.Length; i++)
                    {
                        var signature = functionSignatures[i] = signatures[reader.ReadVarUInt32()];
                        var parms     = signature.IKVMParameterTypes.Concat(new[] { exports }).ToArray();
                        internalFunctions[i] = exportsBuilder.DefineMethod(
                            $"👻 {i}",
                            internalFunctionAttributes,
                            IKVM.Reflection.CallingConventions.Standard,
                            signature.IKVMReturnTypes.FirstOrDefault(),
                            parms
                            );
                    }
                }
                break;

                case Section.Table:
                {
                    var count = reader.ReadVarUInt32();
                    for (var i = 0; i < count; i++)
                    {
                        var elementType = (ElementType)reader.ReadVarInt7();
                        switch (elementType)
                        {
                        default:
                            throw new ModuleLoadException($"Element type {elementType} not supported.", reader.Offset - 1);

                        case ElementType.AnyFunction:
                            var setFlags = (ResizableLimits.Flags)reader.ReadVarUInt32();
                            functionElements = new Indirect[reader.ReadVarUInt32()];
                            if ((setFlags & ResizableLimits.Flags.Maximum) != 0)
                            {
                                reader.ReadVarUInt32();             //Not used.
                            }
                            break;
                        }
                    }
                }
                break;

                case Section.Memory:
                {
                    var preCountOffset = reader.Offset;
                    var count          = reader.ReadVarUInt32();
                    if (count > 1)
                    {
                        throw new ModuleLoadException("Multiple memory values are not supported.", preCountOffset);
                    }

                    var setFlags = (ResizableLimits.Flags)reader.ReadVarUInt32();
                    memoryPagesMinimum = reader.ReadVarUInt32();
                    if ((setFlags & ResizableLimits.Flags.Maximum) != 0)
                    {
                        memoryPagesMaximum = Math.Min(reader.ReadVarUInt32(), uint.MaxValue / Memory.PageSize);
                    }
                    else
                    {
                        memoryPagesMaximum = uint.MaxValue / Memory.PageSize;
                    }

                    memory = exportsBuilder.DefineField("☣ Memory", universe.Import(typeof(Runtime.UnmanagedMemory)), IKVM.Reflection.FieldAttributes.Private | IKVM.Reflection.FieldAttributes.InitOnly);

                    instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                    if (importedMemoryProvider == null)
                    {
                        Instructions.Int32Constant.Emit(instanceConstructorIL, (int)memoryPagesMinimum);
                        Instructions.Int32Constant.Emit(instanceConstructorIL, (int)memoryPagesMaximum);
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Newobj, universe.Import(typeof(uint?)).GetTypeInfo().DeclaredConstructors.Where(info =>
                            {
                                var parms = info.GetParameters();
                                return(parms.Length == 1 && parms[0].ParameterType == universe.Import(typeof(uint)));
                            }).First());

                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Newobj, universe.Import(typeof(Runtime.UnmanagedMemory)).GetTypeInfo().DeclaredConstructors.Where(info =>
                            {
                                var parms = info.GetParameters();
                                return(parms.Length == 2 && parms[0].ParameterType == universe.Import(typeof(uint)) && parms[1].ParameterType == universe.Import(typeof(uint?)));
                            }).First());
                    }
                    else
                    {
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Call, importedMemoryProvider);
                    }

                    instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Stfld, memory);

                    exportsBuilder.AddInterfaceImplementation(universe.Import(typeof(IDisposable)));

                    List <IKVM.Reflection.Type> ikvmEmptyTypes = new List <IKVM.Reflection.Type>();
                    foreach (System.Type type in System.Type.EmptyTypes)
                    {
                        ikvmEmptyTypes.Add(universe.Import(type));
                    }

                    var dispose = exportsBuilder.DefineMethod(
                        "Dispose",
                        IKVM.Reflection.MethodAttributes.Public | IKVM.Reflection.MethodAttributes.Virtual | IKVM.Reflection.MethodAttributes.Final | IKVM.Reflection.MethodAttributes.HideBySig | IKVM.Reflection.MethodAttributes.NewSlot,
                        IKVM.Reflection.CallingConventions.HasThis,
                        universe.Import(typeof(void)),
                        ikvmEmptyTypes.ToArray()
                        );

                    var disposeIL = dispose.GetILGenerator();
                    disposeIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                    disposeIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldfld, memory);
                    disposeIL.Emit(IKVM.Reflection.Emit.OpCodes.Call, universe.Import(typeof(Runtime.UnmanagedMemory))
                                   .GetTypeInfo()
                                   .DeclaredMethods
                                   .Where(info =>
                                          info.ReturnType == universe.Import(typeof(void)) &&
                                          info.GetParameters().Length == 0 &&
                                          info.Name == nameof(Runtime.UnmanagedMemory.Dispose))
                                   .First());
                    disposeIL.Emit(IKVM.Reflection.Emit.OpCodes.Ret);
                }
                break;

                case Section.Global:
                {
                    var count = reader.ReadVarUInt32();
                    globalGetters = new GlobalInfo[count];
                    globalSetters = new GlobalInfo[count];

                    context = new IKVMCompilationContext(
                        universe,
                        exportsBuilder,
                        memory,
                        functionSignatures,
                        internalFunctions,
                        signatures,
                        null,
                        module,
                        globalGetters,
                        globalSetters
                        );

                    var emptySignature = Signature.Empty;

                    for (var i = 0; i < globalGetters.Length; i++)
                    {
                        var contentType = (ValueType)reader.ReadVarInt7();
                        var isMutable   = reader.ReadVarUInt1() == 1;

                        var getter = exportsBuilder.DefineMethod(
                            $"🌍 Get {i}",
                            internalFunctionAttributes,
                            IKVM.Reflection.CallingConventions.Standard,
                            universe.Import(contentType.ToSystemType()),
                            isMutable ? new[] { exports } : null
                            );

                        globalGetters[i] = new GlobalInfo(contentType, isMutable, getter);

                        var il = getter.GetILGenerator();
                        var getterSignature = new Signature(contentType);

                        if (isMutable == false)
                        {
                            context.Reset(
                                il,
                                getterSignature,
                                getterSignature.RawParameterTypes
                                );

                            foreach (var instruction in Instruction.ParseInitializerExpression(reader))
                            {
                                instruction.CompileIKVM(context, universe);
                                context.Previous = instruction.OpCode;
                            }
                        }
                        else         //Mutable
                        {
                            var field = exportsBuilder.DefineField(
                                $"🌍 {i}",
                                universe.Import(contentType.ToSystemType()),
                                IKVM.Reflection.FieldAttributes.Private | (isMutable ? 0 : IKVM.Reflection.FieldAttributes.InitOnly)
                                );

                            il.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                            il.Emit(IKVM.Reflection.Emit.OpCodes.Ldfld, field);
                            il.Emit(IKVM.Reflection.Emit.OpCodes.Ret);

                            var setter = exportsBuilder.DefineMethod(
                                $"🌍 Set {i}",
                                internalFunctionAttributes,
                                IKVM.Reflection.CallingConventions.Standard,
                                universe.Import(typeof(void)),
                                new[] { universe.Import(contentType.ToSystemType()), exports }
                                );

                            il = setter.GetILGenerator();
                            il.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_1);
                            il.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                            il.Emit(IKVM.Reflection.Emit.OpCodes.Stfld, field);
                            il.Emit(IKVM.Reflection.Emit.OpCodes.Ret);

                            globalSetters[i] = new GlobalInfo(contentType, isMutable, setter);

                            context.Reset(
                                instanceConstructorIL,
                                emptySignature,
                                emptySignature.RawParameterTypes
                                );

                            context.EmitLoadThis();
                            var ended = false;

                            foreach (var instruction in Instruction.ParseInitializerExpression(reader))
                            {
                                if (ended)
                                {
                                    throw new CompilerException("Only a single End is allowed within an initializer expression.");
                                }

                                if (instruction.OpCode == OpCode.End)
                                {
                                    context.Emit(IKVM.Reflection.Emit.OpCodes.Stfld, field);
                                    ended = true;
                                    continue;
                                }

                                instruction.CompileIKVM(context, universe);
                                context.Previous = instruction.OpCode;
                            }
                        }
                    }
                }
                break;

                case Section.Export:
                {
                    const IKVM.Reflection.MethodAttributes exportedPropertyAttributes =
                        IKVM.Reflection.MethodAttributes.Public |
                        IKVM.Reflection.MethodAttributes.HideBySig |
                        IKVM.Reflection.MethodAttributes.SpecialName |
                        IKVM.Reflection.MethodAttributes.Virtual |
                        IKVM.Reflection.MethodAttributes.Final;
                    var totalExports = reader.ReadVarUInt32();
                    var xFunctions   = new List <KeyValuePair <string, uint> >((int)Math.Min(int.MaxValue, totalExports));

                    for (var i = 0; i < totalExports; i++)
                    {
                        var name           = reader.ReadString(reader.ReadVarUInt32());
                        var kind           = (ExternalKind)reader.ReadByte();
                        var preIndexOffset = reader.Offset;
                        var index          = reader.ReadVarUInt32();
                        switch (kind)
                        {
                        case ExternalKind.Function:
                            xFunctions.Add(new KeyValuePair <string, uint>(name, index));
                            break;

                        case ExternalKind.Table:
                            throw new NotSupportedException($"Unsupported export kind {kind}.");

                        case ExternalKind.Memory:
                            if (index != 0)
                            {
                                throw new ModuleLoadException($"Exported memory must be of index 0, found {index}.", preIndexOffset);
                            }
                            if (memory == null)
                            {
                                throw new CompilerException("Cannot export linear memory when linear memory is not defined.");
                            }

                            {
                                var memoryGetter = exportsBuilder.DefineMethod("get_" + name,
                                                                               exportedPropertyAttributes,
                                                                               IKVM.Reflection.CallingConventions.HasThis,
                                                                               universe.Import(typeof(Runtime.UnmanagedMemory)),
                                                                               IKVM.Reflection.Type.EmptyTypes
                                                                               );
                                var getterIL = memoryGetter.GetILGenerator();
                                getterIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                                getterIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldfld, memory);
                                getterIL.Emit(IKVM.Reflection.Emit.OpCodes.Ret);

                                exportsBuilder.DefineProperty(name, IKVM.Reflection.PropertyAttributes.None, universe.Import(typeof(Runtime.UnmanagedMemory)), IKVM.Reflection.Type.EmptyTypes)
                                .SetGetMethod(memoryGetter);
                            }
                            break;

                        case ExternalKind.Global:
                            if (index >= globalGetters.Length)
                            {
                                throw new ModuleLoadException($"Exported global index of {index} is greater than the number of globals {globalGetters.Length}.", preIndexOffset);
                            }

                            {
                                var getter     = globalGetters[i];
                                var setter     = globalSetters[i];
                                var property   = exportsBuilder.DefineProperty(name, IKVM.Reflection.PropertyAttributes.None, universe.Import(getter.Type.ToSystemType()), IKVM.Reflection.Type.EmptyTypes);
                                var wrappedGet = exportsBuilder.DefineMethod("get_" + name,
                                                                             exportedPropertyAttributes,
                                                                             IKVM.Reflection.CallingConventions.HasThis,
                                                                             universe.Import(getter.Type.ToSystemType()),
                                                                             IKVM.Reflection.Type.EmptyTypes
                                                                             );

                                var wrappedGetIL = wrappedGet.GetILGenerator();
                                if (getter.IsMutable)
                                {
                                    wrappedGetIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                                }
                                wrappedGetIL.Emit(IKVM.Reflection.Emit.OpCodes.Call, getter.Builder);
                                wrappedGetIL.Emit(IKVM.Reflection.Emit.OpCodes.Ret);
                                property.SetGetMethod(wrappedGet);

                                if (setter != null)
                                {
                                    var wrappedSet = exportsBuilder.DefineMethod("set_" + name,
                                                                                 exportedPropertyAttributes,
                                                                                 IKVM.Reflection.CallingConventions.HasThis,
                                                                                 null,
                                                                                 new[] { universe.Import(getter.Type.ToSystemType()) }
                                                                                 );

                                    var wrappedSetIL = wrappedSet.GetILGenerator();
                                    wrappedSetIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_1);
                                    wrappedSetIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                                    wrappedSetIL.Emit(IKVM.Reflection.Emit.OpCodes.Call, setter.Builder);
                                    wrappedSetIL.Emit(IKVM.Reflection.Emit.OpCodes.Ret);

                                    property.SetSetMethod(wrappedSet);
                                }
                            }
                            break;

                        default:
                            throw new NotSupportedException($"Unrecognized export kind {kind}.");
                        }
                    }

                    exportedFunctions = xFunctions.ToArray();
                }
                break;

                case Section.Start:
                {
                    var preReadOffset = reader.Offset;
                    var startIndex    = reader.ReadVarInt32();
                    if (startIndex >= internalFunctions.Length)
                    {
                        throw new ModuleLoadException($"Start function of index {startIndex} exceeds available functions of {internalFunctions.Length}", preReadOffset);
                    }

                    startFunction = internalFunctions[startIndex];
                }
                break;

                case Section.Element:
                {
                    if (functionElements == null)
                    {
                        throw new ModuleLoadException("Element section found without an associated table section.", preSectionOffset);
                    }

                    var count = reader.ReadVarUInt32();
                    for (var i = 0; i < count; i++)
                    {
                        var preIndexOffset = reader.Offset;
                        var index          = reader.ReadVarUInt32();
                        if (index != 0)
                        {
                            throw new ModuleLoadException($"Index value of anything other than 0 is not supported, {index} found.", preIndexOffset);
                        }

                        {
                            var preInitializerOffset = reader.Offset;
                            var initializer          = Instruction.ParseInitializerExpression(reader).ToArray();
                            if (initializer.Length != 2 || !(initializer[0] is Instructions.Int32Constant c) || c.Value != 0 || !(initializer[1] is Instructions.End))
                            {
                                throw new ModuleLoadException("Initializer expression support for the Element section is limited to a single Int32 constant of 0 followed by end.", preInitializerOffset);
                            }
                        }

                        var preElementsOffset = reader.Offset;
                        var elements          = reader.ReadVarUInt32();
                        if (elements != functionElements.Length)
                        {
                            throw new ModuleLoadException($"Element count {elements} does not match the indication provided by the earlier table {functionElements.Length}.", preElementsOffset);
                        }

                        for (var j = 0; j < functionElements.Length; j++)
                        {
                            var functionIndex = reader.ReadVarUInt32();
                            functionElements[j] = new Indirect(
                                functionSignatures[functionIndex].TypeIndex,
                                (IKVM.Reflection.Emit.MethodBuilder)internalFunctions[importedFunctions + functionIndex]
                                );
                        }
                    }
                }
                break;

                case Section.Code:
                {
                    var preBodiesIndex = reader.Offset;
                    var functionBodies = reader.ReadVarUInt32();

                    if (functionBodies > 0 && (functionSignatures == null || functionSignatures.Length == importedFunctions))
                    {
                        throw new ModuleLoadException("Code section is invalid when Function section is missing.", preBodiesIndex);
                    }
                    if (functionBodies != functionSignatures.Length - importedFunctions)
                    {
                        throw new ModuleLoadException($"Code section has {functionBodies} functions described but {functionSignatures.Length - importedFunctions} were expected.", preBodiesIndex);
                    }

                    if (context == null)         //Might have been created by the Global section, if present.
                    {
                        context = new IKVMCompilationContext(
                            universe,
                            exportsBuilder,
                            memory,
                            functionSignatures,
                            internalFunctions,
                            signatures,
                            functionElements,
                            module,
                            globalGetters,
                            globalSetters
                            );
                    }

                    for (var functionBodyIndex = 0; functionBodyIndex < functionBodies; functionBodyIndex++)
                    {
                        var signature      = functionSignatures[importedFunctions + functionBodyIndex];
                        var byteLength     = reader.ReadVarUInt32();
                        var startingOffset = reader.Offset;

                        var locals = new Local[reader.ReadVarUInt32()];
                        for (var localIndex = 0; localIndex < locals.Length; localIndex++)
                        {
                            locals[localIndex] = new Local(reader);
                        }

                        var il = ((IKVM.Reflection.Emit.MethodBuilder)internalFunctions[importedFunctions + functionBodyIndex]).GetILGenerator();

                        context.Reset(
                            il,
                            signature,
                            signature.RawParameterTypes.Concat(
                                locals
                                .SelectMany(local => Enumerable.Range(0, checked ((int)local.Count)).Select(_ => local.Type))
                                ).ToArray()
                            );

                        foreach (var local in locals.SelectMany(local => Enumerable.Range(0, checked ((int)local.Count)).Select(_ => local.Type)))
                        {
                            il.DeclareLocal(universe.Import(local.ToSystemType()));
                        }

                        foreach (var instruction in Instruction.Parse(reader))
                        {
                            instruction.CompileIKVM(context, universe);
                            context.Previous = instruction.OpCode;
                        }

                        if (reader.Offset - startingOffset != byteLength)
                        {
                            throw new ModuleLoadException($"Instruction sequence reader ended after readering {reader.Offset - startingOffset} characters, expected {byteLength}.", reader.Offset);
                        }
                    }
                }
                break;

                case Section.Data:
                {
                    if (memory == null)
                    {
                        throw new ModuleLoadException("Data section cannot be used unless a memory section is defined.", preSectionOffset);
                    }

                    var count = reader.ReadVarUInt32();

                    if (context == null)         //Would only be null if there is no Global or Code section, but have to check.
                    {
                        context = new IKVMCompilationContext(
                            universe,
                            exportsBuilder,
                            memory,
                            new Signature[0],
                            new IKVM.Reflection.MethodInfo[0],
                            new Signature[0],
                            functionElements,
                            module,
                            globalGetters,
                            globalSetters
                            );
                    }

                    context.Reset(
                        instanceConstructorIL,
                        Signature.Empty,
                        Signature.Empty.RawParameterTypes
                        );

                    var block = new Instructions.Block(BlockType.Int32);

                    var address = instanceConstructorIL.DeclareLocal(universe.Import(typeof(uint)));

                    for (var i = 0; i < count; i++)
                    {
                        var startingOffset = reader.Offset;
                        {
                            var index = reader.ReadVarUInt32();
                            if (index != 0)
                            {
                                throw new ModuleLoadException($"Data index must be 0, found {index}.", startingOffset);
                            }
                        }

                        block.CompileIKVM(context, universe);         //Prevents "end" instruction of the initializer expression from becoming a return.
                        foreach (var instruction in Instruction.ParseInitializerExpression(reader))
                        {
                            instruction.CompileIKVM(context, universe);
                            context.Previous = instruction.OpCode;
                        }
                        context.Stack.Pop();
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Stloc, address);

                        var data = reader.ReadBytes(reader.ReadVarUInt32());

                        //Ensure sufficient memory is allocated, error if max is exceeded.
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldloc, address);
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldc_I4, data.Length);
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Add_Ovf_Un);

                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);

                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Call, Instructions.MemoryImmediateInstruction.IKVMCreateRangeCheck(HelperMethod.RangeCheck8, context, universe));
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Pop);

                        if (data.Length > 0x3f0000)         //Limitation of DefineInitializedData, can be corrected by splitting the data.
                        {
                            throw new NotSupportedException($"Data segment {i} is length {data.Length}, exceeding the current implementation limit of 4128768.");
                        }

                        var field = exportsBuilder.DefineInitializedData($"☣ Data {i}", data, IKVM.Reflection.FieldAttributes.Assembly | IKVM.Reflection.FieldAttributes.InitOnly);

                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldfld, memory);
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Call, Runtime.UnmanagedMemory.IKVMStartGetter(universe));
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldloc, address);
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Conv_I);
                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Add_Ovf_Un);

                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldsflda, field);

                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldc_I4, data.Length);

                        instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Cpblk);
                    }
                }
                break;

                default:
                    throw new ModuleLoadException($"Unrecognized section type {(Section)id}.", preSectionOffset);
                }

                previousSection = (Section)id;
            }

            if (exportedFunctions != null)
            {
                for (var i = 0; i < exportedFunctions.Length; i++)
                {
                    var exported  = exportedFunctions[i];
                    var signature = functionSignatures[exported.Value];
                    List <IKVM.Reflection.Type> signaturearray = new List <IKVM.Reflection.Type>();

                    foreach (var s in signature.ParameterTypes)
                    {
                        signaturearray.Add(universe.Import(s));
                    }

                    var method = exportsBuilder.DefineMethod(
                        exported.Key,
                        exportedFunctionAttributes,
                        IKVM.Reflection.CallingConventions.HasThis,
                        universe.Import(signature.ReturnTypes.FirstOrDefault()),
                        signaturearray.ToArray()
                        );

                    var il = method.GetILGenerator();
                    for (var parm = 0; parm < signature.ParameterTypes.Length; parm++)
                    {
                        il.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg, parm + 1);
                    }

                    il.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                    il.Emit(IKVM.Reflection.Emit.OpCodes.Call, internalFunctions[exported.Value]);
                    il.Emit(IKVM.Reflection.Emit.OpCodes.Ret);
                }
            }

            if (startFunction != null)
            {
                instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Call, startFunction);
            }

            instanceConstructorIL.Emit(IKVM.Reflection.Emit.OpCodes.Ret); //Finish the constructor.
            var exportInfo = exportsBuilder.CreateTypeInfo();

            IKVM.Reflection.TypeInfo instance;
            {
                var instanceBuilder     = module.DefineType("CompiledInstance", classAttributes, instanceContainer);
                var instanceConstructor = instanceBuilder.DefineConstructor(constructorAttributes, IKVM.Reflection.CallingConventions.Standard, null);
                var il = instanceConstructor.GetILGenerator();
                var memoryAllocated = checked (memoryPagesMaximum * Memory.PageSize);

                il.Emit(IKVM.Reflection.Emit.OpCodes.Ldarg_0);
                il.Emit(IKVM.Reflection.Emit.OpCodes.Newobj, exportInfo.DeclaredConstructors.First());
                il.Emit(IKVM.Reflection.Emit.OpCodes.Call, instanceContainer
                        .GetTypeInfo()
                        .DeclaredConstructors
                        .First(info => info.GetParameters()
                               .FirstOrDefault()
                               ?.ParameterType == exportContainer
                               ));
                il.Emit(IKVM.Reflection.Emit.OpCodes.Ret);

                instance = instanceBuilder.CreateTypeInfo();
            }

            module.CreateGlobalFunctions();
            return(assembly);
        }