mirror of https://github.com/mono/CppSharp.git
Browse Source
git-svn-id: https://mono-soc-2010.googlecode.com/svn/trunk/cppinterop@7 a470b8cb-0e6f-1642-1b45-71e107334c4bpull/1/head
22 changed files with 1715 additions and 0 deletions
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00 |
||||
# Visual Studio 2005 |
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.VisualC.Interop", "Mono.VisualC.Interop\Mono.VisualC.Interop.csproj", "{4A864586-93C5-4DC1-8A80-F094A88506D7}" |
||||
EndProject |
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPPPOC", "CPPPOC\CPPPOC.csproj", "{7F309FEA-3A3F-40B1-BBF9-A09796253202}" |
||||
EndProject |
||||
Project("{2857B73E-F847-4B02-9238-064979017E93}") = "CPPTest", "CPPTest\CPPTest.cproj", "{B01E6282-144E-481A-8E1F-95F708DFBC2D}" |
||||
EndProject |
||||
Global |
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
Linux|Any CPU = Linux|Any CPU |
||||
Mac|Any CPU = Mac|Any CPU |
||||
EndGlobalSection |
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
{4A864586-93C5-4DC1-8A80-F094A88506D7}.Linux|Any CPU.ActiveCfg = Linux|Any CPU |
||||
{4A864586-93C5-4DC1-8A80-F094A88506D7}.Linux|Any CPU.Build.0 = Linux|Any CPU |
||||
{4A864586-93C5-4DC1-8A80-F094A88506D7}.Mac|Any CPU.ActiveCfg = Mac|Any CPU |
||||
{4A864586-93C5-4DC1-8A80-F094A88506D7}.Mac|Any CPU.Build.0 = Mac|Any CPU |
||||
{7F309FEA-3A3F-40B1-BBF9-A09796253202}.Linux|Any CPU.ActiveCfg = Linux|Any CPU |
||||
{7F309FEA-3A3F-40B1-BBF9-A09796253202}.Linux|Any CPU.Build.0 = Linux|Any CPU |
||||
{7F309FEA-3A3F-40B1-BBF9-A09796253202}.Mac|Any CPU.ActiveCfg = Mac|Any CPU |
||||
{7F309FEA-3A3F-40B1-BBF9-A09796253202}.Mac|Any CPU.Build.0 = Mac|Any CPU |
||||
{B01E6282-144E-481A-8E1F-95F708DFBC2D}.Linux|Any CPU.ActiveCfg = Linux|Any CPU |
||||
{B01E6282-144E-481A-8E1F-95F708DFBC2D}.Linux|Any CPU.Build.0 = Linux|Any CPU |
||||
{B01E6282-144E-481A-8E1F-95F708DFBC2D}.Mac|Any CPU.ActiveCfg = Mac|Any CPU |
||||
{B01E6282-144E-481A-8E1F-95F708DFBC2D}.Mac|Any CPU.Build.0 = Mac|Any CPU |
||||
EndGlobalSection |
||||
GlobalSection(MonoDevelopProperties) = preSolution |
||||
StartupItem = CPPPOC\CPPPOC.csproj |
||||
Policies = $0 |
||||
$0.TextStylePolicy = $1 |
||||
$1.TabWidth = 8 |
||||
$1.NoTabsAfterNonTabs = True |
||||
$1.RemoveTrailingWhitespace = True |
||||
$1.inheritsSet = VisualStudio |
||||
$1.inheritsScope = text/plain |
||||
$1.scope = text/x-csharp |
||||
$0.CSharpFormattingPolicy = $2 |
||||
$2.inheritsSet = Mono |
||||
$2.inheritsScope = text/x-csharp |
||||
$2.scope = text/x-csharp |
||||
name = CPPInterop |
||||
EndGlobalSection |
||||
EndGlobal |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
<ProductVersion>8.0.50727</ProductVersion> |
||||
<SchemaVersion>2.0</SchemaVersion> |
||||
<ProjectGuid>{7F309FEA-3A3F-40B1-BBF9-A09796253202}</ProjectGuid> |
||||
<OutputType>Exe</OutputType> |
||||
<AssemblyName>CPPPOC</AssemblyName> |
||||
<RootNamespace>CPPPOC</RootNamespace> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
<DebugSymbols>true</DebugSymbols> |
||||
<DebugType>full</DebugType> |
||||
<Optimize>false</Optimize> |
||||
<OutputPath>bin\Debug</OutputPath> |
||||
<DefineConstants>DEBUG</DefineConstants> |
||||
<ErrorReport>prompt</ErrorReport> |
||||
<WarningLevel>4</WarningLevel> |
||||
<Externalconsole>true</Externalconsole> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
<DebugType>none</DebugType> |
||||
<Optimize>false</Optimize> |
||||
<OutputPath>bin\Release</OutputPath> |
||||
<ErrorReport>prompt</ErrorReport> |
||||
<WarningLevel>4</WarningLevel> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Linux|AnyCPU' "> |
||||
<DebugType>none</DebugType> |
||||
<Optimize>false</Optimize> |
||||
<OutputPath>bin\Release</OutputPath> |
||||
<ErrorReport>prompt</ErrorReport> |
||||
<WarningLevel>4</WarningLevel> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Mac|AnyCPU' "> |
||||
<DebugType>none</DebugType> |
||||
<Optimize>false</Optimize> |
||||
<OutputPath>bin\Mac</OutputPath> |
||||
<WarningLevel>4</WarningLevel> |
||||
</PropertyGroup> |
||||
<ItemGroup> |
||||
<Reference Include="System" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ProjectReference Include="..\Mono.VisualC.Interop\Mono.VisualC.Interop.csproj"> |
||||
<Project>{4A864586-93C5-4DC1-8A80-F094A88506D7}</Project> |
||||
<Name>Mono.VisualC.Interop</Name> |
||||
</ProjectReference> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Compile Include="Main.cs" /> |
||||
<Compile Include="AssemblyInfo.cs" /> |
||||
<Compile Include="CSimpleClass.cs" /> |
||||
</ItemGroup> |
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |
||||
<ProjectExtensions> |
||||
<MonoDevelop> |
||||
<Properties InternalTargetFrameworkVersion="3.5" /> |
||||
</MonoDevelop> |
||||
</ProjectExtensions> |
||||
</Project> |
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* CPPTest.cpp |
||||
* CPPTest |
||||
* |
||||
* Created by Alex Corrado on 3/14/09. |
||||
* Copyright 2009 __MyCompanyName__. All rights reserved. |
||||
* |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include "CPPTest.h" |
||||
|
||||
CSimpleClass::CSimpleClass(int value) : value(value) { |
||||
printf("CSimpleClass(%d)\n", value); |
||||
this->value = value; |
||||
} |
||||
|
||||
CSimpleSubClass::CSimpleSubClass(int value) : CSimpleClass(value) { |
||||
printf("CSimpleSubClass(%d)\n", value); |
||||
} |
||||
|
||||
CSimpleClass::~CSimpleClass() { |
||||
printf("~CSimpleClass\n"); |
||||
} |
||||
|
||||
void CSimpleClass::M0() { |
||||
printf("C++/CSimpleClass::M0()\n"); |
||||
V0(value, value + 1); |
||||
V1(value); |
||||
V2(); |
||||
} |
||||
|
||||
void CSimpleClass::V0(int x, int y) { |
||||
printf("C++/CSimpleClass::V0(%d, %d)\n", x, y); |
||||
} |
||||
|
||||
void CSimpleSubClass::V0(int x, int y) { |
||||
printf("C++/CSimpleSubClass::V0(%d, %d)\n", x, y); |
||||
} |
||||
|
||||
void CSimpleClass::M1(int x) { |
||||
printf("C++/CSimpleClass::M1(%d)\n", x); |
||||
V0(x, value); |
||||
V1(x); |
||||
V2(); |
||||
} |
||||
|
||||
void CSimpleClass::V1(int x) { |
||||
printf("C++/CSimpleClass::V1(%d)\n", x); |
||||
} |
||||
|
||||
void CSimpleSubClass::V1(int x) { |
||||
printf("C++/CSimpleSubClass::V1(%d)\n", x); |
||||
} |
||||
|
||||
void CSimpleClass::M2(int x, int y) { |
||||
|
||||
} |
||||
|
||||
void CSimpleClass::V2() { |
||||
printf("C++/CSimpleClass::V2() - value: %d\n", value); |
||||
} |
||||
|
||||
void CSimpleSubClass::V2() { |
||||
printf("C++/CSimpleSubClass::V2() - value: %d\n", value); |
||||
} |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
<ProductVersion>8.0.50727</ProductVersion> |
||||
<SchemaVersion>2.0</SchemaVersion> |
||||
<ProjectGuid>{B01E6282-144E-481A-8E1F-95F708DFBC2D}</ProjectGuid> |
||||
<Compiler> |
||||
<Compiler ctype="GccCompiler" /> |
||||
</Compiler> |
||||
<Language>C</Language> |
||||
<Target>Bin</Target> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
<DebugSymbols>true</DebugSymbols> |
||||
<OutputPath>bin\Debug</OutputPath> |
||||
<DefineSymbols>DEBUG MONODEVELOP</DefineSymbols> |
||||
<SourceDirectory>.</SourceDirectory> |
||||
<OutputName>CPPTest</OutputName> |
||||
<CompileTarget>SharedLibrary</CompileTarget> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
<OutputPath>bin\Release</OutputPath> |
||||
<DefineSymbols>MONODEVELOP</DefineSymbols> |
||||
<SourceDirectory>.</SourceDirectory> |
||||
<OptimizationLevel>3</OptimizationLevel> |
||||
<OutputName>CPPTest</OutputName> |
||||
<CompileTarget>Bin</CompileTarget> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Mac|AnyCPU' "> |
||||
<DebugSymbols>true</DebugSymbols> |
||||
<OutputPath>build\Debug</OutputPath> |
||||
<ExtraLinkerArguments>-dynamic</ExtraLinkerArguments> |
||||
<DefineSymbols>DEBUG MONODEVELOP</DefineSymbols> |
||||
<SourceDirectory>.</SourceDirectory> |
||||
<CustomCommands> |
||||
<CustomCommands> |
||||
<Command type="Build" command="xcodebuild -configuration Debug" workingdir="${ProjectDir}" /> |
||||
<Command type="AfterBuild" command="cp ./build/Debug/libCPPTest.dylib ../CPPPOC/bin/Mac" workingdir="${ProjectDir}" /> |
||||
</CustomCommands> |
||||
</CustomCommands> |
||||
<OutputName>libCPPTest.dylib</OutputName> |
||||
<CompileTarget>Bin</CompileTarget> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Linux|AnyCPU' "> |
||||
<OutputPath>build\Debug</OutputPath> |
||||
<DefineSymbols>DEBUG MONODEVELOP</DefineSymbols> |
||||
<CompileTarget>SharedLibrary</CompileTarget> |
||||
<OutputName>libCPPTest.so</OutputName> |
||||
<CustomCommands> |
||||
<CustomCommands> |
||||
<Command type="AfterBuild" command="cp ./build/Debug/libCPPTest.so ../CPPPOC/bin/Linux" workingdir="${ProjectDir}" /> |
||||
</CustomCommands> |
||||
</CustomCommands> |
||||
</PropertyGroup> |
||||
<ItemGroup> |
||||
<None Include="CPPTest.h" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Compile Include="CPPTest.cpp" /> |
||||
</ItemGroup> |
||||
</Project> |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* CPPTest.h |
||||
* CPPTest |
||||
* |
||||
* Created by Alex Corrado on 3/14/09. |
||||
* Copyright 2009 __MyCompanyName__. All rights reserved. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef CPPTest_ |
||||
#define CPPTest_ |
||||
|
||||
/* The classes below are exported */ |
||||
#pragma GCC visibility push(default) |
||||
|
||||
class CSimpleClass { |
||||
public: |
||||
int value; |
||||
CSimpleClass(int value); |
||||
~CSimpleClass(); |
||||
void M0(); |
||||
virtual void V0(int x, int y); |
||||
void M1(int x); |
||||
virtual void V1(int x); |
||||
void M2(int x, int y); |
||||
virtual void V2(); |
||||
}; |
||||
|
||||
class CSimpleSubClass : CSimpleClass { |
||||
public: |
||||
CSimpleSubClass(int value); |
||||
virtual void V0(int x, int y); |
||||
virtual void V1(int x); |
||||
virtual void V2(); |
||||
}; |
||||
|
||||
extern "C" { |
||||
CSimpleSubClass* CreateCSimpleSubClass(int value) { |
||||
return new CSimpleSubClass(value); |
||||
} |
||||
void DestroyCSimpleSubClass(CSimpleSubClass* obj) { |
||||
delete obj; |
||||
} |
||||
} |
||||
|
||||
#pragma GCC visibility pop |
||||
#endif |
@ -0,0 +1,487 @@
@@ -0,0 +1,487 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.ABI.CppAbi.cs: Represents an abstract C++ ABI
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Reflection; |
||||
using System.Reflection.Emit; |
||||
|
||||
using System.Collections.Generic; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
namespace Mono.VisualC.Interop.ABI { |
||||
|
||||
//TODO: Exception handling, operator overloading etc.
|
||||
//TODO: Allow interface to override default calling convention
|
||||
public abstract class CppAbi { |
||||
|
||||
protected ModuleBuilder implModule; |
||||
protected TypeBuilder implType; |
||||
|
||||
protected Type interfaceType, layoutType, wrapperType; |
||||
protected string library, className; |
||||
|
||||
protected FieldBuilder vtableField; |
||||
protected ILGenerator ctorIL; |
||||
|
||||
public virtual Iface ImplementClass<Iface, NLayout> (ModuleBuilder implModule, Type wrapperType, string lib, string className) |
||||
where NLayout : struct |
||||
//where Iface : ICppNativeInterface or ICppNativeInterfaceManaged
|
||||
{ |
||||
this.implModule = implModule; |
||||
this.library = lib; |
||||
this.className = className; |
||||
this.interfaceType = typeof (Iface); |
||||
this.layoutType = typeof (NLayout); |
||||
this.wrapperType = wrapperType; |
||||
|
||||
MethodInfo[] methods = interfaceType.GetMethods (); |
||||
|
||||
// sort methods into declaration order
|
||||
// TODO: This is kinda kludgy isn't it?
|
||||
Array.Sort (methods, (x, y) => x.MetadataToken - y.MetadataToken); |
||||
|
||||
int vtableIndex = 0; |
||||
List<Delegate> vtableDelegates = new List<Delegate>(); |
||||
|
||||
// Implement all methods
|
||||
for (int i = 0; i < methods.Length; i++) { |
||||
// Skip over special methods like property accessors -- properties will be handled later
|
||||
if (methods [i].IsSpecialName) |
||||
continue; |
||||
|
||||
DefineMethod (methods [i], vtableIndex); |
||||
|
||||
if (!Modifiers.IsVirtual (methods [i])) |
||||
continue; |
||||
|
||||
MethodInfo overrideTarget = FindManagedOverrideTarget (methods [i], VTable.BindOverridesOnly); |
||||
if (overrideTarget != null) |
||||
vtableDelegates.Insert(vtableIndex, GetManagedOverrideTrampoline (methods [i], overrideTarget)); |
||||
|
||||
vtableIndex++; |
||||
} |
||||
|
||||
DefineImplType (); |
||||
|
||||
// Implement all properties
|
||||
foreach (var property in interfaceType.GetProperties ()) |
||||
DefineFieldProperty (property); |
||||
|
||||
|
||||
ctorIL.Emit (OpCodes.Ret); |
||||
return (Iface)Activator.CreateInstance (implType.CreateType (), vtable); |
||||
} |
||||
|
||||
// These methods might be more commonly overridden for a given C++ ABI:
|
||||
|
||||
public virtual MethodType GetMethodType (MethodInfo imethod) |
||||
{ |
||||
if (imethod.Name.Equals (className)) |
||||
return MethodType.NativeCtor; |
||||
else if (imethod.Name.Equals ("Alloc")) |
||||
return MethodType.ManagedAlloc; |
||||
else if (imethod.Name.Equals ("Destruct")) |
||||
return MethodType.NativeDtor; |
||||
|
||||
return MethodType.Native; |
||||
} |
||||
|
||||
public virtual int FieldOffsetPadding { |
||||
get { return Marshal.SizeOf (typeof (IntPtr)); } |
||||
} |
||||
|
||||
protected virtual int NativeSize { |
||||
get { |
||||
// By default: native size = C++ class size + field offset padding (usually just vtable pointer)
|
||||
// TODO: Only include vtable ptr if there are virtual functions? Here I guess it doesn't really matter,
|
||||
// we're just allocing extra memory.
|
||||
return Marshal.SizeOf (layoutType) + FieldOffsetPadding; |
||||
} |
||||
} |
||||
|
||||
protected virtual VTable MakeVTable (Delegate[] overrides) |
||||
{ |
||||
return new VTableManaged (overrides); |
||||
} |
||||
|
||||
// The members below must be implemented for a given C++ ABI:
|
||||
|
||||
public abstract string GetMangledMethodName (MethodInfo methodInfo); |
||||
public abstract CallingConvention DefaultCallingConvention { get; } |
||||
|
||||
protected virtual void DefineImplType () |
||||
{ |
||||
|
||||
implType = implModule.DefineType (implModule.Name + "_" + interfaceType.Name + "_Impl", |
||||
TypeAttributes.Class | TypeAttributes.Sealed); |
||||
implType.AddInterfaceImplementation (interfaceType); |
||||
|
||||
vtableField = implType.DefineField ("_vtable", typeof (VTable), FieldAttributes.InitOnly | FieldAttributes.Private); |
||||
ConstructorBuilder ctor = implType.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, |
||||
new Type[] { typeof (VTable) }); |
||||
|
||||
ctorIL = ctor.GetILGenerator (); |
||||
|
||||
// this._vtable = (VTable passed to constructor)
|
||||
ctorIL.Emit (OpCodes.Ldarg_0); |
||||
ctorIL.Emit (OpCodes.Ldarg_1); |
||||
ctorIL.Emit (OpCodes.Stfld, vtableField); |
||||
} |
||||
|
||||
protected virtual MethodBuilder DefineMethod (MethodInfo interfaceMethod, int index) |
||||
{ |
||||
// 0. Introspect method
|
||||
MethodType methodType = GetMethodType (interfaceMethod); |
||||
Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod, false); |
||||
|
||||
// 1. Generate managed trampoline to call native method
|
||||
MethodBuilder trampoline = GetMethodBuilder (interfaceMethod); |
||||
ILGenerator il = trampoline.GetILGenerator (); |
||||
|
||||
if (methodType == MethodType.ManagedAlloc) { |
||||
EmitManagedAlloc (il); |
||||
il.Emit (OpCodes.Ret); |
||||
return trampoline; |
||||
} |
||||
|
||||
// 2. Load the native C++ instance pointer
|
||||
LocalBuilder cppInstancePtr, nativePtr; |
||||
EmitLoadInstancePtr (il, parameterTypes[0], out cppInstancePtr, out nativePtr); |
||||
|
||||
// 3. Make sure our native pointer is a valid reference. If not, throw ObjectDisposedException
|
||||
EmitCheckDisposed (il, nativePtr, methodType); |
||||
|
||||
MethodInfo nativeMethod; |
||||
if (!Modifiers.IsVirtual (interfaceMethod)) { |
||||
nativeMethod = null; |
||||
else |
||||
nativeMethod = GetPInvokeForMethod (interfaceMethod); |
||||
|
||||
switch (methodType) { |
||||
case MethodType.NativeCtor: |
||||
EmitConstruct (il, nativeMethod, parameterTypes.Length, nativePtr); |
||||
break; |
||||
|
||||
case MethodType.NativeDtor: |
||||
EmitDestruct (il, nativeMethod, parameterTypes.Length, nativePtr); |
||||
break; |
||||
|
||||
default: // regular native method
|
||||
EmitCallNative (il, nativeMethod, parameterTypes.Length, nativePtr); |
||||
break; |
||||
} |
||||
} else |
||||
|
||||
|
||||
il.Emit (OpCodes.Ret); |
||||
return trampoline; |
||||
} |
||||
|
||||
protected virtual PropertyBuilder DefineFieldProperty (PropertyInfo property) |
||||
{ |
||||
if (property.CanWrite) |
||||
throw new InvalidProgramException ("Properties in C++ interface must be read-only."); |
||||
|
||||
MethodInfo imethod = property.GetGetMethod (); |
||||
string methodName = imethod.Name; |
||||
string propName = property.Name; |
||||
Type retType = imethod.ReturnType; |
||||
|
||||
if ((!retType.IsGenericType) || (!retType.GetGenericTypeDefinition ().Equals (typeof (CppField<>)))) |
||||
throw new InvalidProgramException ("Properties in C++ interface can only be of type CppField."); |
||||
|
||||
PropertyBuilder fieldProp = implType.DefineProperty (propName, PropertyAttributes.None, retType, Type.EmptyTypes); |
||||
FieldBuilder fieldData = implType.DefineField ("__" + propName + "_Data", retType, FieldAttributes.InitOnly | FieldAttributes.Private); |
||||
|
||||
// init our field data with a new instance of CppField
|
||||
// first, get field offset
|
||||
ctorIL.Emit (OpCodes.Ldarg_0); |
||||
|
||||
/* TODO: Code prolly should not emit hardcoded offsets n such, in case we end up saving these assemblies in the future. |
||||
* Something more like this perhaps? (need to figure out how to get field offset padding into this) |
||||
* ctorIL.Emit(OpCodes.Ldtoken, nativeLayout); |
||||
* ctorIL.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); |
||||
* ctorIL.Emit(OpCodes.Ldstr, propName); |
||||
* ctorIL.Emit(OpCodes.Call, typeof(Marshal).GetMethod("OffsetOf")); |
||||
*/ |
||||
int fieldOffset = ((int)Marshal.OffsetOf (layoutType, propName)) + FieldOffsetPadding; |
||||
ctorIL.Emit (OpCodes.Ldc_I4, fieldOffset); |
||||
ctorIL.Emit (OpCodes.Newobj, retType.GetConstructor (new Type[] { typeof(int) })); |
||||
|
||||
ctorIL.Emit (OpCodes.Stfld, fieldData); |
||||
|
||||
MethodAttributes methodAttr = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.HideBySig; |
||||
MethodBuilder fieldGetter = implType.DefineMethod (methodName, methodAttr, retType, Type.EmptyTypes); |
||||
ILGenerator il = fieldGetter.GetILGenerator (); |
||||
|
||||
il.Emit (OpCodes.Ldarg_0); |
||||
il.Emit (OpCodes.Ldfld, fieldData); |
||||
il.Emit (OpCodes.Ret); |
||||
|
||||
fieldProp.SetGetMethod (fieldGetter); |
||||
|
||||
return fieldProp; |
||||
} |
||||
|
||||
/** |
||||
* Implements the managed trampoline that will be invoked from the vtable by native C++ code when overriding |
||||
* the specified C++ virtual method with the specified managed one. |
||||
*/ |
||||
protected virtual Delegate GetManagedOverrideTrampoline (MethodInfo interfaceMethod, MethodInfo targetMethod) |
||||
{ |
||||
Type delegateType = Util.GetDelegateTypeForMethodInfo (implModule, interfaceMethod); |
||||
Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod, true); |
||||
|
||||
// TODO: According to http://msdn.microsoft.com/en-us/library/w16z8yc4.aspx
|
||||
// The dynamic method created with this constructor has access to public and internal members of all the types contained in module m.
|
||||
// This does not appear to hold true, so we also disable JIT visibility checks.
|
||||
DynamicMethod trampolineIn = new DynamicMethod (wrapperType.Name + "_" + interfaceMethod.Name + "_FromNative", interfaceMethod.ReturnType, |
||||
parameterTypes, typeof (CppInstancePtr).Module, true); |
||||
|
||||
ILGenerator il = trampolineIn.GetILGenerator (); |
||||
|
||||
// for static methods:
|
||||
OpCode callInstruction = OpCodes.Call; |
||||
int argLoadStart = 0; |
||||
|
||||
// for instance methods, we need an instance to call them on!
|
||||
if (!targetMethod.IsStatic) { |
||||
callInstruction = OpCodes.Callvirt; |
||||
argLoadStart = 1; |
||||
|
||||
il.Emit (OpCodes.Ldarg_0); |
||||
il.Emit (OpCodes.Ldc_I4, NativeSize); |
||||
|
||||
MethodInfo getManagedObj = typeof (CppInstancePtr).GetMethod ("GetManaged", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod (wrapperType); |
||||
il.Emit (OpCodes.Call, getManagedObj); |
||||
} |
||||
|
||||
for (int i = argLoadStart; i < parameterTypes.Length; i++) { |
||||
il.Emit (OpCodes.Ldarg, i); |
||||
} |
||||
il.Emit (OpCodes.Tailcall); |
||||
il.Emit (callInstruction, targetMethod); |
||||
il.Emit (OpCodes.Ret); |
||||
|
||||
return trampolineIn.CreateDelegate (delegateType); |
||||
} |
||||
|
||||
protected virtual MethodInfo FindManagedOverrideTarget (MethodInfo interfaceMethod, MemberFilter filter) |
||||
{ |
||||
MemberInfo[] possibleMembers = wrapperType.FindMembers (MemberTypes.Method, BindingFlags.Public | BindingFlags.NonPublic | |
||||
BindingFlags.Instance | BindingFlags.Static, filter, interfaceMethod); |
||||
|
||||
if (possibleMembers.Length > 1) |
||||
throw new InvalidProgramException ("More than one possible override found when binding virtual method: " + interfaceMethod.Name); |
||||
else if (possibleMembers.Length == 0) |
||||
return null; |
||||
|
||||
return (MethodInfo)possibleMembers [0]; |
||||
} |
||||
/** |
||||
* Defines a new MethodBuilder with the same signature as the passed MethodInfo |
||||
*/ |
||||
protected virtual MethodBuilder GetMethodBuilder (MethodInfo interfaceMethod) |
||||
{ |
||||
|
||||
Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod, false); |
||||
MethodBuilder methodBuilder = implType.DefineMethod (interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, |
||||
interfaceMethod.ReturnType, parameterTypes); |
||||
|
||||
return methodBuilder; |
||||
} |
||||
|
||||
/** |
||||
* Defines a new MethodBuilder that calls the specified C++ (non-virtual) method using its mangled name |
||||
*/ |
||||
protected virtual MethodBuilder GetPInvokeForMethod (MethodInfo signature) |
||||
{ |
||||
string entryPoint = GetMangledMethodName (signature); |
||||
if (entryPoint == null) |
||||
throw new NotSupportedException ("Could not mangle method name."); |
||||
|
||||
Type[] parameterTypes = Util.GetMethodParameterTypes (signature, true); |
||||
|
||||
MethodBuilder builder = implType.DefinePInvokeMethod ("__$" + signature.Name + "_Impl", library, entryPoint, |
||||
MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.PinvokeImpl, |
||||
CallingConventions.Any, signature.ReturnType, parameterTypes, |
||||
DefaultCallingConvention, CharSet.Ansi); |
||||
|
||||
builder.SetImplementationFlags (builder.GetMethodImplementationFlags () | MethodImplAttributes.PreserveSig); |
||||
return builder; |
||||
} |
||||
|
||||
/** |
||||
* Emits IL to allocate the memory for a new instance of the C++ class. |
||||
* To complete method, emit OpCodes.Ret. |
||||
*/ |
||||
protected virtual void EmitManagedAlloc (ILGenerator il) |
||||
{ |
||||
|
||||
//TODO: Do not hard-emit native size in case assembly is saved?
|
||||
il.Emit (OpCodes.Ldc_I4, NativeSize); |
||||
|
||||
if (wrapperType != null) { |
||||
// load managed object
|
||||
il.Emit (OpCodes.Ldarg_1); |
||||
|
||||
//new CppInstancePtr (Abi.GetNativeSize (typeof (NativeLayout)), managedWrapper);
|
||||
il.Emit (OpCodes.Newobj, typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null, |
||||
new Type[] { typeof (int), typeof (object) }, null)); |
||||
} else |
||||
il.Emit (OpCodes.Newobj, typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null, |
||||
new Type[] { typeof (int) }, null)); |
||||
} |
||||
|
||||
protected virtual void EmitConstruct (ILGenerator il, MethodInfo nativeMethod, int parameterCount, LocalBuilder nativePtr) |
||||
{ |
||||
EmitCallNative (il, nativeMethod, parameterCount, nativePtr); |
||||
EmitInitVTable (il, nativePtr); |
||||
} |
||||
|
||||
protected virtual void EmitDestruct (ILGenerator il, MethodInfo nativeMethod, int parameterCount, LocalBuilder nativePtr) |
||||
{ |
||||
EmitResetVTable (il, nativePtr); |
||||
EmitCallNative (il, nativeMethod, parameterCount, nativePtr); |
||||
} |
||||
|
||||
/** |
||||
* Emits IL to call the native method. nativeMethod should be either a method obtained by |
||||
* GetPInvokeForMethod or the MethodInfo of a vtable method. |
||||
* To complete method, emit OpCodes.Ret. |
||||
*/ |
||||
protected virtual void EmitCallNative (ILGenerator il, MethodInfo nativeMethod, int parameterCount, LocalBuilder nativePtr) |
||||
{ |
||||
il.Emit (OpCodes.Ldloc_S, nativePtr); |
||||
for (int i = 2; i <= parameterCount; i++) |
||||
il.Emit (OpCodes.Ldarg, i); |
||||
|
||||
il.Emit (OpCodes.Call, nativeMethod); |
||||
} |
||||
|
||||
/** |
||||
* Emits IL to set the vtable pointer of the instance (if class has a vtable). |
||||
* This should usually happen in the managed wrapper of the C++ instance constructor. |
||||
*/ |
||||
protected virtual void EmitInitVTable (ILGenerator il, LocalBuilder nativePtr) |
||||
{ |
||||
// this._vtable.InitInstance (nativePtr);
|
||||
il.Emit (OpCodes.Ldarg_0); |
||||
il.Emit (OpCodes.Ldfld, vtableField); |
||||
il.Emit (OpCodes.Ldloc_S, nativePtr); |
||||
EmitVTableOp (il, typeof (VTable).GetMethod ("InitInstance"), 2, false); |
||||
} |
||||
|
||||
protected virtual void EmitResetVTable (ILGenerator il, LocalBuilder nativePtr) |
||||
{ |
||||
// this._vtable.ResetInstance (nativePtr);
|
||||
il.Emit (OpCodes.Ldarg_0); |
||||
il.Emit (OpCodes.Ldfld, vtableField); |
||||
il.Emit (OpCodes.Ldloc_S, nativePtr); |
||||
EmitVTableOp (il, typeof(VTable).GetMethod ("ResetInstance"), 2, false); |
||||
} |
||||
|
||||
/** |
||||
* A utility function to emit the IL for a vtable-dependant operation. |
||||
* In other words, classes with no virtual methods will not have vtables, |
||||
* so this method emits code to check for that and either throw an exception |
||||
* or do nothing if no vtable exists. To use, push the arguments to the method you |
||||
* want to call and pass the stackHeight for the call. If no vtable exists, this method |
||||
* will emit code to pop the arguments off the stack. |
||||
*/ |
||||
protected virtual void EmitVTableOp(ILGenerator il, MethodInfo method, int stackHeight, bool throwOnNoVTable) |
||||
{ |
||||
// prepare a jump; do not call vtable method if no vtable
|
||||
Label noVirt = il.DefineLabel (); |
||||
Label dontPushOrThrow = il.DefineLabel (); |
||||
|
||||
il.Emit (OpCodes.Ldarg_0); // load this
|
||||
il.Emit (OpCodes.Ldfld, vtableField); // load this._vtable
|
||||
il.Emit (OpCodes.Brfalse_S, noVirt); // if (vtableInfo == null) goto noVirt
|
||||
|
||||
il.Emit (OpCodes.Callvirt, method); // call method
|
||||
il.Emit (OpCodes.Br_S, dontPushOrThrow); // goto dontPushOrThrow
|
||||
|
||||
il.MarkLabel (noVirt); |
||||
// noVirt:
|
||||
// since there is no vtable, we did not make the method call.
|
||||
// pop arguments
|
||||
for (int i = 0; i < stackHeight; i++) |
||||
il.Emit (OpCodes.Pop); |
||||
|
||||
// if the method was supposed to return a value, we must
|
||||
// still push something onto the stack
|
||||
// TODO: This is a kludge. What about value types?
|
||||
if (!method.ReturnType.Equals (typeof (void))) |
||||
il.Emit (OpCodes.Ldnull); |
||||
|
||||
if (throwOnNoVTable) { |
||||
il.Emit (OpCodes.Ldstr, "Native class has no VTable."); |
||||
il.Emit (OpCodes.Newobj, typeof (InvalidOperationException).GetConstructor(new Type[] {typeof (string)})); |
||||
il.Emit (OpCodes.Throw); |
||||
} |
||||
|
||||
il.MarkLabel (dontPushOrThrow); |
||||
} |
||||
|
||||
protected virtual void EmitLoadInstancePtr (ILGenerator il, Type firstParamType, out LocalBuilder cppip, out LocalBuilder native) |
||||
{ |
||||
cppip = null; |
||||
native = il.DeclareLocal (typeof (IntPtr)); |
||||
|
||||
il.Emit (OpCodes.Ldarg_1); |
||||
if (firstParamType.Equals (typeof (CppInstancePtr))) { |
||||
cppip = il.DeclareLocal (typeof (CppInstancePtr)); |
||||
il.Emit (OpCodes.Stloc_S, cppip); |
||||
il.Emit (OpCodes.Ldloca_S, cppip); |
||||
il.Emit (OpCodes.Call, typeof (CppInstancePtr).GetProperty ("Native").GetGetMethod ()); |
||||
il.Emit (OpCodes.Stloc_S, native); |
||||
} else if (firstParamType.Equals (typeof (IntPtr))) |
||||
il.Emit (OpCodes.Stloc_S, native); |
||||
else |
||||
throw new ArgumentException ("First argument to C++ method must be IntPtr or CppInstancePtr."); |
||||
} |
||||
|
||||
protected virtual void EmitCheckManagedAlloc (ILGenerator il, LocalBuilder cppip) |
||||
{ |
||||
// make sure we were allocated by managed code
|
||||
// if not, return
|
||||
Label managedAlloc = il.DefineLabel (); |
||||
il.Emit (OpCodes.Ldloca_S, cppip); |
||||
il.Emit (OpCodes.Call, typeof (CppInstancePtr).GetProperty ("IsManagedAlloc").GetGetMethod ()); |
||||
il.Emit (OpCodes.Brtrue_S, managedAlloc); |
||||
il.Emit (OpCodes.Ret); |
||||
il.MarkLabel (managedAlloc); |
||||
} |
||||
|
||||
/** |
||||
* throw ObjectDisposedException if we have a null pointer for native |
||||
* however, allow destructor to be called even if we're disposed (just return immediately) |
||||
*/ |
||||
protected virtual void EmitCheckDisposed (ILGenerator il, LocalBuilder native, MethodType methodType) |
||||
{ |
||||
Label validRef = il.DefineLabel (); |
||||
il.Emit (OpCodes.Ldloc_S, native); |
||||
il.Emit (OpCodes.Brtrue_S, validRef); |
||||
if (methodType == MethodType.NativeDtor) { |
||||
il.Emit (OpCodes.Ret); |
||||
il.MarkLabel (validRef); |
||||
} else { |
||||
il.Emit (OpCodes.Ldstr, String.Empty); |
||||
il.Emit (OpCodes.Newobj, typeof (ObjectDisposedException).GetConstructor (new Type[] { typeof(string) })); |
||||
il.Emit (OpCodes.Throw); |
||||
il.MarkLabel (validRef); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
|
||||
using System; |
||||
using System.Text; |
||||
using System.Reflection; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
namespace Mono.VisualC.Interop.ABI { |
||||
|
||||
public class Itanium : CppAbi { |
||||
|
||||
public Itanium() {} |
||||
|
||||
public override CallingConvention DefaultCallingConvention { |
||||
get { |
||||
return CallingConvention.Cdecl; |
||||
} |
||||
} |
||||
|
||||
public override string GetMangledMethodName(MethodInfo methodInfo) { |
||||
string methodName = methodInfo.Name; |
||||
MethodType methodType = GetMethodType(methodInfo); |
||||
ParameterInfo[] parameters = methodInfo.GetParameters(); |
||||
|
||||
StringBuilder nm = new StringBuilder("_ZN", 30); |
||||
nm.Append(className.Length).Append(className); |
||||
|
||||
switch (methodType) { |
||||
|
||||
case MethodType.NativeCtor: |
||||
nm.Append("C1"); |
||||
break; |
||||
|
||||
case MethodType.NativeDtor: |
||||
nm.Append("D1"); |
||||
break; |
||||
|
||||
default: |
||||
nm.Append(methodName.Length).Append(methodName); |
||||
break; |
||||
|
||||
} |
||||
|
||||
nm.Append("E"); |
||||
|
||||
if (parameters.Length == 1) { //only the C++ "this" object
|
||||
nm.Append("v"); |
||||
} else for (int i = 1; i < parameters.Length; i++) { |
||||
nm.Append(GetTypeCode(parameters[i].ParameterType)); |
||||
} |
||||
|
||||
return nm.ToString(); |
||||
} |
||||
|
||||
protected virtual string GetTypeCode(Type t) { |
||||
if (t.Equals(typeof(int))) return "i"; |
||||
throw new NotSupportedException("Unsupported parameter type: " + t.ToString()); |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
using System; |
||||
namespace Mono.VisualC.Interop { |
||||
public enum MethodType { |
||||
Native, |
||||
NativeCtor, |
||||
NativeDtor, |
||||
ManagedAlloc |
||||
} |
||||
} |
@ -0,0 +1,141 @@
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.ABI.VTable.cs: abstract vtable
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using System.Reflection; |
||||
using System.Reflection.Emit; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
namespace Mono.VisualC.Interop.ABI { |
||||
public abstract class VTable : IDisposable { |
||||
protected IntPtr basePtr, vtPtr; |
||||
|
||||
public virtual int EntryCount { get; protected set; } |
||||
|
||||
public abstract int EntrySize { get; } |
||||
public abstract void EmitVirtualCall (ILGenerator il, IntPtr native, int index); |
||||
|
||||
// Creates a new VTable
|
||||
public VTable (Delegate[] overrides) |
||||
{ |
||||
EntryCount = overrides.Length; |
||||
|
||||
int vtableSize = EntryCount * EntrySize; |
||||
IntPtr vtEntryPtr; |
||||
|
||||
basePtr = IntPtr.Zero; |
||||
vtPtr = Marshal.AllocHGlobal (vtableSize); |
||||
|
||||
try { |
||||
int offset = 0; |
||||
for (int i = 0; i < EntryCount; i++) { |
||||
|
||||
if (overrides [i] != null) // managed override
|
||||
vtEntryPtr = Marshal.GetFunctionPointerForDelegate (overrides [i]); |
||||
else |
||||
vtEntryPtr = IntPtr.Zero; |
||||
|
||||
Marshal.WriteIntPtr (vtPtr, offset, vtEntryPtr); |
||||
offset += EntrySize; |
||||
} |
||||
} catch { |
||||
|
||||
Marshal.FreeHGlobal (vtPtr); |
||||
throw; |
||||
} |
||||
} |
||||
|
||||
public virtual void InitInstance (IntPtr instance) |
||||
{ |
||||
if (basePtr == IntPtr.Zero) { |
||||
basePtr = Marshal.ReadIntPtr (instance); |
||||
|
||||
int offset = 0; |
||||
for (int i = 0; i < EntryCount; i++) { |
||||
|
||||
IntPtr vtEntryPtr = Marshal.ReadIntPtr (vtPtr, offset); |
||||
if (vtEntryPtr == IntPtr.Zero) |
||||
Marshal.WriteIntPtr (vtPtr, offset, Marshal.ReadIntPtr (basePtr, offset)); |
||||
|
||||
offset += EntrySize; |
||||
} |
||||
} |
||||
|
||||
Marshal.WriteIntPtr (instance, vtPtr); |
||||
} |
||||
|
||||
public virtual void ResetInstance (IntPtr instance) |
||||
{ |
||||
Marshal.WriteIntPtr (instance, basePtr); |
||||
} |
||||
|
||||
public IntPtr Pointer { |
||||
get { return vtPtr; } |
||||
} |
||||
|
||||
protected virtual void Dispose (bool disposing) |
||||
{ |
||||
if (vtPtr != IntPtr.Zero) { |
||||
Marshal.FreeHGlobal (vtPtr); |
||||
vtPtr = IntPtr.Zero; |
||||
} |
||||
} |
||||
|
||||
// TODO: This WON'T usually be called because VTables are associated with classes
|
||||
// and managed C++ class wrappers are staticly held?
|
||||
public void Dispose () |
||||
{ |
||||
Dispose (true); |
||||
GC.SuppressFinalize (this); |
||||
} |
||||
|
||||
~VTable () |
||||
{ |
||||
Dispose (false); |
||||
} |
||||
|
||||
public static bool BindOverridesOnly (MemberInfo member, object obj) |
||||
{ |
||||
bool result = BindAny (member, obj); |
||||
if (member.GetCustomAttributes (typeof (OverrideNativeAttribute), true).Length != 1) |
||||
return false; |
||||
|
||||
return result; |
||||
} |
||||
|
||||
public static bool BindAny (MemberInfo member, object obj) |
||||
{ |
||||
MethodInfo imethod = (MethodInfo) obj; |
||||
MethodInfo candidate = (MethodInfo) member; |
||||
|
||||
if (!candidate.Name.Equals (imethod.Name)) |
||||
return false; |
||||
|
||||
ParameterInfo[] invokeParams = imethod.GetParameters (); |
||||
ParameterInfo[] methodParams = candidate.GetParameters (); |
||||
|
||||
if (invokeParams.Length == methodParams.Length) { |
||||
for (int i = 0; i < invokeParams.Length; i++) { |
||||
if (!invokeParams [i].ParameterType.IsAssignableFrom (methodParams [i].ParameterType)) |
||||
return false; |
||||
} |
||||
} else if (invokeParams.Length == methodParams.Length + 1) { |
||||
for (int i = 1; i < invokeParams.Length; i++) { |
||||
if (!invokeParams [i].ParameterType.IsAssignableFrom (methodParams [i - 1].ParameterType)) |
||||
return false; |
||||
} |
||||
} else |
||||
return false; |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.ABI.VTableCOM.cs: vtable implementation based on COM interop (reqiures mono patch)
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using System.Reflection; |
||||
using System.Reflection.Emit; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
namespace Mono.VisualC.Interop.ABI { |
||||
public class VTableCOM : VTable { |
||||
|
||||
public VTableCOM (Delegate[] entries) : base(entries) |
||||
{ |
||||
} |
||||
|
||||
public override int EntrySize { |
||||
get { return Marshal.SizeOf (typeof (IntPtr)); } |
||||
} |
||||
|
||||
public override T GetDelegateForNative<T> (IntPtr native, int index) |
||||
{ |
||||
return default(T); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.ABI.VTableManaged.cs: Managed vtable implementation
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
using System.Reflection; |
||||
using System.Reflection.Emit; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
namespace Mono.VisualC.Interop.ABI { |
||||
public class VTableManaged : VTable { |
||||
|
||||
public VTableManaged (Delegate[] entries) : base(entries) |
||||
{ |
||||
} |
||||
|
||||
public override int EntrySize { |
||||
get { return Marshal.SizeOf (typeof (IntPtr)); } |
||||
} |
||||
|
||||
public override T GetDelegateForNative<T> (IntPtr native, int index) |
||||
{ |
||||
IntPtr vtable = Marshal.ReadIntPtr (native); |
||||
if (vtable == vtPtr) // do not return managed overrides
|
||||
vtable = basePtr; |
||||
|
||||
IntPtr ftnptr = Marshal.ReadIntPtr (vtable, index * EntrySize); |
||||
if (ftnptr == IntPtr.Zero) |
||||
return null; |
||||
|
||||
Delegate del = Marshal.GetDelegateForFunctionPointer (ftnptr, typeof (T)); |
||||
return del as T; |
||||
} |
||||
|
||||
} |
||||
|
||||
/* |
||||
protected static Type GetNativeLayoutType(MethodInfo thisMethod) { |
||||
ParameterInfo[] parameters = thisMethod.GetParameters(); |
||||
if (parameters.Length < 1) return null; |
||||
|
||||
Type nativeLayoutType = parameters[0].ParameterType.GetElementType(); |
||||
return nativeLayoutType; |
||||
} |
||||
*/ |
||||
|
||||
} |
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.Attributes.cs
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Reflection; |
||||
|
||||
namespace Mono.VisualC.Interop { |
||||
[AttributeUsage (AttributeTargets.Method)] |
||||
public class VirtualAttribute : Attribute {} |
||||
|
||||
[AttributeUsage (AttributeTargets.Method)] |
||||
public class OverrideNativeAttribute : Attribute {} |
||||
|
||||
public static class Modifiers { |
||||
|
||||
public static bool IsVirtual (MethodInfo method) |
||||
{ |
||||
return method.IsDefined (typeof (VirtualAttribute), false); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.CppField.cs: Represents a field in a native C++ object
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
namespace Mono.VisualC.Interop |
||||
{ |
||||
public class CppField<T> |
||||
{ |
||||
private int fieldOffset; |
||||
|
||||
public CppField (int fieldOffset) |
||||
{ |
||||
this.fieldOffset = fieldOffset; |
||||
} |
||||
|
||||
public T this [CppInstancePtr ip] { |
||||
get { |
||||
Type retType = typeof(T); |
||||
object retVal; |
||||
if (retType.Equals(typeof(Byte))) |
||||
retVal = Marshal.ReadByte(ip.Native, fieldOffset); |
||||
else if (retType.Equals(typeof(Int16))) |
||||
retVal = Marshal.ReadInt16(ip.Native, fieldOffset); |
||||
else if (retType.Equals(typeof(Int32))) |
||||
retVal = Marshal.ReadInt32(ip.Native, fieldOffset); |
||||
else throw new NotImplementedException("Cannot read C++ fields of type " + retType.Name); |
||||
|
||||
return (T)retVal; |
||||
} |
||||
set { |
||||
Type setType = typeof(T); |
||||
object setVal = value; |
||||
if (setType.Equals(typeof(Byte))) |
||||
Marshal.WriteByte(ip.Native, fieldOffset, (byte)setVal); |
||||
else if (setType.Equals(typeof(Int16))) |
||||
Marshal.WriteInt16(ip.Native, fieldOffset, (Int16)setVal); |
||||
else if (setType.Equals(typeof(Int32))) |
||||
Marshal.WriteInt32(ip.Native, fieldOffset, (Int32)setVal); |
||||
else throw new NotImplementedException("Cannot write C++ fields of type " + setType.Name); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,101 @@
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.CppInstancePtr.cs: Represents a pointer to a native C++ instance
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
namespace Mono.VisualC.Interop { |
||||
public struct CppInstancePtr : ICppInstance { |
||||
private IntPtr ptr; |
||||
private bool manageMemory; |
||||
|
||||
// Alloc a new C++ instance
|
||||
internal CppInstancePtr (int nativeSize, object managedWrapper) |
||||
{ |
||||
// Under the hood, we're secretly subclassing this C++ class to store a
|
||||
// handle to the managed wrapper.
|
||||
ptr = Marshal.AllocHGlobal (nativeSize + Marshal.SizeOf (typeof (IntPtr))); |
||||
|
||||
IntPtr handlePtr = GetGCHandle (managedWrapper); |
||||
Marshal.WriteIntPtr (ptr, nativeSize, handlePtr); |
||||
|
||||
manageMemory = true; |
||||
} |
||||
|
||||
// Alloc a new C++ instance when there is no managed wrapper.
|
||||
internal CppInstancePtr (int nativeSize) |
||||
{ |
||||
ptr = Marshal.AllocHGlobal (nativeSize); |
||||
manageMemory = true; |
||||
} |
||||
|
||||
// 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; |
||||
manageMemory = false; |
||||
} |
||||
|
||||
// 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; |
||||
} |
||||
} |
||||
|
||||
public bool IsManagedAlloc { |
||||
get { return manageMemory; } |
||||
} |
||||
|
||||
internal static IntPtr GetGCHandle (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); |
||||
} |
||||
|
||||
// WARNING! This method is not safe. DO NOT call
|
||||
// if we do not KNOW that this instance is managed.
|
||||
internal static T GetManaged<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 (manageMemory && ptr != IntPtr.Zero) |
||||
Marshal.FreeHGlobal (ptr); |
||||
|
||||
ptr = IntPtr.Zero; |
||||
manageMemory = false; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.CppLibrary.cs: Represents a native C++ library for interop
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
|
||||
using System; |
||||
using System.IO; |
||||
using System.Collections.Generic; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
using System.Reflection; |
||||
using System.Reflection.Emit; |
||||
|
||||
using Mono.VisualC.Interop.ABI; |
||||
|
||||
namespace Mono.VisualC.Interop |
||||
{ |
||||
|
||||
public sealed class CppLibrary |
||||
{ |
||||
private static AssemblyBuilder interopAssembly; |
||||
private static ModuleBuilder interopModule; |
||||
|
||||
private CppAbi abi; |
||||
private string name; |
||||
|
||||
static CppLibrary () |
||||
{ |
||||
AssemblyName assemblyName = new AssemblyName ("__CPPLibraryImplAssembly"); |
||||
string moduleName = "__CPPLibraryImplModule"; |
||||
|
||||
interopAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly (assemblyName, AssemblyBuilderAccess.Run); |
||||
interopModule = interopAssembly.DefineDynamicModule (moduleName); |
||||
} |
||||
|
||||
public CppLibrary (string name, CppAbi abi) |
||||
{ |
||||
|
||||
if (name == null) |
||||
throw new ArgumentNullException ("Name cannot be NULL."); |
||||
if (abi == null) |
||||
throw new ArgumentNullException ("Abi cannot be NULL."); |
||||
|
||||
this.name = name; |
||||
this.abi = abi; |
||||
} |
||||
|
||||
public string Name { |
||||
get { return name; } |
||||
} |
||||
|
||||
public CppAbi Abi { |
||||
get { return abi; } |
||||
} |
||||
|
||||
// For a class that may have fields with no virtual methods to be overridden
|
||||
public Iface GetClass<Iface,NativeLayout> (string className) |
||||
where Iface : ICppInstantiatable |
||||
where NativeLayout : struct |
||||
{ |
||||
|
||||
return Abi.ImplementClass<Iface, NativeLayout> (interopModule, null, Name, className); |
||||
} |
||||
|
||||
// For a class that may have fields and virtual methods to be overridden
|
||||
public Iface GetClass<Iface,NativeLayout,Managed> (string className) |
||||
where Iface : ICppOverridable<Managed> |
||||
where NativeLayout : struct |
||||
where Managed : ICppInstance |
||||
{ |
||||
|
||||
return Abi.ImplementClass<Iface, NativeLayout> (interopModule, typeof (Managed), Name, className); |
||||
} |
||||
// TODO: Define a method for pure virtual classes (no NativeLayout)?
|
||||
|
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Mono.VisualC.Interop.Interfaces.cs
|
||||
//
|
||||
// Author:
|
||||
// Alexander Corrado (alexander.corrado@gmail.com)
|
||||
//
|
||||
// Copyright (C) 2010 Alexander Corrado
|
||||
//
|
||||
|
||||
using System; |
||||
|
||||
namespace Mono.VisualC.Interop |
||||
{ |
||||
|
||||
public interface ICppInstance : IDisposable |
||||
{ |
||||
IntPtr Native { get; } |
||||
} |
||||
|
||||
public interface ICppInstantiatable |
||||
{ |
||||
CppInstancePtr Alloc(); |
||||
void Destruct(CppInstancePtr instance); |
||||
} |
||||
|
||||
public interface ICppOverridable<T> where T : ICppInstance |
||||
{ |
||||
CppInstancePtr Alloc(T managed); |
||||
void Destruct(CppInstancePtr instance); |
||||
} |
||||
} |
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
<ProductVersion>8.0.50727</ProductVersion> |
||||
<ProjectGuid>{4A864586-93C5-4DC1-8A80-F094A88506D7}</ProjectGuid> |
||||
<OutputType>Library</OutputType> |
||||
<SchemaVersion>2.0</SchemaVersion> |
||||
<RootNamespace>Mono.VisualC.Interop</RootNamespace> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
<DebugSymbols>true</DebugSymbols> |
||||
<DebugType>full</DebugType> |
||||
<Optimize>false</Optimize> |
||||
<OutputPath>bin\Debug</OutputPath> |
||||
<DefineConstants>DEBUG</DefineConstants> |
||||
<ErrorReport>prompt</ErrorReport> |
||||
<WarningLevel>4</WarningLevel> |
||||
<ConsolePause>false</ConsolePause> |
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
<AssemblyName>Mono.VisualC.Interop</AssemblyName> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
<DebugType>none</DebugType> |
||||
<Optimize>false</Optimize> |
||||
<OutputPath>bin\Release</OutputPath> |
||||
<ErrorReport>prompt</ErrorReport> |
||||
<WarningLevel>4</WarningLevel> |
||||
<ConsolePause>false</ConsolePause> |
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
<AssemblyName>CPPInterop</AssemblyName> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Linux|AnyCPU' "> |
||||
<DebugType>none</DebugType> |
||||
<Optimize>false</Optimize> |
||||
<OutputPath>bin\Release</OutputPath> |
||||
<ErrorReport>prompt</ErrorReport> |
||||
<WarningLevel>4</WarningLevel> |
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
<ConsolePause>false</ConsolePause> |
||||
<AssemblyName>CPPInterop</AssemblyName> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Mac|AnyCPU' "> |
||||
<DebugType>none</DebugType> |
||||
<Optimize>false</Optimize> |
||||
<OutputPath>bin\Mac</OutputPath> |
||||
<WarningLevel>4</WarningLevel> |
||||
<AssemblyName>Mono.VisualC.Interop</AssemblyName> |
||||
</PropertyGroup> |
||||
<ItemGroup> |
||||
<Reference Include="System" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Compile Include="CppLibrary.cs" /> |
||||
<Compile Include="AssemblyInfo.cs" /> |
||||
<Compile Include="ABI\CppAbi.cs" /> |
||||
<Compile Include="ABI\Impl\Itanium.cs" /> |
||||
<Compile Include="Interfaces.cs" /> |
||||
<Compile Include="ABI\VTableManaged.cs" /> |
||||
<Compile Include="Attributes.cs" /> |
||||
<Compile Include="CppInstancePtr.cs" /> |
||||
<Compile Include="CppField.cs" /> |
||||
<Compile Include="ABI\VTableCOM.cs" /> |
||||
<Compile Include="ABI\VTable.cs" /> |
||||
<Compile Include="Util.cs" /> |
||||
<Compile Include="ABI\MethodType.cs" /> |
||||
</ItemGroup> |
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |
||||
<ItemGroup> |
||||
<Folder Include="ABI\" /> |
||||
<Folder Include="ABI\Impl\" /> |
||||
</ItemGroup> |
||||
<ProjectExtensions> |
||||
<MonoDevelop> |
||||
<Properties InternalTargetFrameworkVersion="3.5" /> |
||||
</MonoDevelop> |
||||
</ProjectExtensions> |
||||
</Project> |
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
using System; |
||||
using System.Reflection; |
||||
using System.Reflection.Emit; |
||||
|
||||
namespace Mono.VisualC.Interop { |
||||
public static class Util { |
||||
|
||||
public static MethodInfo GetMethodInfoForDelegate (Type delType) |
||||
{ |
||||
return delType.GetMethod ("Invoke"); |
||||
} |
||||
|
||||
public static Type GetDelegateTypeForMethodInfo (ModuleBuilder mod, MethodInfo targetMethod) |
||||
{ |
||||
|
||||
TypeAttributes typeAttr = TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass; |
||||
TypeBuilder del = mod.DefineType (mod.Name + "_" + targetMethod.Name + "_VTdel", typeAttr, typeof(MulticastDelegate)); |
||||
|
||||
|
||||
MethodAttributes ctorAttr = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; |
||||
ConstructorBuilder ctor = del.DefineConstructor (ctorAttr, CallingConventions.Standard, new Type[] { typeof(object), typeof(System.IntPtr) }); |
||||
ctor.SetImplementationFlags (MethodImplAttributes.Runtime | MethodImplAttributes.Managed); |
||||
|
||||
Type[] parameterTypes = GetMethodParameterTypes (targetMethod, true); |
||||
MethodAttributes methodAttr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; |
||||
|
||||
MethodBuilder invokeMethod = del.DefineMethod ("Invoke", methodAttr, targetMethod.ReturnType, parameterTypes); |
||||
invokeMethod.SetImplementationFlags (MethodImplAttributes.Runtime | MethodImplAttributes.Managed); |
||||
|
||||
return del.CreateType (); |
||||
} |
||||
|
||||
public static Delegate GetDelegateForMethodInfo (ModuleBuilder mod, MethodInfo targetMethod) |
||||
{ |
||||
Type delType = GetDelegateTypeForMethodInfo (mod, targetMethod); |
||||
return Delegate.CreateDelegate (delType, targetMethod); |
||||
} |
||||
|
||||
public static Type[] GetDelegateParameterTypes (Type delType) |
||||
{ |
||||
MethodInfo invoke = GetMethodInfoForDelegate (delType); |
||||
if (invoke == null) |
||||
return null; |
||||
|
||||
return GetMethodParameterTypes (invoke, false); |
||||
} |
||||
|
||||
public static Type[] GetMethodParameterTypes (MethodInfo method, bool forPInvoke) |
||||
{ |
||||
ParameterInfo[] parameters = method.GetParameters (); |
||||
Type[] parameterTypes = new Type [parameters.Length]; |
||||
|
||||
for (int i = 0; i < parameters.Length; i++) { |
||||
if (forPInvoke) { |
||||
if (parameters [i].ParameterType.Equals (typeof (CppInstancePtr))) |
||||
parameterTypes [i] = typeof (IntPtr); |
||||
else if (parameters [i].ParameterType.IsPointer) |
||||
parameterTypes [i] = parameters [i].ParameterType.GetElementType ().MakeByRefType (); |
||||
else |
||||
parameterTypes [i] = parameters [i].ParameterType; |
||||
} else |
||||
parameterTypes [i] = parameters [i].ParameterType; |
||||
} |
||||
|
||||
return parameterTypes; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue