private static ConstructorInfo FromBinary( Reader reader, System.Type instanceContainer, System.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 module = AssemblyBuilder.DefineDynamicAssembly( new AssemblyName("CompiledWebAssembly"), AssemblyBuilderAccess.RunAndCollect ) .DefineDynamicModule("CompiledWebAssembly") ; const TypeAttributes classAttributes = TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.BeforeFieldInit ; const MethodAttributes constructorAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName ; const MethodAttributes internalFunctionAttributes = MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig ; const MethodAttributes exportedFunctionAttributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig ; var exportsBuilder = module.DefineType("CompiledExports", classAttributes, exportContainer); FieldBuilder memory = null; ILGenerator instanceConstructorIL; { var instanceConstructor = exportsBuilder.DefineConstructor(constructorAttributes, CallingConventions.Standard, System.Type.EmptyTypes); instanceConstructorIL = instanceConstructor.GetILGenerator(); { var usableConstructor = exportContainer.GetTypeInfo().DeclaredConstructors.FirstOrDefault(c => c.GetParameters().Length == 0); if (usableConstructor != null) { instanceConstructorIL.Emit(OpCodes.Ldarg_0); instanceConstructorIL.Emit(OpCodes.Call, usableConstructor); } } } var exports = exportsBuilder.AsType(); var importedFunctions = 0; MethodInfo[] internalFunctions = null; Indirect[] functionElements = null; GlobalInfo[] globalGetters = null; GlobalInfo[] globalSetters = null; CompilationContext context = null; MethodInfo startFunction = null; while (reader.TryReadVarUInt7(out var id)) //At points where TryRead is used, the stream can safely end. { var payloadLength = reader.ReadVarUInt32(); if (id != 0 && (Section)id < previousSection) { throw new ModuleLoadException($"Sections out of order; section {(Section)id} encounterd after {previousSection}.", reader.Offset); } 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(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 = reader.ReadVarUInt32(); var functionImports = new List <MethodInfo>(checked ((int)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 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."); } if (!signatures[typeIndex].Equals(functionImport.Type)) { throw new CompilerException($"{moduleName}::{fieldName} did not match the required type signature."); } functionImports.Add(functionImport.Method); break; case ExternalKind.Table: case ExternalKind.Memory: case ExternalKind.Global: throw new ModuleLoadException($"Imported external kind of {kind} is not currently supported.", reader.Offset); default: throw new ModuleLoadException($"Imported external kind of {kind} is not recognized.", reader.Offset); } } importedFunctions = functionImports.Count; internalFunctions = functionImports.ToArray(); } break; case Section.Function: { functionSignatures = new Signature[reader.ReadVarUInt32()]; var importedFunctionCount = internalFunctions == null ? 0 : internalFunctions.Length; if (importedFunctionCount != 0) { Array.Resize(ref internalFunctions, checked (importedFunctionCount + functionSignatures.Length)); } else { internalFunctions = new MethodInfo[functionSignatures.Length]; } for (var i = 0; i < functionSignatures.Length; i++) { var signature = functionSignatures[i] = signatures[reader.ReadVarUInt32()]; var parms = signature.ParameterTypes.Concat(new[] { exports }).ToArray(); internalFunctions[importedFunctionCount + i] = exportsBuilder.DefineMethod( $"👻 {i}", internalFunctionAttributes, CallingConventions.Standard, signature.ReturnTypes.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); 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 count = reader.ReadVarUInt32(); if (count > 1) { throw new ModuleLoadException("Multiple memory values are not supported.", reader.Offset); } 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", typeof(Runtime.UnmanagedMemory), FieldAttributes.Private | FieldAttributes.InitOnly); instanceConstructorIL.Emit(OpCodes.Ldarg_0); Instructions.Int32Constant.Emit(instanceConstructorIL, (int)memoryPagesMinimum); Instructions.Int32Constant.Emit(instanceConstructorIL, (int)memoryPagesMaximum); instanceConstructorIL.Emit(OpCodes.Newobj, typeof(uint?).GetTypeInfo().DeclaredConstructors.Where(info => { var parms = info.GetParameters(); return(parms.Length == 1 && parms[0].ParameterType == typeof(uint)); }).First()); instanceConstructorIL.Emit(OpCodes.Newobj, typeof(Runtime.UnmanagedMemory).GetTypeInfo().DeclaredConstructors.Where(info => { var parms = info.GetParameters(); return(parms.Length == 2 && parms[0].ParameterType == typeof(uint) && parms[1].ParameterType == typeof(uint?)); }).First()); instanceConstructorIL.Emit(OpCodes.Stfld, memory); exportsBuilder.AddInterfaceImplementation(typeof(IDisposable)); var dispose = exportsBuilder.DefineMethod( "Dispose", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot, CallingConventions.HasThis, typeof(void), System.Type.EmptyTypes ); var disposeIL = dispose.GetILGenerator(); disposeIL.Emit(OpCodes.Ldarg_0); disposeIL.Emit(OpCodes.Ldfld, memory); disposeIL.Emit(OpCodes.Call, typeof(Runtime.UnmanagedMemory) .GetTypeInfo() .DeclaredMethods .Where(info => info.ReturnType == typeof(void) && info.GetParameters().Length == 0 && info.Name == nameof(Runtime.UnmanagedMemory.Dispose)) .First()); disposeIL.Emit(OpCodes.Ret); } break; case Section.Global: { var count = reader.ReadVarUInt32(); globalGetters = new GlobalInfo[count]; globalSetters = new GlobalInfo[count]; context = new CompilationContext( exportsBuilder, memory, functionSignatures, internalFunctions, signatures, functionElements, 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, CallingConventions.Standard, 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.Compile(context); context.Previous = instruction.OpCode; } } else //Mutable { var field = exportsBuilder.DefineField( $"🌍 {i}", contentType.ToSystemType(), FieldAttributes.Private | (isMutable ? 0 : FieldAttributes.InitOnly) ); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Ret); var setter = exportsBuilder.DefineMethod( $"🌍 Set {i}", internalFunctionAttributes, CallingConventions.Standard, typeof(void), new[] { contentType.ToSystemType(), exports } ); il = setter.GetILGenerator(); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Stfld, field); il.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(OpCodes.Stfld, field); ended = true; continue; } instruction.Compile(context); context.Previous = instruction.OpCode; } } } } break; case Section.Export: { 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 index = reader.ReadVarUInt32(); switch (kind) { case ExternalKind.Function: xFunctions.Add(new KeyValuePair <string, uint>(name, index)); break; case ExternalKind.Memory: if (index != 0) { throw new ModuleLoadException($"Exported memory must be of index 0, found {index}.", reader.Offset); } if (memory == null) { throw new CompilerException("Cannot export linear memory when linear memory is not defined."); } var memoryGetter = exportsBuilder.DefineMethod("get_" + name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, typeof(Runtime.UnmanagedMemory), System.Type.EmptyTypes ); var getterIL = memoryGetter.GetILGenerator(); getterIL.Emit(OpCodes.Ldarg_0); getterIL.Emit(OpCodes.Ldfld, memory); getterIL.Emit(OpCodes.Ret); exportsBuilder.DefineProperty(name, PropertyAttributes.None, typeof(Runtime.UnmanagedMemory), System.Type.EmptyTypes) .SetGetMethod(memoryGetter); break; default: throw new NotSupportedException($"Unsupported or 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.", reader.Offset); } var count = reader.ReadVarUInt32(); for (var i = 0; i < count; i++) { var index = reader.ReadVarUInt32(); if (index != 0) { throw new ModuleLoadException($"Index value of anything other than 0 is not supported, {index} found.", 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.", 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}.", reader.Offset); } for (var j = 0; j < functionElements.Length; j++) { var functionIndex = reader.ReadVarUInt32(); functionElements[j] = new Indirect( functionSignatures[functionIndex].TypeIndex, (MethodBuilder)internalFunctions[importedFunctions + functionIndex] ); } } } break; case Section.Code: { var functionBodies = reader.ReadVarUInt32(); if (functionBodies > 0 && functionSignatures == null) { throw new ModuleLoadException("Code section is invalid when Function section is missing.", reader.Offset); } if (functionBodies != functionSignatures.Length) { throw new ModuleLoadException($"Code section has {functionBodies} functions described but {functionSignatures.Length} were expected.", reader.Offset); } if (context == null) //Might have been created by the Global section, if present. { context = new CompilationContext( exportsBuilder, memory, functionSignatures, internalFunctions, signatures, functionElements, module, globalGetters, globalSetters ); } for (var functionBodyIndex = 0; functionBodyIndex < functionBodies; functionBodyIndex++) { var signature = functionSignatures[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 = ((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(local.ToSystemType()); } foreach (var instruction in Instruction.Parse(reader)) { instruction.Compile(context); 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; default: throw new ModuleLoadException($"Unrecognized section type {(Section)id}.", reader.Offset); } previousSection = (Section)id; } if (exportedFunctions != null) { for (var i = 0; i < exportedFunctions.Length; i++) { var exported = exportedFunctions[i]; var signature = functionSignatures[exported.Value - importedFunctions]; var method = exportsBuilder.DefineMethod( exported.Key, exportedFunctionAttributes, CallingConventions.HasThis, signature.ReturnTypes.FirstOrDefault(), signature.ParameterTypes ); var il = method.GetILGenerator(); for (var parm = 0; parm < signature.ParameterTypes.Length; parm++) { il.Emit(OpCodes.Ldarg, parm + 1); } il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, internalFunctions[exported.Value]); il.Emit(OpCodes.Ret); } } if (startFunction != null) { instanceConstructorIL.Emit(OpCodes.Ldarg_0); instanceConstructorIL.Emit(OpCodes.Call, startFunction); } instanceConstructorIL.Emit(OpCodes.Ret); //Finish the constructor. var exportInfo = exportsBuilder.CreateTypeInfo(); TypeInfo instance; { var instanceBuilder = module.DefineType("CompiledInstance", classAttributes, instanceContainer); var instanceConstructor = instanceBuilder.DefineConstructor(constructorAttributes, CallingConventions.Standard, null); var il = instanceConstructor.GetILGenerator(); var memoryAllocated = checked (memoryPagesMaximum * Memory.PageSize); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Newobj, exportInfo.DeclaredConstructors.First()); il.Emit(OpCodes.Call, instanceContainer .GetTypeInfo() .DeclaredConstructors .First(info => info.GetParameters() .FirstOrDefault() ?.ParameterType == exportContainer ) ); il.Emit(OpCodes.Ret); instance = instanceBuilder.CreateTypeInfo(); } module.CreateGlobalFunctions(); return(instance.DeclaredConstructors.First()); }
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); }