mirror of https://github.com/mono/CppSharp.git
9 changed files with 2184 additions and 22 deletions
@ -0,0 +1,16 @@
@@ -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 @@
@@ -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 @@
@@ -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