Browse Source

Continue implementation of NAPI generator.

pull/1547/head
Joao Matos 5 years ago committed by João Matos
parent
commit
f79e0efacc
  1. 8
      src/Generator/Generators/C/NAPI/NAPIGenerator.cs
  2. 3
      src/Generator/Generators/C/NAPI/NAPIHelpers.cs
  3. 207
      src/Generator/Generators/C/NAPI/NAPIMarshal.cs
  4. 283
      src/Generator/Generators/C/NAPI/NAPISources.cs
  5. 96
      src/Generator/Generators/C/NAPI/NAPITypeCheckGen.cs
  6. 76
      src/Generator/Generators/C/NAPI/NAPITypeCheckPass.cs
  7. 12
      tests2/Classes.h
  8. 3
      tests2/Enums.h
  9. 5
      tests2/Overloads.h
  10. 30
      tests2/napi/test.js

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

@ -14,6 +14,14 @@ namespace CppSharp.Generators.C @@ -14,6 +14,14 @@ namespace CppSharp.Generators.C
{
}
public override bool SetupPasses()
{
var typeCheck = new NAPITypeCheck();
Context.TranslationUnitPasses.AddPass(typeCheck);
return true;
}
public override List<GeneratorOutput> Generate()
{
var outputs = base.Generate();

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

@ -127,6 +127,9 @@ namespace CppSharp.Generators.Cpp @@ -127,6 +127,9 @@ namespace CppSharp.Generators.Cpp
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))");
WriteLine(@"#define NAPI_IS_ARRAY(valuetype) (valuetype == napi_object)");
WriteLine(@"#define NAPI_IS_OBJECT(valuetype) (valuetype == napi_object)");
NewLine();
}
}

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

@ -1,7 +1,9 @@ @@ -1,7 +1,9 @@
using System;
using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using CppSharp.Generators.C;
using CppSharp.Generators.Cpp;
using CppSharp.Types;
using Delegate = CppSharp.AST.Delegate;
using Type = CppSharp.AST.Type;
@ -58,68 +60,10 @@ namespace CppSharp.Generators.NAPI @@ -58,68 +60,10 @@ namespace CppSharp.Generators.NAPI
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;
}
if (pointer.IsConstCharString())
return VisitPrimitiveType(PrimitiveType.String);
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;
}
var pointee = pointer.Pointee.Desugar();
return pointer.QualifiedPointee.Visit(this);
}
@ -220,6 +164,10 @@ namespace CppSharp.Generators.NAPI @@ -220,6 +164,10 @@ namespace CppSharp.Generators.NAPI
return true;
case PrimitiveType.String:
Context.Before.WriteLine($"napi_value {result};");
Context.Before.WriteLine($"status = {func}(env, {Context.ReturnVarName}, NAPI_AUTO_LENGTH, &{result});");
Context.Before.WriteLine("assert(status == napi_ok);");
Context.Return.Write(result);
return true;
case PrimitiveType.Null:
@ -298,26 +246,31 @@ namespace CppSharp.Generators.NAPI @@ -298,26 +246,31 @@ namespace CppSharp.Generators.NAPI
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;
}
var ctor = @class.Constructors.FirstOrDefault();
var ctorRef = $"ctor_{NAPISources.GetCIdentifier(Context.Context, ctor)}";
var ctorId = $"__{Context.ReturnVarName}_ctor";
Context.Before.WriteLine($"napi_value {ctorId};");
Context.Before.WriteLine($"status = napi_get_reference_value(env, {ctorRef}, &{ctorId});");
Context.Before.WriteLine("assert(status == napi_ok);");
Context.Before.NewLine();
var instanceId = $"__{Context.ReturnVarName}_instance";
Context.Before.WriteLine($"napi_value {instanceId};");
Context.Before.WriteLine($"status = napi_new_instance(env, {ctorId}, 0, nullptr, &{instanceId});");
Context.Before.WriteLine("assert(status == napi_ok);");
Context.Before.NewLine();
/*
var refId = $"__{Context.ReturnVarName}_ref";
Context.Before.WriteLine($"napi_ref {refId};");
var dtorId = $"dtor_{NAPISources.GetCIdentifier(Context.Context, ctor)}";
Context.Before.WriteLine($"status = napi_wrap(env, _this, {instanceId}, {dtorId}" +
$", nullptr, &{refId});");
Context.Before.WriteLine("assert(status == napi_ok);");
*/
Context.Return.Write($"{instanceId}");
WriteClassInstance(@class, instance);
return true;
}
@ -329,28 +282,6 @@ namespace CppSharp.Generators.NAPI @@ -329,28 +282,6 @@ namespace CppSharp.Generators.NAPI
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);
@ -382,10 +313,8 @@ namespace CppSharp.Generators.NAPI @@ -382,10 +313,8 @@ namespace CppSharp.Generators.NAPI
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}");
Context.ReturnVarName = $"(int32_t) {Context.ReturnVarName}";
VisitPrimitiveType(PrimitiveType.Int);
return true;
}
@ -472,6 +401,9 @@ namespace CppSharp.Generators.NAPI @@ -472,6 +401,9 @@ namespace CppSharp.Generators.NAPI
if (!VisitType(pointer, quals))
return false;
if (pointer.IsConstCharString())
return VisitPrimitiveType(PrimitiveType.String);
var pointee = pointer.Pointee.Desugar();
if (pointee is FunctionType)
@ -567,7 +499,7 @@ namespace CppSharp.Generators.NAPI @@ -567,7 +499,7 @@ namespace CppSharp.Generators.NAPI
case PrimitiveType.Float128:
return (null, null, null);
case PrimitiveType.String:
return (null, null, null);
return ("napi_get_value_string_utf8", "char*", null);
case PrimitiveType.Null:
return ("napi_get_null", null, null);
case PrimitiveType.Void:
@ -631,10 +563,23 @@ namespace CppSharp.Generators.NAPI @@ -631,10 +563,23 @@ namespace CppSharp.Generators.NAPI
return true;
case PrimitiveType.String:
var size = $"_{Context.Parameter.Name}_size";
Context.Before.WriteLine($"size_t {size};");
Context.Before.WriteLine($"status = {func}(env, args[{Context.ParameterIndex}], " +
$"nullptr, 0, &{size});");
Context.Before.NewLine();
var buf = $"{Context.Parameter.Name}";
Context.Before.WriteLine($"char* {buf} = (char*) malloc({size});");
Context.Before.WriteLine($"status = {func}(env, args[{Context.ParameterIndex}], " +
$"nullptr, 0, &{size});");
Context.Before.WriteLine("assert(status == napi_ok);");
Context.Cleanup.WriteLine($"free({buf};");
Context.Return.Write($"{buf}");
return true;
case PrimitiveType.Null:
return true;
case PrimitiveType.Void:
@ -744,42 +689,27 @@ namespace CppSharp.Generators.NAPI @@ -744,42 +689,27 @@ namespace CppSharp.Generators.NAPI
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);
typeMap.NAPIMarshalToNative(Context);
return;
}
*/
var instance = $"{Context.Parameter.Name}_instance";
Context.Before.WriteLine($"{@class.QualifiedOriginalName}* {instance};");
Context.Before.WriteLine($"status = napi_unwrap(env, _this, (void**) &{instance});");
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 isPointer = type.SkipPointerRefs().IsPointer();
var deref = isPointer ? "" : "*";
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.Parameter.Type.IsReference())
VarPrefix.Write("&");
if (isPointer)
Context.Return.Write($"{Context.Parameter.Name} ? {instance} : nullptr");
else
Context.Return.Write($"{instance}");
Context.Return.Write($"{deref}{instance}");
}
private void MarshalValueClass(Class @class)
@ -831,8 +761,11 @@ namespace CppSharp.Generators.NAPI @@ -831,8 +761,11 @@ namespace CppSharp.Generators.NAPI
public override bool VisitEnumDecl(Enumeration @enum)
{
Context.Return.Write("(::{0}){1}", @enum.QualifiedOriginalName,
Context.Parameter.Name);
VisitPrimitiveType(PrimitiveType.Int);
Context.Return.StringBuilder.Clear();
Context.Return.Write($"(::{@enum.QualifiedOriginalName}){Context.Parameter.Name}");
return true;
}

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

@ -3,19 +3,51 @@ using System.Collections.Generic; @@ -3,19 +3,51 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using CppSharp.Extensions;
using CppSharp.Generators.C;
using CppSharp.Generators.NAPI;
using CppSharp.Types.Std;
using CppSharp.Utils;
using CppSharp.Passes;
using CppSharp.Utils.FSM;
using static CppSharp.Generators.Cpp.NAPISources;
namespace CppSharp.Generators.Cpp
{
public class NAPIClassReturnCollector : TranslationUnitPass
{
public HashSet<Class> Classes = new HashSet<Class>();
public TranslationUnit TranslationUnit;
public override bool VisitFunctionDecl(Function function)
{
if (!NAPISources.ShouldGenerate(function))
return false;
var retType = function.ReturnType.Type.Desugar();
if (retType.IsPointer())
retType = retType.GetFinalPointee().Desugar();
if (!(retType is TagType tagType))
return base.VisitFunctionDecl(function);
if (!(tagType.Declaration is Class @class))
return base.VisitFunctionDecl(function);
if (@class.TranslationUnit == TranslationUnit)
return base.VisitFunctionDecl(function);
Classes.Add(@class);
return true;
}
public override bool VisitTranslationUnit(TranslationUnit unit)
{
TranslationUnit = unit;
return base.VisitTranslationUnit(unit);
}
}
public class NAPICodeGenerator : CCodeGenerator
{
public override string FileExtension => "cpp";
@ -49,17 +81,36 @@ namespace CppSharp.Generators.Cpp @@ -49,17 +81,36 @@ namespace CppSharp.Generators.Cpp
public override void VisitClassConstructors(Class @class)
{
var constructors = @class.Constructors.Where(c => !ASTUtils.CheckIgnoreMethod(c)).ToList();
var constructors = @class.Constructors.Where(c => c.IsGenerated && !c.IsCopyConstructor)
.ToList();
if (!constructors.Any())
return;
GenerateMethodGroup(constructors);
}
public static bool ShouldGenerate(Function function)
{
if (!function.IsGenerated)
return false;
if (function is Method method)
{
if (method.IsConstructor || method.IsDestructor)
return false;
if (method.IsOperator)
if (method.OperatorKind == CXXOperatorKind.Conversion ||
method.OperatorKind == CXXOperatorKind.Equal)
return false;
}
return true;
}
public override void VisitClassMethods(Class @class)
{
var methods = @class.Methods.Where(m => !ASTUtils.CheckIgnoreMethod(m) &&
!m.IsConstructor);
var methods = @class.Methods.Where(ShouldGenerate);
var uniqueMethods = methods.GroupBy(m => m.Name);
foreach (var group in uniqueMethods)
GenerateMethodGroup(group.ToList());
@ -104,6 +155,18 @@ namespace CppSharp.Generators.Cpp @@ -104,6 +155,18 @@ namespace CppSharp.Generators.Cpp
WriteInclude("NAPIHelpers.h", CInclude.IncludeKind.Quoted);
PopBlock(NewLineKind.BeforeNextBlock);
var collector = new NAPIClassReturnCollector();
TranslationUnit.Visit(collector);
PushBlock();
foreach (var @class in collector.Classes)
{
var ctor = @class.Methods.First(m => m.IsConstructor);
WriteLine($"extern napi_ref ctor_{GetCIdentifier(Context, @ctor)};");
}
PopBlock(NewLineKind.BeforeNextBlock);
var registerGen = new NAPIRegister(Context, TranslationUnits);
registerGen.Process();
WriteLine(registerGen.Generate());
@ -283,6 +346,7 @@ namespace CppSharp.Generators.Cpp @@ -283,6 +346,7 @@ namespace CppSharp.Generators.Cpp
GenerateNativeCall(group);
NewLine();
// TODO:
WriteLine("return nullptr;");
UnindentAndWriteCloseBrace();
@ -302,33 +366,13 @@ namespace CppSharp.Generators.Cpp @@ -302,33 +366,13 @@ namespace CppSharp.Generators.Cpp
}
PushBlock(BlockKind.Method);
GenerateFunctionCallback(@group.OfType<Function>().ToList());
if (method.IsConstructor)
{
WriteLine("napi_ref result;");
//WriteLine("napi_value result;");
//WriteLine("NAPI_CALL(env, napi_create_object(env, &result));");
var @class = method.Namespace as Class;
if (@group.Any(m => m.IsDefaultConstructor))
WriteLine($"{@class.QualifiedOriginalName}* obj = new {@class.QualifiedOriginalName}();");
else
WriteLine($"{@class.QualifiedOriginalName}* obj = nullptr;");
WriteLine($"status = napi_wrap(env, _this, obj, dtor_{GetCIdentifier(Context, method)}, nullptr, &result);");
WriteLine("assert(status == napi_ok);");
NewLine();
}
else
{
GenerateNativeCall(@group);
}
GenerateFunctionCallback(@group.OfType<Function>().ToList());
GenerateNativeCall(@group);
WriteLine($"printf(\"{method.QualifiedName}: %lu\\n\", argc);");
WriteLine("return _this;");
UnindentAndWriteCloseBrace();
PopBlock(NewLineKind.BeforeNextBlock);
}
@ -337,48 +381,97 @@ namespace CppSharp.Generators.Cpp @@ -337,48 +381,97 @@ namespace CppSharp.Generators.Cpp
}
public virtual void GenerateFunctionCallback(IList<Function> @group)
public virtual void GenerateFunctionCallback(List<Function> @group)
{
var function = @group.First();
WriteLine($"// {function.QualifiedName}");
var type = function is Method ? "method" : "function";
Write($"static napi_value callback_{type}_{GetCIdentifier(Context, function)}");
var callbackId = $"callback_{type}_{GetCIdentifier(Context, function)}";
Write($"static napi_value {callbackId}");
WriteLine("(napi_env env, napi_callback_info info)");
WriteOpenBraceAndIndent();
WriteLine("napi_status status;");
WriteLine("napi_value _this;");
WriteLine("size_t argc;");
WriteLine("status = napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);");
WriteLine("status = napi_get_cb_info(env, info, &argc, nullptr, &_this, nullptr);");
WriteLine("assert(status == napi_ok);");
NewLine();
WriteLine("napi_value args[argc];");
WriteLine("napi_valuetype types[argc];");
NewLine();
// Handle the zero arguments case right away if one exists.
var zeroParamsOverload = @group.SingleOrDefault(f => f.Parameters.Count == 0);
if (zeroParamsOverload != null && @group.Count > 1)
{
var index = @group.FindIndex(f => f == zeroParamsOverload);
WriteLine($"if (argc == 0)");
WriteLineIndent($"goto overload{index};");
NewLine();
}
// Check if the arguments are in the expected range.
CheckArgumentsRange(@group);
NewLine();
var needsArguments = @group.Any(f => f.Parameters.Any(p => p.IsGenerated));
if (!needsArguments)
if (needsArguments)
{
GenerateFunctionCall(function);
return;
//WriteLine("void* data;");
WriteLine("status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);");
WriteLine("assert(status == napi_ok);");
NewLine();
// Next we need to disambiguate which overload to call based on:
// 1. Number of arguments passed to the method
// 2. Type of arguments
CheckArgumentsTypes(@group);
NewLine();
}
WriteLine("napi_value args[argc], _this;");
//WriteLine("void* data;");
WriteLine("status = napi_get_cb_info(env, info, &argc, args, &_this, nullptr);");
WriteLine("assert(status == napi_ok);");
NewLine();
var method = function as Method;
if (method != null)
{
var @class = method.Namespace as Class;
if (method.IsConstructor)
{
WriteLine($"{@class.QualifiedOriginalName}* instance = nullptr;");
}
else
{
WriteLine($"{@class.QualifiedOriginalName}* instance;");
WriteLine("status = napi_unwrap(env, _this, (void**) &instance);");
}
// Next we need to disambiguate which overload to call based on:
// 1. Number of arguments passed to the method
// 2. Type of arguments
NewLine();
}
CheckArgumentsTypes(@group);
NewLine();
if (needsArguments)
{
var stateMachine = CalculateOverloadStates(@group);
CheckArgumentsOverload(@group, stateMachine);
GenerateOverloadCalls(@group, stateMachine);
}
else
{
GenerateFunctionCall(function);
}
CheckArgumentsOverload(@group);
if (method != null && method.IsConstructor)
{
WriteLine("napi_ref result;");
WriteLine($"status = napi_wrap(env, _this, instance, dtor_{GetCIdentifier(Context, method)}" +
$", nullptr, &result);");
WriteLine("assert(status == napi_ok);");
NewLine();
}
}
private void CheckArgumentsRange(IEnumerable<Function> @group)
@ -403,20 +496,15 @@ namespace CppSharp.Generators.Cpp @@ -403,20 +496,15 @@ namespace CppSharp.Generators.Cpp
private void CheckArgumentsTypes(IEnumerable<Function> @group)
{
WriteLine("napi_valuetype types[argc];");
WriteLine("for (int i = 0; i < argc; i++)");
WriteLine("for (size_t i = 0; i < argc; i++)");
WriteOpenBraceAndIndent();
WriteLine("status = napi_typeof(env, args[i], &types[i]);");
WriteLine("assert(status == napi_ok);");
UnindentAndWriteCloseBrace();
}
private void CheckArgumentsOverload(IList<Function> @group)
private void CheckArgumentsOverload(IList<Function> @group, DFSM stateMachine)
{
// First handle the easy case of zero arguments.
//WriteLine("if (argc == 0)");
var stateMachine = CalculateOverloadStates(@group);
var typeCheckStates = stateMachine.Q.Except(stateMachine.F).ToList();
var finalStates = stateMachine.F;
@ -429,9 +517,12 @@ namespace CppSharp.Generators.Cpp @@ -429,9 +517,12 @@ namespace CppSharp.Generators.Cpp
{
NewLineIfNeeded();
Unindent();
WriteLine($"typecheck{i}:");
Indent();
if (i > 0)
{
Unindent();
WriteLine($"typecheck{i}:");
Indent();
}
var state = typeCheckStates[i];
var transitions = stateMachine.Delta.Where(t => t.StartState == state).ToArray();
@ -445,7 +536,14 @@ namespace CppSharp.Generators.Cpp @@ -445,7 +536,14 @@ namespace CppSharp.Generators.Cpp
int.Parse(transition.StartState.Split(' ').Last().Split('_').Last()) + 1;
var type = uniqueTypes[(int) transition.Symbol];
var condition = GenerateTypeCheckCondition(paramIndex, type);
var typeChecker = new NAPITypeCheckGen(paramIndex);
type.Visit(typeChecker);
var condition = typeChecker.Generate();
if (string.IsNullOrWhiteSpace(condition))
throw new NotSupportedException();
WriteLine($"if ({condition})");
var nextState = typeCheckStates.Contains(transition.EndState)
@ -473,13 +571,16 @@ namespace CppSharp.Generators.Cpp @@ -473,13 +571,16 @@ namespace CppSharp.Generators.Cpp
WriteLine("return nullptr;");
NewLine();
}
private void GenerateOverloadCalls(IList<Function> @group, DFSM stateMachine)
{
// Final states.
for (var i = 0; i < finalStates.Count; i++)
for (var i = 0; i < stateMachine.F.Count; i++)
{
NewLineIfNeeded();
var function = group[i];
var function = @group[i];
WriteLine($"// {function.Signature}");
Unindent();
@ -487,9 +588,10 @@ namespace CppSharp.Generators.Cpp @@ -487,9 +588,10 @@ namespace CppSharp.Generators.Cpp
Indent();
WriteOpenBraceAndIndent();
GenerateFunctionCall(function);
UnindentAndWriteCloseBrace();
UnindentAndWriteCloseBrace();
NeedNewLine();
}
}
@ -515,8 +617,7 @@ namespace CppSharp.Generators.Cpp @@ -515,8 +617,7 @@ namespace CppSharp.Generators.Cpp
var field = property?.Field;
if (field != null)
{
Write($"((::{@class.QualifiedOriginalName}*){Helpers.InstanceIdentifier})->");
Write($"{field.OriginalName}");
Write($"instance->{field.OriginalName}");
var isGetter = property.GetMethod == method;
if (isGetter)
@ -526,14 +627,18 @@ namespace CppSharp.Generators.Cpp @@ -526,14 +627,18 @@ namespace CppSharp.Generators.Cpp
}
else
{
if (IsNativeFunctionOrStaticMethod(function))
if (method != null && method.IsConstructor)
{
Write($"instance = new {@class.QualifiedOriginalName}(");
}
else if (IsNativeFunctionOrStaticMethod(function))
{
Write($"::{function.QualifiedOriginalName}(");
}
else
{
if (function.IsNativeMethod())
Write($"((::{@class.QualifiedOriginalName}*){Helpers.InstanceIdentifier})->");
Write($"instance->");
Write($"{base.GetMethodIdentifier(function, TypePrinterContextKind.Native)}(");
}
@ -569,6 +674,7 @@ namespace CppSharp.Generators.Cpp @@ -569,6 +674,7 @@ namespace CppSharp.Generators.Cpp
if (needsReturn)
{
NewLine();
GenerateFunctionCallReturnMarshal(function);
}
}
@ -594,36 +700,6 @@ namespace CppSharp.Generators.Cpp @@ -594,36 +700,6 @@ namespace CppSharp.Generators.Cpp
WriteLine($"return {marshal.Context.Return};");
}
private string GenerateTypeCheckCondition(int paramIndex, CppSharp.AST.Type type)
{
if (type.IsPrimitiveType(out PrimitiveType primitive))
{
var condition = $"NAPI_IS_NUMBER(types[{paramIndex}])";
if (primitive == PrimitiveType.Bool)
condition =$"NAPI_IS_BOOL(types[{paramIndex}])";
else if (primitive == PrimitiveType.UInt)
condition =$"NAPI_IS_UINT32(types[{paramIndex}], args[{paramIndex}])";
else if (primitive == PrimitiveType.LongLong)
condition =$"NAPI_IS_INT64(types[{paramIndex}], args[{paramIndex}])";
else if (primitive == PrimitiveType.ULongLong)
condition =$"NAPI_IS_UINT64(types[{paramIndex}], args[{paramIndex}])";
else if (primitive.IsIntegerType())
condition =$"NAPI_IS_INT32(types[{paramIndex}], args[{paramIndex}])";
else if (primitive == PrimitiveType.Null)
condition =$"NAPI_IS_NULL(types[{paramIndex}])";
return condition;
}
throw new NotImplementedException();
}
public bool IsNativeFunctionOrStaticMethod(Function function)
{
var method = function as Method;
@ -732,7 +808,7 @@ namespace CppSharp.Generators.Cpp @@ -732,7 +808,7 @@ namespace CppSharp.Generators.Cpp
private static DFSM CalculateOverloadStates(IEnumerable<Function> group)
{
var functionGroup = group.Where(m => m.Parameters.Count != 0).ToArray();
var functionGroup = group.ToList();
// Create a set of unique parameter types.
var uniqueTypes = functionGroup.SelectMany(method => method.Parameters)
@ -743,14 +819,14 @@ namespace CppSharp.Generators.Cpp @@ -743,14 +819,14 @@ namespace CppSharp.Generators.Cpp
var Q = new List<string> {"S"};
var overloadStates = Enumerable.Range(0, functionGroup.Length).Select(i => $"F{i}")
var overloadStates = Enumerable.Range(0, functionGroup.Count).Select(i => $"F{i}")
.ToArray();
Q.AddRange(overloadStates);
var Delta = new List<Transition>();
// Setup states and transitions.
for (var methodIndex = 0; methodIndex < functionGroup.Length; methodIndex++)
for (var methodIndex = 0; methodIndex < functionGroup.Count; methodIndex++)
{
var method = functionGroup[methodIndex];
var curState = "S";
@ -777,6 +853,14 @@ namespace CppSharp.Generators.Cpp @@ -777,6 +853,14 @@ namespace CppSharp.Generators.Cpp
var NDFSM = new NDFSM(Q, Sigma, Delta, Q0, F);
var DFSM = Minimize.PowersetConstruction(NDFSM);
// Add the zero-parameters overload manually if one exists since it got optimized out.
var zeroParamsOverload = functionGroup.SingleOrDefault(f => f.Parameters.Count == 0);
if (zeroParamsOverload != null)
{
var index = functionGroup.FindIndex(f => f == zeroParamsOverload);
DFSM.F.Insert(index, $"F{index}");
}
#if OPTIMIZE_STATES
DFSM = Minimize.MinimizeDFSM(DFSM);
#endif
@ -834,6 +918,11 @@ namespace CppSharp.Generators.Cpp @@ -834,6 +918,11 @@ namespace CppSharp.Generators.Cpp
WriteLine("// { utf8name, name, method, getter, setter, value, attributes, data }");
VisitClassDeclContext(@class);
@class.FindHierarchy(c =>
{
WriteLine($"// {@class.QualifiedOriginalName}");
return VisitClassDeclContext(c);
});
NewLine();
Unindent();

96
src/Generator/Generators/C/NAPI/NAPITypeCheckGen.cs

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
using System;
using CppSharp.AST;
using CppSharp.Extensions;
namespace CppSharp.Generators.Cpp
{
public class NAPITypeCheckGen : CodeGenerator
{
private int ParameterIndex;
public override string FileExtension { get; }
public NAPITypeCheckGen(int parameterIndex) : base(null)
{
ParameterIndex = parameterIndex;
}
public override void Process()
{
throw new System.NotImplementedException();
}
public override bool VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals)
{
var condition = string.Empty;
switch (primitive)
{
case PrimitiveType.Bool:
condition = $"NAPI_IS_BOOL(types[{ParameterIndex}])";
break;
case PrimitiveType.Char:
case PrimitiveType.SChar:
case PrimitiveType.UChar:
case PrimitiveType.WideChar:
case PrimitiveType.Short:
case PrimitiveType.UShort:
case PrimitiveType.Int:
case PrimitiveType.Long:
case PrimitiveType.ULong:
condition = $"NAPI_IS_INT32(types[{ParameterIndex}], args[{ParameterIndex}])";
break;
case PrimitiveType.UInt:
condition = $"NAPI_IS_UINT32(types[{ParameterIndex}], args[{ParameterIndex}])";
break;
case PrimitiveType.LongLong:
condition = $"NAPI_IS_INT64(types[{ParameterIndex}], args[{ParameterIndex}])";
break;
case PrimitiveType.ULongLong:
condition = $"NAPI_IS_UINT64(types[{ParameterIndex}], args[{ParameterIndex}])";
break;
case PrimitiveType.Null:
condition = $"NAPI_IS_NULL(types[{ParameterIndex}])";
break;
case PrimitiveType.Half:
case PrimitiveType.Float:
case PrimitiveType.Double:
case PrimitiveType.LongDouble:
condition = $"NAPI_IS_NUMBER(types[{ParameterIndex}])";
break;
case PrimitiveType.Void:
case PrimitiveType.Char16:
case PrimitiveType.Char32:
case PrimitiveType.Int128:
case PrimitiveType.UInt128:
case PrimitiveType.Float128:
case PrimitiveType.IntPtr:
case PrimitiveType.UIntPtr:
case PrimitiveType.String:
case PrimitiveType.Decimal:
default:
throw new NotImplementedException();
}
Write(condition);
return true;
}
public override bool VisitTagType(TagType tag, TypeQualifiers quals)
{
if (tag.Declaration is Enumeration)
{
VisitPrimitiveType(PrimitiveType.Int, quals);
return true;
}
Write($"NAPI_IS_OBJECT(types[{ParameterIndex}])");
return true;
}
public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
{
Write($"NAPI_IS_ARRAY(types[{ParameterIndex}])");
return true;
}
}
}

76
src/Generator/Generators/C/NAPI/NAPITypeCheckPass.cs

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
using System;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using CppSharp.Passes;
namespace CppSharp.Generators.C
{
public class NAPITypeCheck : TranslationUnitPass
{
public override bool VisitTranslationUnit(TranslationUnit unit)
{
return unit.IsSystemHeader || base.VisitTranslationUnit(unit);
}
public override bool VisitFieldDecl(Field field)
{
return true;
}
private Declaration decl;
public override bool VisitFunctionDecl(Function function)
{
if (!function.IsGenerated)
return false;
decl = function;
return base.VisitFunctionDecl(function);
}
public override bool VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals)
{
switch (primitive)
{
case PrimitiveType.LongDouble:
case PrimitiveType.Char16:
case PrimitiveType.Char32:
case PrimitiveType.Int128:
case PrimitiveType.UInt128:
case PrimitiveType.Float128:
case PrimitiveType.IntPtr:
case PrimitiveType.UIntPtr:
case PrimitiveType.Decimal:
Diagnostics.Warning($"Unsupported decl: {decl.QualifiedName}");
return false;
default:
return true;
}
}
public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals)
{
if (pointer.IsConstCharString())
return true;
var pointee = pointer.GetFinalPointee().Desugar();
if (!pointee.TryGetClass(out _))
{
Diagnostics.Warning($"Unsupported decl: {decl.QualifiedName}");
return false;
}
return true;
}
public override bool VisitTagType(TagType tag, TypeQualifiers quals)
{
return true;
}
public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
{
return true;
}
}
}

12
tests2/Classes.h

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
class Class
{
public:
void ReturnsVoid() {}
int ReturnsInt() { return 0; }
};
class ClassWithSingleInheritance : public Class
{
public:
int ChildMethod() { return 2; }
};

3
tests2/Enums.h

@ -4,3 +4,6 @@ enum class Enum0 @@ -4,3 +4,6 @@ enum class Enum0
Item1,
Item2 = 5
};
Enum0 ReturnsEnum() { return Enum0::Item0; }
Enum0 PassAndReturnsEnum(Enum0 e) { return e; }

5
tests2/Overloads.h

@ -1,3 +1,8 @@ @@ -1,3 +1,8 @@
void Overload0() {}
int Overload1() { return 1; }
int Overload1(int) { return 2; }
int Overload(int, int) { return 1; }
int Overload(int, float) { return 2; }
int Overload(float, int) { return 3; }

30
tests2/napi/test.js

@ -23,6 +23,8 @@ function builtins() @@ -23,6 +23,8 @@ function builtins()
eq(test.PassAndReturnsSChar(ascii('b')), ascii('b'));
eq(test.PassAndReturnsUChar(ascii('c')), ascii('c'));
// TODO: add wchar_t tests
eq(test.ReturnsFloat (), 5.0);
eq(test.ReturnsDouble(), -5.0);
//eq(test.ReturnsLongDouble(), -5.0);
@ -78,15 +80,37 @@ function enums() @@ -78,15 +80,37 @@ function enums()
eq(test.Enum0.Item0, 0);
eq(test.Enum0.Item1, 1);
eq(test.Enum0.Item2, 5);
eq(test.ReturnsEnum(), test.Enum0.Item0);
eq(test.PassAndReturnsEnum(test.Enum0.Item1), test.Enum0.Item1);
}
function overloads()
{
eq(test.Overload(1, 2), 1)
eq(test.Overload0(), undefined);
eq(test.Overload1(), 1);
eq(test.Overload1(2), 2);
eq(test.Overload(1, 2), 1);
eq(test.Overload(1, 2.032), 2);
eq(test.Overload(1.23, 2), 3);
eq(test.Overload(1.23, 2), 3);
}
function classes()
{
var c = new test.Class();
eq(typeof(c), "object")
//eq(c.ReturnsVoid(), undefined)
eq(c.ReturnsInt(), 0)
var c1 = new test.ClassWithSingleInheritance();
///eq(c1.ReturnsVoid(), undefined);
eq(c1.ReturnsInt(), 0);
eq(c1.ChildMethod(), 2);
}
builtins();
enums();
overloads();
overloads();
classes();

Loading…
Cancel
Save