Browse Source

Big update to NAPI generator with initial support for basic examples.

pull/1546/head
Joao Matos 5 years ago committed by João Matos
parent
commit
f98829e700
  1. 16
      src/Generator/Extensions/FunctionExtensions.cs
  2. 21
      src/Generator/Extensions/PrimitiveTypeExtensions.cs
  3. 21
      src/Generator/Generators/C/NAPI/NAPIGenerator.cs
  4. 3
      src/Generator/Generators/C/NAPI/NAPIHeaders.cs
  5. 133
      src/Generator/Generators/C/NAPI/NAPIHelpers.cs
  6. 874
      src/Generator/Generators/C/NAPI/NAPIMarshal.cs
  7. 30
      src/Generator/Generators/C/NAPI/NAPIModule.cs
  8. 1033
      src/Generator/Generators/C/NAPI/NAPISources.cs
  9. 75
      src/Generator/Generators/CodeGenerator.cs

16
src/Generator/Extensions/FunctionExtensions.cs

@ -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;
}
}
}

21
src/Generator/Extensions/PrimitiveTypeExtensions.cs

@ -7,6 +7,27 @@ namespace CppSharp.Extensions @@ -7,6 +7,27 @@ namespace CppSharp.Extensions
{
public static class PrimitiveTypeExtensions
{
public static bool IsIntegerType(this PrimitiveType type)
{
switch (type)
{
case PrimitiveType.Char:
case PrimitiveType.SChar:
case PrimitiveType.UChar:
case PrimitiveType.Short:
case PrimitiveType.UShort:
case PrimitiveType.Int:
case PrimitiveType.UInt:
case PrimitiveType.Long:
case PrimitiveType.ULong:
case PrimitiveType.LongLong:
case PrimitiveType.ULongLong:
return true;
default:
return false;
}
}
public static (uint Width, uint Alignment) GetInfo(this PrimitiveType primitive, ParserTargetInfo targetInfo, out bool signed)
{
signed = false;

21
src/Generator/Generators/C/NAPI/NAPIGenerator.cs

@ -31,16 +31,31 @@ namespace CppSharp.Generators.C @@ -31,16 +31,31 @@ namespace CppSharp.Generators.C
}
}
var helpers = GenerateHelpers();
OnUnitGenerated(helpers);
outputs.Add(helpers);
return outputs;
}
public GeneratorOutput GenerateHelpers()
{
var helpersGen = new NAPIHelpers(Context);
helpersGen.Process();
var output = new GeneratorOutput
{
TranslationUnit = new TranslationUnit { FilePath = "NAPIHelpers.h" },
Outputs = new List<CodeGenerator> { helpersGen }
};
return output;
}
public override List<CodeGenerator> Generate(IEnumerable<TranslationUnit> units)
{
var outputs = new List<CodeGenerator>();
var header = new NAPIHeaders(Context, units);
outputs.Add(header);
var source = new NAPISources(Context, units);
outputs.Add(source);

3
src/Generator/Generators/C/NAPI/NAPIHeaders.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using CppSharp.AST;
namespace CppSharp.Generators.Cpp
@ -17,7 +18,7 @@ namespace CppSharp.Generators.Cpp @@ -17,7 +18,7 @@ namespace CppSharp.Generators.Cpp
public override void Process()
{
base.Process();
return;
}
}
}

133
src/Generator/Generators/C/NAPI/NAPIHelpers.cs

@ -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();
}
}
}

874
src/Generator/Generators/C/NAPI/NAPIMarshal.cs

@ -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();
}
}
}

30
src/Generator/Generators/C/NAPI/NAPIModule.cs

@ -20,24 +20,34 @@ namespace CppSharp.Generators.Cpp @@ -20,24 +20,34 @@ namespace CppSharp.Generators.Cpp
public override void Process()
{
var include = new CInclude()
{
File = "node/node_api.h",
Kind = CInclude.IncludeKind.Angled
};
WriteInclude(include);
WriteInclude("node/node_api.h", CInclude.IncludeKind.Angled);
WriteInclude("NAPIHelpers.h", CInclude.IncludeKind.Quoted);
NewLine();
PushBlock();
foreach (var unit in TranslationUnits)
{
var name = NAPISources.GetTranslationUnitName(unit);
WriteLine($"extern void register_{name}(napi_env env, napi_value exports);");
}
PopBlock(NewLineKind.BeforeNextBlock);
PushBlock();
WriteLine("// napi_value NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports)");
WriteLine("NAPI_MODULE_INIT()");
WriteOpenBraceAndIndent();
WriteLine("napi_value result;");
WriteLine("NAPI_CALL(env, napi_create_object(env, &result));");
foreach (var unit in TranslationUnits)
{
var name = NAPISources.GetTranslationUnitName(unit);
WriteLine($"register_{name}(env, exports);");
}
NewLine();
WriteLine("return result;");
WriteLine("return nullptr;");
UnindentAndWriteCloseBrace();
PopBlock(NewLineKind.BeforeNextBlock);
}
}
}

1033
src/Generator/Generators/C/NAPI/NAPISources.cs

File diff suppressed because it is too large Load Diff

75
src/Generator/Generators/CodeGenerator.cs

@ -261,15 +261,88 @@ namespace CppSharp.Generators @@ -261,15 +261,88 @@ namespace CppSharp.Generators
public virtual bool VisitDeclContext(DeclarationContext context)
{
foreach (var decl in context.Declarations)
{
if (decl is Function) continue;
if (decl.IsGenerated)
decl.Visit(this);
}
VisitDeclContextFunctions(context);
return true;
}
public virtual void VisitDeclContextFunctions(DeclarationContext context)
{
foreach (var decl in context.Functions)
decl.Visit(this);
}
public virtual bool VisitClassDecl(Class @class)
{
return VisitDeclContext(@class);
if (@class.IsIncomplete)
return false;
return VisitClassDeclContext(@class);
}
public virtual bool VisitClassDeclContext(Class @class)
{
if (!VisitDeclContext(@class))
return false;
foreach (var field in @class.Fields.Where(f => !ASTUtils.CheckIgnoreField(f)))
{
field.Visit(this);
}
foreach (var property in @class.Properties.Where(p => !ASTUtils.CheckIgnoreProperty(p)))
{
property.Visit(this);
}
VisitClassConstructors(@class);
VisitClassMethods(@class);
foreach (var @event in @class.Events)
{
if (!@event.IsGenerated)
continue;
@event.Visit(this);
}
foreach (var variable in @class.Variables)
{
if (!variable.IsGenerated)
continue;
if (variable.Access != AccessSpecifier.Public)
continue;
variable.Visit(this);
}
return true;
}
public virtual void VisitClassMethods(Class @class)
{
foreach (var method in @class.Methods.Where(c => !ASTUtils.CheckIgnoreMethod(c)))
{
if (method.IsConstructor)
continue;
method.Visit(this);
}
}
public virtual void VisitClassConstructors(Class @class)
{
foreach (var ctor in @class.Constructors.Where(c => !ASTUtils.CheckIgnoreMethod(c)))
{
ctor.Visit(this);
}
}
public virtual bool VisitFieldDecl(Field field)

Loading…
Cancel
Save