Skip to content
This repository has been archived by the owner on Aug 24, 2022. It is now read-only.

Commit

Permalink
Add slow copy-in-copy-out fallback for IntPtr marshalling that uses d…
Browse files Browse the repository at this point in the history
…epraved magic to identify the source data range and make copies
  • Loading branch information
kg committed Jun 6, 2015
1 parent 361ea2c commit ea06a95
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 28 deletions.
94 changes: 68 additions & 26 deletions Libraries/JSIL.PInvoke.js
Expand Up @@ -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);

Expand All @@ -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);
}
);
Expand All @@ -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,
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down
12 changes: 11 additions & 1 deletion Libraries/JSIL.Unsafe.js
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
9 changes: 9 additions & 0 deletions 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;

Expand Down Expand Up @@ -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<T> nativeArray) {
if (!nativeArray.IsNotDisposed)
Expand Down
2 changes: 1 addition & 1 deletion Tests/EmscriptenTestCases/PassPtrToPinnedArray.cs
Expand Up @@ -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()));
}
}

0 comments on commit ea06a95

Please sign in to comment.