diff --git a/Libraries/JSIL.PInvoke.js b/Libraries/JSIL.PInvoke.js index 2cef2c075..c66d0ffd2 100644 --- a/Libraries/JSIL.PInvoke.js +++ b/Libraries/JSIL.PInvoke.js @@ -154,6 +154,10 @@ JSIL.MakeClass("System.Object", "JSIL.Runtime.NativePackedArray`1", true, ["T"], this.ElementSize = JSIL.GetNativeSizeOf(this.T, false); var sizeBytes = this.ElementSize * this.Length; + // HACK because emscripten malloc is finicky + if (sizeBytes < 4) + sizeBytes = 4; + this.Module = module; this.EmscriptenOffset = module._malloc(sizeBytes); @@ -165,21 +169,21 @@ JSIL.MakeClass("System.Object", "JSIL.Runtime.NativePackedArray`1", true, ["T"], } else { var buffer = this.MemoryRange.getView(tByte); - var arrayType = JSIL.PackedStructArray.Of(elementTypeObject); + var arrayType = JSIL.PackedStructArray.Of(this.T); this._Array = new arrayType(buffer, this.MemoryRange); } }); $.Method({Static: false, Public: true }, ".ctor", new JSIL.MethodSignature(null, [$.Int32], []), - function (size) { + function NativePackedArray_ctor (size) { this.$innerCtor(JSIL.PInvoke.GetDefaultModule(), size); } ); $.Method({Static: false, Public: true }, ".ctor", new JSIL.MethodSignature(null, [$.String, $.Int32], []), - function (dllName, size) { + function NativePackedArray_ctor (dllName, size) { this.$innerCtor(JSIL.PInvoke.GetModule(dllName), size); } ); @@ -199,6 +203,23 @@ JSIL.MakeClass("System.Object", "JSIL.Runtime.NativePackedArray`1", true, ["T"], } ); + $.Method({Static:false, Public:true }, "AllocHandle", + new JSIL.MethodSignature($jsilcore.TypeRef("System.Runtime.InteropServices.GCHandle"), [], []), + function AllocHandle () { + return System.Runtime.InteropServices.GCHandle.Alloc(this._Array); + } + ); + + $.Method({Static:false, Public:true }, "AllocHandle", + new JSIL.MethodSignature($jsilcore.TypeRef("System.Runtime.InteropServices.GCHandle"), [$jsilcore.TypeRef("System.Runtime.InteropServices.GCHandleType")], []), + function AllocHandle (type) { + // FIXME: type + return System.Runtime.InteropServices.GCHandle.Alloc( + this._Array, type + ); + } + ); + $.Method( {Public: true , Static: false}, "Dispose", JSIL.MethodSignature.Void, @@ -411,10 +432,23 @@ JSIL.PInvoke.IntPtrMarshaller.prototype.GetSignatureToken = function () { JSIL.PInvoke.IntPtrMarshaller.prototype.ManagedToNative = function (managedValue, callContext) { if (managedValue.pointer) { - if (callContext.module.HEAPU8.buffer !== managedValue.pointer.memoryRange.buffer) - JSIL.RuntimeError("The pointer does not point into the module's heap"); + var sourceBuffer = managedValue.pointer.memoryRange.buffer; + var destBuffer = callContext.module.HEAPU8.buffer; + + if (destBuffer === sourceBuffer) { + // Pointer is in the correct heap, so marshal as-is. + return managedValue.pointer.offsetInBytes | 0; + } else { + // HACK: Use the length of the underlying memory range the pointer + // is aimed at as the length of the region being marshalled. + // Best we can do. Will be correct for trivial cases (pinned an array) + return JSIL.PInvoke.ArrayMarshaller.CreateTemporaryNativeCopy( + managedValue.pointer, managedValue.pointer.memoryRange.length, callContext, + // HACK: no way to infer isOut reliably here + true + ); + } - return managedValue.pointer.offsetInBytes | 0; } else { // HACK: We have no way to know this address is in the correct heap. @@ -529,6 +563,30 @@ JSIL.PInvoke.ArrayMarshaller.prototype.GetSignatureToken = function () { return "i"; }; +JSIL.PInvoke.ArrayMarshaller.CreateTemporaryNativeCopy = function (pointer, sizeBytes, callContext, isOut) { + var module = callContext.module; + + if (pointer.memoryRange.buffer === module.HEAPU8.buffer) { + return pointer.offsetInBytes | 0; + } else { + // Copy to temporary storage on the emscripten heap, then copy back after the call + var emscriptenOffset = callContext.Allocate(sizeBytes); + + var sourceView = pointer.asView($jsilcore.System.Byte, sizeBytes); + var destView = new Uint8Array(module.HEAPU8.buffer, emscriptenOffset, sizeBytes); + + destView.set(sourceView, 0); + + if (isOut) { + callContext.QueueCleanup(function () { + sourceView.set(destView, 0); + }); + } + + return emscriptenOffset; + } +}; + JSIL.PInvoke.ArrayMarshaller.prototype.ManagedToNative = function (managedValue, callContext) { var module = callContext.module; @@ -571,28 +629,12 @@ JSIL.PInvoke.ArrayMarshaller.prototype.ManagedToNative = function (managedValue, }); } - return emscriptenOffset; - } else if (pointer.memoryRange.buffer === module.HEAPU8.buffer) { - return pointer.offsetInBytes | 0; - } else { - // Copy to temporary storage on the emscripten heap, then copy back after the call - - var sizeBytes = managedValue.byteLength; - var emscriptenOffset = callContext.Allocate(sizeBytes); - - var sourceView = pointer.asView($jsilcore.System.Byte, sizeBytes); - var destView = new Uint8Array(module.HEAPU8.buffer, emscriptenOffset, sizeBytes); - - destView.set(sourceView, 0); - - if (this.isOut) { - callContext.QueueCleanup(function () { - sourceView.set(destView, 0); - }); - } - return emscriptenOffset; } + + return JSIL.PInvoke.ArrayMarshaller.CreateTemporaryNativeCopy( + pointer, managedValue.byteLength, callContext, this.isOut + ); }; JSIL.PInvoke.ArrayMarshaller.prototype.NativeToManaged = function (nativeValue, callContext) { diff --git a/Libraries/JSIL.Unsafe.js b/Libraries/JSIL.Unsafe.js index b40a65dd5..5cca87884 100644 --- a/Libraries/JSIL.Unsafe.js +++ b/Libraries/JSIL.Unsafe.js @@ -505,6 +505,11 @@ JSIL.MakeClass("System.Object", "JSIL.MemoryRange", true, [], function ($) { this.length = buffer.byteLength; } + if (this.offset < 0) + JSIL.RuntimeError("MemoryRange offset must be >= 0"); + else if (this.length < 0) + JSIL.RuntimeError("MemoryRange length must be >= 0"); + if (typeof (Map) !== "undefined") { this.viewCache = new Map(); this.viewCacheIsMap = true; @@ -1436,7 +1441,9 @@ JSIL.UnmarshalStruct = function Struct_Unmarshal (struct, bytes, offset) { }; JSIL.GetNativeSizeOf = function GetNativeSizeOf (typeObject, forPInvoke) { - if (typeObject.__IsNativeType__) { + if (!typeObject) { + return -1; + } if (typeObject.__IsNativeType__) { var arrayCtor = JSIL.GetTypedArrayConstructorForElementType(typeObject, false); if (arrayCtor) return arrayCtor.BYTES_PER_ELEMENT; @@ -1619,6 +1626,9 @@ JSIL.$MakeStructMarshalFunctionSource = function (typeObject, marshal, isConstru var fields = JSIL.GetFieldList(typeObject); var nativeSize = JSIL.GetNativeSizeOf(typeObject, forPInvoke); var nativeAlignment = JSIL.GetNativeAlignmentOf(typeObject, forPInvoke); + if (nativeSize < 0) + JSIL.RuntimeError("Type '" + typeObject.__FullName__ + "' cannot be marshalled"); + var scratchBuffer = new ArrayBuffer(nativeSize); var scratchRange = JSIL.GetMemoryRangeForBuffer(scratchBuffer); diff --git a/Meta/PackedArray.cs b/Meta/PackedArray.cs index 5f3f0b490..64ed72679 100644 --- a/Meta/PackedArray.cs +++ b/Meta/PackedArray.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using JSIL.Meta; @@ -96,6 +97,14 @@ public NativePackedArray (string dllName, int length) } } + public GCHandle AllocHandle () { + return GCHandle.Alloc(_Array); + } + + public GCHandle AllocHandle (GCHandleType type) { + return GCHandle.Alloc(_Array, type); + } + [JSIsPure] public static implicit operator T[] (NativePackedArray nativeArray) { if (!nativeArray.IsNotDisposed) diff --git a/Tests/EmscriptenTestCases/PassPtrToPinnedArray.cs b/Tests/EmscriptenTestCases/PassPtrToPinnedArray.cs index dc509bde5..497230409 100644 --- a/Tests/EmscriptenTestCases/PassPtrToPinnedArray.cs +++ b/Tests/EmscriptenTestCases/PassPtrToPinnedArray.cs @@ -8,6 +8,6 @@ public static unsafe class Program { public static void Main () { var arr = new ushort[5] { 1, 2, 3, 4, 5 }; GCHandle dataHandle = GCHandle.Alloc(arr, GCHandleType.Pinned); - Console.WriteLine("Test: {0}", FirstElementOfUshortArray((IntPtr) dataHandle.AddrOfPinnedObject().ToInt64())); + Console.WriteLine("Test: {0}", FirstElementOfUshortArray(dataHandle.AddrOfPinnedObject())); } } \ No newline at end of file