mirror of https://github.com/mono/CppSharp.git
9 changed files with 2184 additions and 22 deletions
@ -0,0 +1,16 @@ |
|||||||
|
using CppSharp.AST; |
||||||
|
|
||||||
|
namespace CppSharp.Extensions |
||||||
|
{ |
||||||
|
public static class FunctionExtensions |
||||||
|
{ |
||||||
|
public static bool IsNativeMethod(this Function function) |
||||||
|
{ |
||||||
|
var method = function as Method; |
||||||
|
if (method == null) |
||||||
|
return false; |
||||||
|
|
||||||
|
return method.Conversion == MethodConversionKind.None; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,133 @@ |
|||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using CppSharp.AST; |
||||||
|
using CppSharp.Generators.C; |
||||||
|
|
||||||
|
namespace CppSharp.Generators.Cpp |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Generates a common Node N-API C/C++ common files.
|
||||||
|
/// N-API documentation: https://nodejs.org/api/n-api.html
|
||||||
|
/// </summary>
|
||||||
|
public class NAPIHelpers : CppHeaders |
||||||
|
{ |
||||||
|
public NAPIHelpers(BindingContext context) |
||||||
|
: base(context, null) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public override void Process() |
||||||
|
{ |
||||||
|
GenerateFilePreamble(CommentKind.BCPL); |
||||||
|
|
||||||
|
WriteLine("#pragma once"); |
||||||
|
NewLine(); |
||||||
|
|
||||||
|
WriteInclude("math.h", CInclude.IncludeKind.Angled); |
||||||
|
WriteInclude("limits.h", CInclude.IncludeKind.Angled); |
||||||
|
NewLine(); |
||||||
|
|
||||||
|
GenerateHelpers(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
private void GenerateHelpers() |
||||||
|
{ |
||||||
|
WriteLine(@"#define NAPI_CALL(env, call) \
|
||||||
|
do { \ |
||||||
|
napi_status status = (call); \ |
||||||
|
if (status != napi_ok) { \ |
||||||
|
const napi_extended_error_info* error_info = NULL; \ |
||||||
|
napi_get_last_error_info((env), &error_info); \ |
||||||
|
bool is_pending; \ |
||||||
|
napi_is_exception_pending((env), &is_pending); \ |
||||||
|
if (!is_pending) { \ |
||||||
|
const char* message = (error_info->error_message == NULL) \ |
||||||
|
? ""empty error message"" \ |
||||||
|
: error_info->error_message; \ |
||||||
|
napi_throw_error((env), NULL, message); \ |
||||||
|
return NULL; \ |
||||||
|
} \ |
||||||
|
} \ |
||||||
|
} while(0)");
|
||||||
|
NewLine(); |
||||||
|
|
||||||
|
WriteLine(@"#define NAPI_CALL_NORET(env, call) \
|
||||||
|
do { \ |
||||||
|
napi_status status = (call); \ |
||||||
|
if (status != napi_ok) { \ |
||||||
|
const napi_extended_error_info* error_info = NULL; \ |
||||||
|
napi_get_last_error_info((env), &error_info); \ |
||||||
|
bool is_pending; \ |
||||||
|
napi_is_exception_pending((env), &is_pending); \ |
||||||
|
if (!is_pending) { \ |
||||||
|
const char* message = (error_info->error_message == NULL) \ |
||||||
|
? ""empty error message"" \ |
||||||
|
: error_info->error_message; \ |
||||||
|
napi_throw_error((env), NULL, message); \ |
||||||
|
return; \ |
||||||
|
} \ |
||||||
|
} \ |
||||||
|
} while(0)");
|
||||||
|
NewLine(); |
||||||
|
|
||||||
|
WriteLine(@"static int napi_is_int32(napi_env env, napi_value value, int* integer) {
|
||||||
|
double temp = 0; |
||||||
|
if ( |
||||||
|
// We get the value as a double so we can check for NaN, Infinity and float:
|
||||||
|
// https://github.com/nodejs/node/issues/26323
|
||||||
|
napi_get_value_double(env, value, &temp) != napi_ok || |
||||||
|
// Reject NaN:
|
||||||
|
isnan(temp) || |
||||||
|
// Reject Infinity and avoid undefined behavior when casting double to int:
|
||||||
|
// https://groups.google.com/forum/#!topic/comp.lang.c/rhPzd4bgKJk
|
||||||
|
temp < INT_MIN || |
||||||
|
temp > INT_MAX || |
||||||
|
// Reject float by casting double to int:
|
||||||
|
(double) ((int) temp) != temp |
||||||
|
) { |
||||||
|
//napi_throw_error(env, NULL, ""argument must be an integer"");
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
if (integer) |
||||||
|
*integer = (int) temp; |
||||||
|
return 1; |
||||||
|
}");
|
||||||
|
NewLine(); |
||||||
|
|
||||||
|
WriteLine(@"static int napi_is_uint32(napi_env env, napi_value value, int* integer) {
|
||||||
|
double temp = 0; |
||||||
|
if ( |
||||||
|
// We get the value as a double so we can check for NaN, Infinity and float:
|
||||||
|
// https://github.com/nodejs/node/issues/26323
|
||||||
|
napi_get_value_double(env, value, &temp) != napi_ok || |
||||||
|
// Reject NaN:
|
||||||
|
isnan(temp) || |
||||||
|
// Reject Infinity and avoid undefined behavior when casting double to int:
|
||||||
|
// https://groups.google.com/forum/#!topic/comp.lang.c/rhPzd4bgKJk
|
||||||
|
temp < 0 || |
||||||
|
temp > ULONG_MAX || |
||||||
|
// Reject float by casting double to int:
|
||||||
|
(double) ((unsigned long) temp) != temp |
||||||
|
) { |
||||||
|
//napi_throw_error(env, NULL, ""argument must be an integer"");
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
if (integer) |
||||||
|
*integer = (int) temp; |
||||||
|
return 1; |
||||||
|
}");
|
||||||
|
NewLine(); |
||||||
|
|
||||||
|
WriteLine(@"#define NAPI_IS_BOOL(valuetype) (valuetype == napi_boolean)"); |
||||||
|
WriteLine(@"#define NAPI_IS_NULL(valuetype) (valuetype == napi_null)"); |
||||||
|
WriteLine(@"#define NAPI_IS_NUMBER(valuetype) (valuetype == napi_number)"); |
||||||
|
WriteLine(@"#define NAPI_IS_BIGINT(valuetype) (valuetype == napi_bigint)"); |
||||||
|
WriteLine(@"#define NAPI_IS_INT32(valuetype, value) (NAPI_IS_NUMBER(valuetype) && napi_is_int32(env, value, nullptr))"); |
||||||
|
WriteLine(@"#define NAPI_IS_UINT32(valuetype, value) (NAPI_IS_NUMBER(valuetype) && napi_is_uint32(env, value, nullptr))"); |
||||||
|
WriteLine(@"#define NAPI_IS_INT64(valuetype, value) (NAPI_IS_BIGINT(valuetype))"); |
||||||
|
WriteLine(@"#define NAPI_IS_UINT64(valuetype, value) (NAPI_IS_BIGINT(valuetype))"); |
||||||
|
NewLine(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,874 @@ |
|||||||
|
using System; |
||||||
|
using CppSharp.AST; |
||||||
|
using CppSharp.AST.Extensions; |
||||||
|
using CppSharp.Generators.C; |
||||||
|
using CppSharp.Types; |
||||||
|
using Delegate = CppSharp.AST.Delegate; |
||||||
|
using Type = CppSharp.AST.Type; |
||||||
|
|
||||||
|
namespace CppSharp.Generators.NAPI |
||||||
|
{ |
||||||
|
public class NAPIMarshalNativeToManagedPrinter : MarshalPrinter<MarshalContext> |
||||||
|
{ |
||||||
|
public NAPIMarshalNativeToManagedPrinter(MarshalContext marshalContext) |
||||||
|
: base(marshalContext) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public string MemoryAllocOperator => |
||||||
|
(Context.Context.Options.GeneratorKind == GeneratorKind.CLI) ? |
||||||
|
"gcnew" : "new"; |
||||||
|
|
||||||
|
public override bool VisitType(Type type, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
TypeMap typeMap; |
||||||
|
if (Context.Context.TypeMaps.FindTypeMap(type, out typeMap) && typeMap.DoesMarshalling) |
||||||
|
{ |
||||||
|
typeMap.CppMarshalToManaged(Context); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitArrayType(ArrayType array, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
switch (array.SizeType) |
||||||
|
{ |
||||||
|
case ArrayType.ArraySize.Constant: |
||||||
|
case ArrayType.ArraySize.Incomplete: |
||||||
|
case ArrayType.ArraySize.Variable: |
||||||
|
Context.Return.Write("nullptr"); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new System.NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFunctionType(FunctionType function, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
Context.Return.Write(Context.ReturnVarName); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (!VisitType(pointer, quals)) |
||||||
|
return false; |
||||||
|
|
||||||
|
var pointee = pointer.Pointee.Desugar(); |
||||||
|
|
||||||
|
PrimitiveType primitive; |
||||||
|
var param = Context.Parameter; |
||||||
|
if (param != null && (param.IsOut || param.IsInOut) && |
||||||
|
pointee.IsPrimitiveType(out primitive)) |
||||||
|
{ |
||||||
|
Context.Return.Write(Context.ReturnVarName); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (pointee.IsPrimitiveType(out primitive)) |
||||||
|
{ |
||||||
|
var returnVarName = Context.ReturnVarName; |
||||||
|
|
||||||
|
if (pointer.GetFinalQualifiedPointee().Qualifiers.IsConst != |
||||||
|
Context.ReturnType.Qualifiers.IsConst) |
||||||
|
{ |
||||||
|
var nativeTypePrinter = new CppTypePrinter(Context.Context) |
||||||
|
{ PrintTypeQualifiers = false }; |
||||||
|
var returnType = Context.ReturnType.Type.Desugar(); |
||||||
|
var constlessPointer = new PointerType() |
||||||
|
{ |
||||||
|
IsDependent = pointer.IsDependent, |
||||||
|
Modifier = pointer.Modifier, |
||||||
|
QualifiedPointee = new QualifiedType(returnType.GetPointee()) |
||||||
|
}; |
||||||
|
var nativeConstlessTypeName = constlessPointer.Visit(nativeTypePrinter, new TypeQualifiers()); |
||||||
|
returnVarName = string.Format("const_cast<{0}>({1})", |
||||||
|
nativeConstlessTypeName, Context.ReturnVarName); |
||||||
|
} |
||||||
|
|
||||||
|
if (pointer.Pointee is TypedefType) |
||||||
|
{ |
||||||
|
var desugaredPointer = new PointerType() |
||||||
|
{ |
||||||
|
IsDependent = pointer.IsDependent, |
||||||
|
Modifier = pointer.Modifier, |
||||||
|
QualifiedPointee = new QualifiedType(pointee) |
||||||
|
}; |
||||||
|
var nativeTypePrinter = new CppTypePrinter(Context.Context); |
||||||
|
var nativeTypeName = desugaredPointer.Visit(nativeTypePrinter, quals); |
||||||
|
Context.Return.Write("reinterpret_cast<{0}>({1})", nativeTypeName, |
||||||
|
returnVarName); |
||||||
|
} |
||||||
|
else |
||||||
|
Context.Return.Write(returnVarName); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
TypeMap typeMap = null; |
||||||
|
Context.Context.TypeMaps.FindTypeMap(pointee, out typeMap); |
||||||
|
|
||||||
|
Class @class; |
||||||
|
if (pointee.TryGetClass(out @class) && typeMap == null) |
||||||
|
{ |
||||||
|
var instance = (pointer.IsReference) ? "&" + Context.ReturnVarName |
||||||
|
: Context.ReturnVarName; |
||||||
|
WriteClassInstance(@class, instance); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return pointer.QualifiedPointee.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitMemberPointerType(MemberPointerType member, |
||||||
|
TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return VisitPrimitiveType(builtin.Type); |
||||||
|
} |
||||||
|
|
||||||
|
public static (string type, string func) GetNAPIPrimitiveType(PrimitiveType type) |
||||||
|
{ |
||||||
|
switch(type) |
||||||
|
{ |
||||||
|
case PrimitiveType.Bool: |
||||||
|
return ("napi_boolean", "napi_get_boolean"); |
||||||
|
case PrimitiveType.WideChar: |
||||||
|
case PrimitiveType.Char: |
||||||
|
case PrimitiveType.SChar: |
||||||
|
case PrimitiveType.Char16: |
||||||
|
case PrimitiveType.Char32: |
||||||
|
case PrimitiveType.Short: |
||||||
|
case PrimitiveType.Int: |
||||||
|
case PrimitiveType.Long: |
||||||
|
return ("napi_number", "napi_create_int32"); |
||||||
|
case PrimitiveType.UChar: |
||||||
|
case PrimitiveType.UShort: |
||||||
|
case PrimitiveType.UInt: |
||||||
|
case PrimitiveType.ULong: |
||||||
|
return ("napi_number", "napi_create_uint32"); |
||||||
|
case PrimitiveType.LongLong: |
||||||
|
return ("napi_number", "napi_create_bigint_int64"); |
||||||
|
case PrimitiveType.ULongLong: |
||||||
|
return ("napi_number", "napi_create_bigint_uint64"); |
||||||
|
case PrimitiveType.Half: |
||||||
|
case PrimitiveType.Float: |
||||||
|
case PrimitiveType.Double: |
||||||
|
case PrimitiveType.LongDouble: |
||||||
|
return ("napi_number", "napi_create_double"); |
||||||
|
case PrimitiveType.Int128: |
||||||
|
case PrimitiveType.UInt128: |
||||||
|
case PrimitiveType.Float128: |
||||||
|
return ("napi_bigint", "napi_create_bigint_words"); |
||||||
|
case PrimitiveType.String: |
||||||
|
return ("napi_string", "napi_create_string_latin1"); |
||||||
|
case PrimitiveType.Null: |
||||||
|
return ("napi_null", "napi_get_null"); |
||||||
|
case PrimitiveType.Void: |
||||||
|
return ("napi_undefined", "napi_get_undefined"); |
||||||
|
case PrimitiveType.IntPtr: |
||||||
|
case PrimitiveType.UIntPtr: |
||||||
|
case PrimitiveType.Decimal: |
||||||
|
default: |
||||||
|
throw new ArgumentOutOfRangeException(nameof(type), type, null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitPrimitiveType(PrimitiveType primitive) |
||||||
|
{ |
||||||
|
var result = $"__result"; |
||||||
|
var (_, func) = GetNAPIPrimitiveType(primitive); |
||||||
|
|
||||||
|
switch (primitive) |
||||||
|
{ |
||||||
|
case PrimitiveType.Bool: |
||||||
|
case PrimitiveType.WideChar: |
||||||
|
case PrimitiveType.Char: |
||||||
|
case PrimitiveType.SChar: |
||||||
|
case PrimitiveType.Char16: |
||||||
|
case PrimitiveType.Char32: |
||||||
|
case PrimitiveType.Short: |
||||||
|
case PrimitiveType.Int: |
||||||
|
case PrimitiveType.Long: |
||||||
|
case PrimitiveType.UChar: |
||||||
|
case PrimitiveType.UShort: |
||||||
|
case PrimitiveType.UInt: |
||||||
|
case PrimitiveType.ULong: |
||||||
|
case PrimitiveType.LongLong: |
||||||
|
case PrimitiveType.ULongLong: |
||||||
|
case PrimitiveType.Half: |
||||||
|
case PrimitiveType.Float: |
||||||
|
case PrimitiveType.Double: |
||||||
|
case PrimitiveType.LongDouble: |
||||||
|
Context.Before.WriteLine($"napi_value {result};"); |
||||||
|
Context.Before.WriteLine($"status = {func}(env, {Context.ReturnVarName}, &{result});"); |
||||||
|
Context.Before.WriteLine("assert(status == napi_ok);"); |
||||||
|
Context.Return.Write(result); |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.Int128: |
||||||
|
case PrimitiveType.UInt128: |
||||||
|
case PrimitiveType.Float128: |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.String: |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.Null: |
||||||
|
Context.Before.WriteLine($"napi_value {result};"); |
||||||
|
Context.Before.WriteLine($"status = {func}(env, &{result});"); |
||||||
|
Context.Before.WriteLine("assert(status == napi_ok);"); |
||||||
|
Context.Return.Write(result); |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.IntPtr: |
||||||
|
case PrimitiveType.UIntPtr: |
||||||
|
case PrimitiveType.Decimal: |
||||||
|
default: |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTypedefType(TypedefType typedef, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
var decl = typedef.Declaration; |
||||||
|
|
||||||
|
TypeMap typeMap; |
||||||
|
if (Context.Context.TypeMaps.FindTypeMap(decl.Type, out typeMap) && |
||||||
|
typeMap.DoesMarshalling) |
||||||
|
{ |
||||||
|
typeMap.Type = typedef; |
||||||
|
typeMap.CppMarshalToManaged(Context); |
||||||
|
return typeMap.IsValueType; |
||||||
|
} |
||||||
|
|
||||||
|
FunctionType function; |
||||||
|
if (decl.Type.IsPointerTo(out function)) |
||||||
|
{ |
||||||
|
var typePrinter = new CppTypePrinter(Context.Context); |
||||||
|
var typeName = typePrinter.VisitDeclaration(decl); |
||||||
|
var typeName2 = decl.Type.Visit(typePrinter); |
||||||
|
Context.Return.Write(typeName); |
||||||
|
//return typeName;
|
||||||
|
//throw new System.NotImplementedException();
|
||||||
|
} |
||||||
|
|
||||||
|
return decl.Type.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTemplateSpecializationType(TemplateSpecializationType template, |
||||||
|
TypeQualifiers quals) |
||||||
|
{ |
||||||
|
TypeMap typeMap; |
||||||
|
if (Context.Context.TypeMaps.FindTypeMap(template, out typeMap) && typeMap.DoesMarshalling) |
||||||
|
{ |
||||||
|
typeMap.Type = template; |
||||||
|
typeMap.CppMarshalToManaged(Context); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return template.Template.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitPrimitiveType(PrimitiveType type, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitDeclaration(Declaration decl, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitClassDecl(Class @class) |
||||||
|
{ |
||||||
|
if (@class.CompleteDeclaration != null) |
||||||
|
return VisitClassDecl(@class.CompleteDeclaration as Class); |
||||||
|
|
||||||
|
var instance = string.Empty; |
||||||
|
|
||||||
|
if (Context.Context.Options.GeneratorKind == GeneratorKind.CLI) |
||||||
|
{ |
||||||
|
if (!Context.ReturnType.Type.IsPointer()) |
||||||
|
instance += "&"; |
||||||
|
} |
||||||
|
|
||||||
|
instance += Context.ReturnVarName; |
||||||
|
var needsCopy = Context.MarshalKind != MarshalKind.NativeField; |
||||||
|
|
||||||
|
if (@class.IsRefType && needsCopy) |
||||||
|
{ |
||||||
|
var name = Generator.GeneratedIdentifier(Context.ReturnVarName); |
||||||
|
Context.Before.WriteLine($"auto {name} = {MemoryAllocOperator} ::{{0}}({{1}});", |
||||||
|
@class.QualifiedOriginalName, Context.ReturnVarName); |
||||||
|
instance = name; |
||||||
|
} |
||||||
|
|
||||||
|
WriteClassInstance(@class, instance); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public string QualifiedIdentifier(Declaration decl) |
||||||
|
{ |
||||||
|
if (!string.IsNullOrEmpty(decl.TranslationUnit.Module.OutputNamespace)) |
||||||
|
return $"{decl.TranslationUnit.Module.OutputNamespace}::{decl.QualifiedName}"; |
||||||
|
|
||||||
|
return decl.QualifiedName; |
||||||
|
} |
||||||
|
|
||||||
|
public void WriteClassInstance(Class @class, string instance) |
||||||
|
{ |
||||||
|
if (@class.CompleteDeclaration != null) |
||||||
|
{ |
||||||
|
WriteClassInstance(@class.CompleteDeclaration as Class, instance); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!Context.ReturnType.Type.Desugar().IsPointer()) |
||||||
|
{ |
||||||
|
Context.Return.Write($"{instance}"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (@class.IsRefType) |
||||||
|
Context.Return.Write($"({instance} == nullptr) ? nullptr : {MemoryAllocOperator} "); |
||||||
|
|
||||||
|
Context.Return.Write($"{QualifiedIdentifier(@class)}("); |
||||||
|
Context.Return.Write($"(::{@class.QualifiedOriginalName}*)"); |
||||||
|
Context.Return.Write($"{instance})"); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFieldDecl(Field field) |
||||||
|
{ |
||||||
|
return field.Type.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFunctionDecl(Function function) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitMethodDecl(Method method) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitParameterDecl(Parameter parameter) |
||||||
|
{ |
||||||
|
Context.Parameter = parameter; |
||||||
|
var ret = parameter.Type.Visit(this, parameter.QualifiedType.Qualifiers); |
||||||
|
Context.Parameter = null; |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTypedefDecl(TypedefDecl typedef) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitEnumDecl(Enumeration @enum) |
||||||
|
{ |
||||||
|
var typePrinter = new CppTypePrinter(Context.Context); |
||||||
|
typePrinter.PushContext(TypePrinterContextKind.Managed); |
||||||
|
var typeName = typePrinter.VisitDeclaration(@enum); |
||||||
|
Context.Return.Write($"({typeName}){Context.ReturnVarName}"); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitVariableDecl(Variable variable) |
||||||
|
{ |
||||||
|
return variable.Type.Visit(this, variable.QualifiedType.Qualifiers); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitClassTemplateDecl(ClassTemplate template) |
||||||
|
{ |
||||||
|
return template.TemplatedClass.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFunctionTemplateDecl(FunctionTemplate template) |
||||||
|
{ |
||||||
|
return template.TemplatedFunction.Visit(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class NAPIMarshalManagedToNativePrinter : MarshalPrinter<MarshalContext> |
||||||
|
{ |
||||||
|
public readonly TextGenerator VarPrefix; |
||||||
|
public readonly TextGenerator ArgumentPrefix; |
||||||
|
|
||||||
|
public NAPIMarshalManagedToNativePrinter(MarshalContext ctx) |
||||||
|
: base(ctx) |
||||||
|
{ |
||||||
|
VarPrefix = new TextGenerator(); |
||||||
|
ArgumentPrefix = new TextGenerator(); |
||||||
|
|
||||||
|
Context.MarshalToNative = this; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitType(Type type, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
TypeMap typeMap; |
||||||
|
if (Context.Context.TypeMaps.FindTypeMap(type, out typeMap) && typeMap.DoesMarshalling) |
||||||
|
{ |
||||||
|
typeMap.CppMarshalToNative(Context); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTagType(TagType tag, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (!VisitType(tag, quals)) |
||||||
|
return false; |
||||||
|
|
||||||
|
return tag.Declaration.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitArrayType(ArrayType array, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (!VisitType(array, quals)) |
||||||
|
return false; |
||||||
|
|
||||||
|
switch (array.SizeType) |
||||||
|
{ |
||||||
|
default: |
||||||
|
Context.Return.Write("nullptr"); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFunctionType(FunctionType function, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
var returnType = function.ReturnType; |
||||||
|
return returnType.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitDelegateType(string type) |
||||||
|
{ |
||||||
|
Context.Return.Write(Context.Parameter.Name); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (!VisitType(pointer, quals)) |
||||||
|
return false; |
||||||
|
|
||||||
|
var pointee = pointer.Pointee.Desugar(); |
||||||
|
|
||||||
|
if (pointee is FunctionType) |
||||||
|
{ |
||||||
|
var cppTypePrinter = new CppTypePrinter(Context.Context); |
||||||
|
cppTypePrinter.PushContext(TypePrinterContextKind.Managed); |
||||||
|
var cppTypeName = pointer.Visit(cppTypePrinter, quals); |
||||||
|
|
||||||
|
return VisitDelegateType(cppTypeName); |
||||||
|
} |
||||||
|
|
||||||
|
Enumeration @enum; |
||||||
|
if (pointee.TryGetEnum(out @enum)) |
||||||
|
{ |
||||||
|
var isRef = Context.Parameter.Usage == ParameterUsage.Out || |
||||||
|
Context.Parameter.Usage == ParameterUsage.InOut; |
||||||
|
|
||||||
|
ArgumentPrefix.Write("&"); |
||||||
|
Context.Return.Write($"(::{@enum.QualifiedOriginalName}){0}{Context.Parameter.Name}", |
||||||
|
isRef ? string.Empty : "*"); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
Class @class; |
||||||
|
if (pointee.TryGetClass(out @class) && @class.IsValueType) |
||||||
|
{ |
||||||
|
if (Context.Function == null) |
||||||
|
Context.Return.Write("&"); |
||||||
|
return pointer.QualifiedPointee.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
var finalPointee = pointer.GetFinalPointee(); |
||||||
|
if (finalPointee.IsPrimitiveType()) |
||||||
|
{ |
||||||
|
var cppTypePrinter = new CppTypePrinter(Context.Context); |
||||||
|
var cppTypeName = pointer.Visit(cppTypePrinter, quals); |
||||||
|
|
||||||
|
Context.Return.Write($"({cppTypeName})"); |
||||||
|
Context.Return.Write(Context.Parameter.Name); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return pointer.QualifiedPointee.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitMemberPointerType(MemberPointerType member, |
||||||
|
TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return VisitPrimitiveType(builtin.Type); |
||||||
|
} |
||||||
|
|
||||||
|
public static (string func, string type, string cast) GetNAPIPrimitiveGetter(PrimitiveType type) |
||||||
|
{ |
||||||
|
switch(type) |
||||||
|
{ |
||||||
|
case PrimitiveType.Bool: |
||||||
|
return ("napi_get_value_bool", "bool", "bool"); |
||||||
|
case PrimitiveType.WideChar: |
||||||
|
return ("napi_get_value_int32", "int32_t", "wchar_t"); |
||||||
|
case PrimitiveType.Char: |
||||||
|
case PrimitiveType.SChar: |
||||||
|
return ("napi_get_value_int32", "int32_t", "char"); |
||||||
|
case PrimitiveType.Char16: |
||||||
|
return ("napi_get_value_int32", "int32_t", "char16_t"); |
||||||
|
case PrimitiveType.Char32: |
||||||
|
return ("napi_get_value_int32", "int32_t", "char32_t"); |
||||||
|
case PrimitiveType.Short: |
||||||
|
case PrimitiveType.Int: |
||||||
|
case PrimitiveType.Long: |
||||||
|
return ("napi_get_value_int32", "int32_t", null); |
||||||
|
case PrimitiveType.UChar: |
||||||
|
case PrimitiveType.UShort: |
||||||
|
case PrimitiveType.UInt: |
||||||
|
case PrimitiveType.ULong: |
||||||
|
return ("napi_get_value_uint32", "uint32_t", null); |
||||||
|
case PrimitiveType.LongLong: |
||||||
|
return ("napi_get_value_bigint_int64", "int64_t", null); |
||||||
|
case PrimitiveType.ULongLong: |
||||||
|
return ("napi_get_value_bigint_uint64", "uint64_t", null); |
||||||
|
case PrimitiveType.Half: |
||||||
|
case PrimitiveType.Float: |
||||||
|
return ("napi_get_value_double", "double", "float"); |
||||||
|
case PrimitiveType.Double: |
||||||
|
case PrimitiveType.LongDouble: |
||||||
|
return ("napi_get_value_double", "double", null); |
||||||
|
case PrimitiveType.Int128: |
||||||
|
case PrimitiveType.UInt128: |
||||||
|
case PrimitiveType.Float128: |
||||||
|
return (null, null, null); |
||||||
|
case PrimitiveType.String: |
||||||
|
return (null, null, null); |
||||||
|
case PrimitiveType.Null: |
||||||
|
return ("napi_get_null", null, null); |
||||||
|
case PrimitiveType.Void: |
||||||
|
return (null, null, null); |
||||||
|
case PrimitiveType.IntPtr: |
||||||
|
case PrimitiveType.UIntPtr: |
||||||
|
return (null, null, null); |
||||||
|
case PrimitiveType.Decimal: |
||||||
|
default: |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitPrimitiveType(PrimitiveType primitive) |
||||||
|
{ |
||||||
|
var (func, type, cast) = GetNAPIPrimitiveGetter(primitive); |
||||||
|
|
||||||
|
switch (primitive) |
||||||
|
{ |
||||||
|
case PrimitiveType.Bool: |
||||||
|
case PrimitiveType.WideChar: |
||||||
|
case PrimitiveType.Char: |
||||||
|
case PrimitiveType.SChar: |
||||||
|
case PrimitiveType.Char16: |
||||||
|
case PrimitiveType.Char32: |
||||||
|
case PrimitiveType.Short: |
||||||
|
case PrimitiveType.Int: |
||||||
|
case PrimitiveType.Long: |
||||||
|
case PrimitiveType.UChar: |
||||||
|
case PrimitiveType.UShort: |
||||||
|
case PrimitiveType.UInt: |
||||||
|
case PrimitiveType.ULong: |
||||||
|
case PrimitiveType.Half: |
||||||
|
case PrimitiveType.Float: |
||||||
|
case PrimitiveType.Double: |
||||||
|
case PrimitiveType.LongDouble: |
||||||
|
Context.Before.WriteLine($"{type} {Context.Parameter.Name};"); |
||||||
|
Context.Before.WriteLine($"status = {func}(env, args[{Context.ParameterIndex}]," + |
||||||
|
$" &{Context.Parameter.Name});"); |
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(cast)) |
||||||
|
Context.Return.Write($"({cast})"); |
||||||
|
|
||||||
|
Context.Return.Write($"{Context.Parameter.Name}"); |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.LongLong: |
||||||
|
case PrimitiveType.ULongLong: |
||||||
|
case PrimitiveType.Int128: |
||||||
|
case PrimitiveType.UInt128: |
||||||
|
case PrimitiveType.Float128: |
||||||
|
Context.Before.WriteLine($"{type} {Context.Parameter.Name};"); |
||||||
|
Context.Before.WriteLine("bool lossless;"); |
||||||
|
Context.Before.WriteLine($"status = {func}(env, args[{Context.ParameterIndex}]," + |
||||||
|
$" &{Context.Parameter.Name}, &lossless);"); |
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(cast)) |
||||||
|
Context.Return.Write($"({cast})"); |
||||||
|
|
||||||
|
Context.Return.Write($"{Context.Parameter.Name}"); |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.String: |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.Null: |
||||||
|
|
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.Void: |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.IntPtr: |
||||||
|
case PrimitiveType.UIntPtr: |
||||||
|
return true; |
||||||
|
|
||||||
|
case PrimitiveType.Decimal: |
||||||
|
default: |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTypedefType(TypedefType typedef, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
var decl = typedef.Declaration; |
||||||
|
|
||||||
|
TypeMap typeMap; |
||||||
|
if (Context.Context.TypeMaps.FindTypeMap(decl.Type, out typeMap) && |
||||||
|
typeMap.DoesMarshalling) |
||||||
|
{ |
||||||
|
typeMap.CppMarshalToNative(Context); |
||||||
|
return typeMap.IsValueType; |
||||||
|
} |
||||||
|
|
||||||
|
FunctionType func; |
||||||
|
if (decl.Type.IsPointerTo(out func)) |
||||||
|
{ |
||||||
|
var typePrinter = new CppTypePrinter(Context.Context); |
||||||
|
typePrinter.PushContext(TypePrinterContextKind.Native); |
||||||
|
var declName = decl.Visit(typePrinter); |
||||||
|
typePrinter.PopContext(); |
||||||
|
|
||||||
|
// Use the original typedef name if available, otherwise just use the function pointer type
|
||||||
|
string cppTypeName; |
||||||
|
if (!decl.IsSynthetized) |
||||||
|
{ |
||||||
|
cppTypeName = "::" + typedef.Declaration.QualifiedOriginalName; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
cppTypeName = decl.Type.Visit(typePrinter, quals); |
||||||
|
} |
||||||
|
|
||||||
|
VisitDelegateType(cppTypeName); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
PrimitiveType primitive; |
||||||
|
if (decl.Type.IsPrimitiveType(out primitive)) |
||||||
|
{ |
||||||
|
Context.Return.Write($"(::{typedef.Declaration.QualifiedOriginalName})"); |
||||||
|
} |
||||||
|
|
||||||
|
return decl.Type.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTemplateSpecializationType(TemplateSpecializationType template, |
||||||
|
TypeQualifiers quals) |
||||||
|
{ |
||||||
|
TypeMap typeMap; |
||||||
|
if (Context.Context.TypeMaps.FindTypeMap(template, out typeMap) && typeMap.DoesMarshalling) |
||||||
|
{ |
||||||
|
typeMap.Type = template; |
||||||
|
typeMap.CppMarshalToNative(Context); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return template.Template.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
Context.Return.Write(param.Parameter.Name); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitPrimitiveType(PrimitiveType type, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitDeclaration(Declaration decl, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitClassDecl(Class @class) |
||||||
|
{ |
||||||
|
if (@class.CompleteDeclaration != null) |
||||||
|
return VisitClassDecl(@class.CompleteDeclaration as Class); |
||||||
|
|
||||||
|
if (@class.IsValueType) |
||||||
|
{ |
||||||
|
MarshalValueClass(@class); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
MarshalRefClass(@class); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private void MarshalRefClass(Class @class) |
||||||
|
{ |
||||||
|
var type = Context.Parameter.Type.Desugar(); |
||||||
|
TypeMap typeMap; |
||||||
|
if (Context.Context.TypeMaps.FindTypeMap(type, out typeMap) && |
||||||
|
typeMap.DoesMarshalling) |
||||||
|
{ |
||||||
|
typeMap.CppMarshalToNative(Context); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!type.SkipPointerRefs().IsPointer()) |
||||||
|
{ |
||||||
|
Context.Return.Write("*"); |
||||||
|
|
||||||
|
if (Context.Parameter.Type.IsReference()) |
||||||
|
VarPrefix.Write("&"); |
||||||
|
} |
||||||
|
|
||||||
|
var method = Context.Function as Method; |
||||||
|
if (method != null |
||||||
|
&& method.Conversion == MethodConversionKind.FunctionToInstanceMethod |
||||||
|
&& Context.ParameterIndex == 0) |
||||||
|
{ |
||||||
|
Context.Return.Write($"(::{@class.QualifiedOriginalName}*)"); |
||||||
|
Context.Return.Write(Helpers.InstanceIdentifier); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
var paramType = Context.Parameter.Type.Desugar(); |
||||||
|
var isPointer = paramType.SkipPointerRefs().IsPointer(); |
||||||
|
var deref = isPointer ? "->" : "."; |
||||||
|
var instance = $"(::{@class.QualifiedOriginalName}*)" + |
||||||
|
$"{Context.Parameter.Name}{deref}{Helpers.InstanceIdentifier}"; |
||||||
|
|
||||||
|
if (isPointer) |
||||||
|
Context.Return.Write($"{Context.Parameter.Name} ? {instance} : nullptr"); |
||||||
|
else |
||||||
|
Context.Return.Write($"{instance}"); |
||||||
|
} |
||||||
|
|
||||||
|
private void MarshalValueClass(Class @class) |
||||||
|
{ |
||||||
|
throw new System.NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFieldDecl(Field field) |
||||||
|
{ |
||||||
|
Context.Parameter = new Parameter |
||||||
|
{ |
||||||
|
Name = Context.ArgName, |
||||||
|
QualifiedType = field.QualifiedType |
||||||
|
}; |
||||||
|
|
||||||
|
return field.Type.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitProperty(Property property) |
||||||
|
{ |
||||||
|
Context.Parameter = new Parameter |
||||||
|
{ |
||||||
|
Name = Context.ArgName, |
||||||
|
QualifiedType = property.QualifiedType |
||||||
|
}; |
||||||
|
|
||||||
|
return base.VisitProperty(property); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFunctionDecl(Function function) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitMethodDecl(Method method) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitParameterDecl(Parameter parameter) |
||||||
|
{ |
||||||
|
return parameter.Type.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTypedefDecl(TypedefDecl typedef) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitEnumDecl(Enumeration @enum) |
||||||
|
{ |
||||||
|
Context.Return.Write("(::{0}){1}", @enum.QualifiedOriginalName, |
||||||
|
Context.Parameter.Name); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitVariableDecl(Variable variable) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitClassTemplateDecl(ClassTemplate template) |
||||||
|
{ |
||||||
|
return template.TemplatedClass.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFunctionTemplateDecl(FunctionTemplate template) |
||||||
|
{ |
||||||
|
return template.TemplatedFunction.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitMacroDefinition(MacroDefinition macro) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitNamespace(Namespace @namespace) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitEvent(Event @event) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitDelegate(Delegate @delegate) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue