mirror of https://github.com/mono/CppSharp.git
c-sharpdotnetmonobindingsbridgecclangcpluspluscppsharpglueinteropparserparsingpinvokeswigsyntax-treevisitorsxamarinxamarin-bindings
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
6.1 KiB
201 lines
6.1 KiB
// |
|
// Mono.Cxxi.CppInstancePtr.cs: Represents a pointer to a native C++ instance |
|
// |
|
// Author: |
|
// Alexander Corrado (alexander.corrado@gmail.com) |
|
// Andreia Gaita (shana@spoiledcat.net) |
|
// |
|
// Copyright (C) 2010-2011 Alexander Corrado |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining |
|
// a copy of this software and associated documentation files (the |
|
// "Software"), to deal in the Software without restriction, including |
|
// without limitation the rights to use, copy, modify, merge, publish, |
|
// distribute, sublicense, and/or sell copies of the Software, and to |
|
// permit persons to whom the Software is furnished to do so, subject to |
|
// the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be |
|
// included in all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
|
|
using System; |
|
using System.Reflection.Emit; |
|
using System.Collections.Generic; |
|
using System.Runtime.InteropServices; |
|
using System.Diagnostics; |
|
|
|
using Mono.Cxxi.Abi; |
|
|
|
namespace Mono.Cxxi { |
|
public struct CppInstancePtr : ICppObject { |
|
|
|
private IntPtr ptr, native_vtptr; |
|
private bool manage_memory; |
|
|
|
private static Dictionary<IntPtr,int> managed_vtptr_to_gchandle_offset = null; |
|
|
|
// Alloc a new C++ instance |
|
internal CppInstancePtr (CppTypeInfo typeInfo, object managedWrapper) |
|
{ |
|
// Under the hood, we're secretly subclassing this C++ class to store a |
|
// handle to the managed wrapper. |
|
int allocSize = typeInfo.GCHandleOffset + IntPtr.Size; |
|
ptr = Marshal.AllocHGlobal (allocSize); |
|
|
|
// NOTE: native_vtptr will be set later after native ctor is called |
|
native_vtptr = IntPtr.Zero; |
|
|
|
// zero memory for sanity |
|
// FIXME: This should be an initblk |
|
byte[] zeroArray = new byte [allocSize]; |
|
Marshal.Copy (zeroArray, 0, ptr, allocSize); |
|
|
|
IntPtr handlePtr = MakeGCHandle (managedWrapper); |
|
Marshal.WriteIntPtr (ptr, typeInfo.GCHandleOffset, handlePtr); |
|
|
|
manage_memory = true; |
|
} |
|
|
|
// Alloc a new C++ instance when there is no managed wrapper. |
|
internal CppInstancePtr (int nativeSize) |
|
{ |
|
ptr = Marshal.AllocHGlobal (nativeSize); |
|
native_vtptr = IntPtr.Zero; |
|
manage_memory = true; |
|
} |
|
|
|
// Gets a casted CppInstancePtr |
|
internal CppInstancePtr (CppInstancePtr instance, int offset) |
|
{ |
|
// FIXME: On NET_4_0 use IntPtr.Add |
|
ptr = new IntPtr (instance.Native.ToInt64 () + offset); |
|
native_vtptr = IntPtr.Zero; |
|
manage_memory = false; |
|
} |
|
|
|
// Get a CppInstancePtr for an existing C++ instance from an IntPtr |
|
public CppInstancePtr (IntPtr native) |
|
{ |
|
if (native == IntPtr.Zero) |
|
throw new ArgumentOutOfRangeException ("native cannot be null pointer"); |
|
|
|
ptr = native; |
|
native_vtptr = IntPtr.Zero; |
|
manage_memory = false; |
|
} |
|
|
|
// Fulfills ICppObject requirement |
|
public CppInstancePtr (CppInstancePtr copy) |
|
{ |
|
this.ptr = copy.ptr; |
|
this.native_vtptr = copy.native_vtptr; |
|
this.manage_memory = copy.manage_memory; |
|
} |
|
|
|
// Provide casts to/from IntPtr: |
|
public static implicit operator CppInstancePtr (IntPtr native) |
|
{ |
|
return new CppInstancePtr (native); |
|
} |
|
|
|
// cast from CppInstancePtr -> IntPtr is explicit because we lose information |
|
public static explicit operator IntPtr (CppInstancePtr ip) |
|
{ |
|
return ip.Native; |
|
} |
|
|
|
public IntPtr Native { |
|
get { |
|
if (ptr == IntPtr.Zero) |
|
throw new ObjectDisposedException ("CppInstancePtr"); |
|
|
|
return ptr; |
|
} |
|
} |
|
|
|
// Internal for now to prevent attempts to read vtptr from non-virtual class |
|
internal IntPtr NativeVTable { |
|
get { |
|
|
|
if (native_vtptr == IntPtr.Zero) { |
|
// For pointers from native code... |
|
// Kludge! CppInstancePtr doesn't know whether this class is virtual or not, but we'll just assume that either |
|
// way it's at least sizeof(void*) and read what would be the vtptr anyway. Supposedly, if it's not virtual, |
|
// the wrappers won't use this field anyway... |
|
native_vtptr = Marshal.ReadIntPtr (ptr); |
|
} |
|
|
|
return native_vtptr; |
|
} |
|
set { |
|
native_vtptr = value; |
|
} |
|
} |
|
|
|
CppInstancePtr ICppObject.Native { |
|
get { return this; } |
|
} |
|
|
|
public bool IsManagedAlloc { |
|
get { return manage_memory; } |
|
} |
|
|
|
internal static void RegisterManagedVTable (VTable vtable) |
|
{ |
|
if (managed_vtptr_to_gchandle_offset == null) |
|
managed_vtptr_to_gchandle_offset = new Dictionary<IntPtr, int> (); |
|
|
|
managed_vtptr_to_gchandle_offset [vtable.Pointer] = vtable.TypeInfo.GCHandleOffset; |
|
} |
|
|
|
internal static IntPtr MakeGCHandle (object managedWrapper) |
|
{ |
|
// TODO: Dispose() should probably be called at some point on this GCHandle. |
|
GCHandle handle = GCHandle.Alloc (managedWrapper, GCHandleType.Normal); |
|
return GCHandle.ToIntPtr (handle); |
|
} |
|
|
|
// This might be made public, but in this form it only works for classes with vtables. |
|
// Returns null if the native ptr passed in does not appear to be a managed instance. |
|
// (i.e. its vtable ptr is not in managed_vtptr_to_gchandle_offset) |
|
internal static T ToManaged<T> (IntPtr native) where T : class |
|
{ |
|
if (managed_vtptr_to_gchandle_offset == null) |
|
return null; |
|
|
|
int gchOffset; |
|
if (!managed_vtptr_to_gchandle_offset.TryGetValue (Marshal.ReadIntPtr (native), out gchOffset)) |
|
return null; |
|
|
|
return ToManaged<T> (native, gchOffset); |
|
} |
|
|
|
// WARNING! This method is not safe. DO NOT call |
|
// if we do not KNOW that this instance is managed. |
|
internal static T ToManaged<T> (IntPtr native, int nativeSize) where T : class |
|
{ |
|
IntPtr handlePtr = Marshal.ReadIntPtr (native, nativeSize); |
|
GCHandle handle = GCHandle.FromIntPtr (handlePtr); |
|
|
|
return handle.Target as T; |
|
} |
|
|
|
// TODO: Free GCHandle? |
|
public void Dispose () |
|
{ |
|
if (manage_memory && ptr != IntPtr.Zero) |
|
Marshal.FreeHGlobal (ptr); |
|
|
|
ptr = IntPtr.Zero; |
|
manage_memory = false; |
|
} |
|
} |
|
}
|
|
|