mirror of https://github.com/mono/CppSharp.git
12 changed files with 853 additions and 3 deletions
@ -0,0 +1,41 @@ |
|||||||
|
using System.Collections.Generic; |
||||||
|
using CppSharp.AST; |
||||||
|
using CppSharp.Generators.C; |
||||||
|
using CppSharp.Passes; |
||||||
|
|
||||||
|
namespace CppSharp.Generators.TS |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// C++ generator responsible for driving the generation of source and
|
||||||
|
/// header files.
|
||||||
|
/// </summary>
|
||||||
|
public class TSGenerator : CGenerator |
||||||
|
{ |
||||||
|
private readonly TSTypePrinter typePrinter; |
||||||
|
|
||||||
|
public TSGenerator(BindingContext context) : base(context) |
||||||
|
{ |
||||||
|
typePrinter = new TSTypePrinter(Context); |
||||||
|
} |
||||||
|
|
||||||
|
public override List<CodeGenerator> Generate(IEnumerable<TranslationUnit> units) |
||||||
|
{ |
||||||
|
var outputs = new List<CodeGenerator>(); |
||||||
|
|
||||||
|
var header = new TSSources(Context, units); |
||||||
|
outputs.Add(header); |
||||||
|
|
||||||
|
return outputs; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool SetupPasses() |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
protected override string TypePrinterDelegate(Type type) |
||||||
|
{ |
||||||
|
return type.Visit(typePrinter).ToString(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,505 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using CppSharp.AST; |
||||||
|
using CppSharp.AST.Extensions; |
||||||
|
using CppSharp.Generators.C; |
||||||
|
using CppSharp.Generators.CLI; |
||||||
|
|
||||||
|
namespace CppSharp.Generators.TS |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Generates TypeScript interface files.
|
||||||
|
/// </summary>
|
||||||
|
public class TSSources : CCodeGenerator |
||||||
|
{ |
||||||
|
public TSSources(BindingContext context, IEnumerable<TranslationUnit> units) |
||||||
|
: base(context, units) |
||||||
|
{ |
||||||
|
typePrinter = new TSTypePrinter(Context); |
||||||
|
} |
||||||
|
|
||||||
|
public override string FileExtension => "d.ts"; |
||||||
|
|
||||||
|
public override bool GenerateSemicolonAsDeclarationTerminator => false; |
||||||
|
|
||||||
|
public virtual bool GenerateNamespaces => true; |
||||||
|
|
||||||
|
public virtual bool GenerateSelectiveImports => false; |
||||||
|
|
||||||
|
public override void Process() |
||||||
|
{ |
||||||
|
GenerateFilePreamble(CommentKind.BCPL); |
||||||
|
|
||||||
|
if (GenerateSelectiveImports) |
||||||
|
GenerateImports(); |
||||||
|
else |
||||||
|
GenerateWildcardImports(); |
||||||
|
|
||||||
|
GenerateMain(); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual void GenerateMain() |
||||||
|
{ |
||||||
|
VisitTranslationUnit(TranslationUnit); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Dictionary<TranslationUnit, List<Declaration>> ComputeExternalReferences() |
||||||
|
{ |
||||||
|
var typeReferenceCollector = new CLITypeReferenceCollector(Context.TypeMaps, |
||||||
|
Context.Options); |
||||||
|
typeReferenceCollector.Process(TranslationUnit); |
||||||
|
|
||||||
|
var typeReferences = typeReferenceCollector.TypeReferences; |
||||||
|
var imports = new Dictionary<TranslationUnit, List<Declaration>>(); |
||||||
|
|
||||||
|
foreach (var typeRef in typeReferences) |
||||||
|
{ |
||||||
|
if (typeRef.Include.TranslationUnit == TranslationUnit) |
||||||
|
continue; |
||||||
|
|
||||||
|
if (typeRef.Include.File == TranslationUnit.FileName) |
||||||
|
continue; |
||||||
|
|
||||||
|
var include = typeRef.Include; |
||||||
|
var typeRefUnit = include.TranslationUnit; |
||||||
|
|
||||||
|
if (typeRefUnit != null && !typeRefUnit.IsDeclared) |
||||||
|
continue; |
||||||
|
|
||||||
|
if (!imports.ContainsKey(typeRefUnit)) |
||||||
|
imports[typeRefUnit] = new List<Declaration>(); |
||||||
|
|
||||||
|
imports[typeRefUnit].Add(typeRef.Declaration); |
||||||
|
} |
||||||
|
|
||||||
|
return imports; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual bool NeedsExternalImports() |
||||||
|
{ |
||||||
|
var imports = ComputeExternalReferences(); |
||||||
|
return imports.Keys.Count > 0; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual void GenerateWildcardImports() |
||||||
|
{ |
||||||
|
if (!NeedsExternalImports()) |
||||||
|
return; |
||||||
|
|
||||||
|
foreach (var module in Options.Modules) |
||||||
|
{ |
||||||
|
if (module == Options.SystemModule) |
||||||
|
continue; |
||||||
|
|
||||||
|
WriteLine($"import * as {module.LibraryName} from \"{module.LibraryName}\";"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public virtual void GenerateImports() |
||||||
|
{ |
||||||
|
PushBlock(BlockKind.ForwardReferences); |
||||||
|
|
||||||
|
var imports = ComputeExternalReferences(); |
||||||
|
|
||||||
|
foreach (var unit in imports) |
||||||
|
{ |
||||||
|
string unitName = unit.Key.FileNameWithoutExtension; |
||||||
|
if (Options.GenerateName != null) |
||||||
|
unitName = Options.GenerateName(unit.Key); |
||||||
|
|
||||||
|
var names = string.Join(", ", unit.Value.Select(d => d.Name)); |
||||||
|
WriteLine($"import {{{names}}} from \"{unitName}\";"); |
||||||
|
} |
||||||
|
|
||||||
|
PopBlock(NewLineKind.BeforeNextBlock); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitNamespace(Namespace @namespace) |
||||||
|
{ |
||||||
|
var isTopLevel = @namespace is TranslationUnit; |
||||||
|
var generateNamespace = GenerateNamespaces; |
||||||
|
|
||||||
|
if (generateNamespace) |
||||||
|
{ |
||||||
|
PushBlock(BlockKind.Namespace, @namespace); |
||||||
|
WriteLine(isTopLevel |
||||||
|
? $"declare module \"{@namespace.TranslationUnit.Module.LibraryName}\"" |
||||||
|
: $"namespace {@namespace.Name}"); |
||||||
|
WriteOpenBraceAndIndent(); |
||||||
|
} |
||||||
|
|
||||||
|
VisitDeclContext(@namespace); |
||||||
|
|
||||||
|
if (generateNamespace) |
||||||
|
{ |
||||||
|
UnindentAndWriteCloseBrace(); |
||||||
|
PopBlock(NewLineKind.BeforeNextBlock); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitClassDecl(Class @class) |
||||||
|
{ |
||||||
|
if (!@class.IsGenerated || @class.IsIncomplete) |
||||||
|
return false; |
||||||
|
|
||||||
|
//if (@class.IsOpaque)
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
PushBlock(BlockKind.Class, @class); |
||||||
|
|
||||||
|
GenerateDeclarationCommon(@class); |
||||||
|
|
||||||
|
GenerateClassSpecifier(@class); |
||||||
|
|
||||||
|
if (@class.IsOpaque) |
||||||
|
{ |
||||||
|
WriteLine(";"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
NewLine(); |
||||||
|
WriteLine("{"); |
||||||
|
NewLine(); |
||||||
|
|
||||||
|
// Process the nested types.
|
||||||
|
Indent(); |
||||||
|
VisitDeclContext(@class); |
||||||
|
Unindent(); |
||||||
|
|
||||||
|
GenerateClassConstructors(@class); |
||||||
|
GenerateClassProperties(@class); |
||||||
|
GenerateClassEvents(@class); |
||||||
|
GenerateClassMethods(@class.Methods); |
||||||
|
|
||||||
|
GenerateClassVariables(@class); |
||||||
|
|
||||||
|
PushBlock(BlockKind.Fields); |
||||||
|
GenerateClassFields(@class); |
||||||
|
PopBlock(); |
||||||
|
|
||||||
|
WriteLine("}"); |
||||||
|
|
||||||
|
PopBlock(NewLineKind.BeforeNextBlock); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public void GenerateClassConstructors(Class @class) |
||||||
|
{ |
||||||
|
if (@class.IsStatic) |
||||||
|
return; |
||||||
|
|
||||||
|
Indent(); |
||||||
|
|
||||||
|
var classNativeName = @class.Visit(CTypePrinter); |
||||||
|
|
||||||
|
foreach (var ctor in @class.Constructors) |
||||||
|
{ |
||||||
|
if (ASTUtils.CheckIgnoreMethod(ctor) || FunctionIgnored(ctor)) |
||||||
|
continue; |
||||||
|
|
||||||
|
ctor.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
Unindent(); |
||||||
|
} |
||||||
|
|
||||||
|
public void GenerateClassFields(Class @class) |
||||||
|
{ |
||||||
|
// Handle the case of struct (value-type) inheritance by adding the base
|
||||||
|
// properties to the managed value subtypes.
|
||||||
|
if (@class.IsValueType) |
||||||
|
{ |
||||||
|
foreach (var @base in @class.Bases.Where(b => b.IsClass && b.Class.IsDeclared)) |
||||||
|
{ |
||||||
|
GenerateClassFields(@base.Class); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Indent(); |
||||||
|
// check for value types because some of the ignored fields may back properties;
|
||||||
|
// not the case for ref types because the NativePtr pattern is used there
|
||||||
|
foreach (var field in @class.Fields.Where(f => !ASTUtils.CheckIgnoreField(f))) |
||||||
|
{ |
||||||
|
var property = @class.Properties.FirstOrDefault(p => p.Field == field); |
||||||
|
if (property != null && !property.IsInRefTypeAndBackedByValueClassField()) |
||||||
|
{ |
||||||
|
field.Visit(this); |
||||||
|
} |
||||||
|
} |
||||||
|
Unindent(); |
||||||
|
} |
||||||
|
|
||||||
|
public void GenerateClassMethods(List<Method> methods) |
||||||
|
{ |
||||||
|
if (methods.Count == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
Indent(); |
||||||
|
|
||||||
|
var @class = (Class) methods[0].Namespace; |
||||||
|
|
||||||
|
if (@class.IsValueType) |
||||||
|
foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore)) |
||||||
|
GenerateClassMethods(@base.Class.Methods.Where(m => !m.IsOperator).ToList()); |
||||||
|
|
||||||
|
var staticMethods = new List<Method>(); |
||||||
|
foreach (var method in methods) |
||||||
|
{ |
||||||
|
if (ASTUtils.CheckIgnoreMethod(method) || FunctionIgnored(method)) |
||||||
|
continue; |
||||||
|
|
||||||
|
if (method.IsConstructor) |
||||||
|
continue; |
||||||
|
|
||||||
|
if (method.IsOperator) |
||||||
|
continue; |
||||||
|
|
||||||
|
if (method.IsStatic) |
||||||
|
{ |
||||||
|
staticMethods.Add(method); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
method.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
foreach(var method in staticMethods) |
||||||
|
method.Visit(this); |
||||||
|
|
||||||
|
Unindent(); |
||||||
|
} |
||||||
|
|
||||||
|
public void GenerateClassVariables(Class @class) |
||||||
|
{ |
||||||
|
foreach (var variable in @class.Variables) |
||||||
|
{ |
||||||
|
if (!variable.IsGenerated) continue; |
||||||
|
variable.Visit(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static string GetClassTemplateParameters(Class @class) |
||||||
|
{ |
||||||
|
var @params = @class.TemplateParameters.OfType<TypeTemplateParameter>().Select( |
||||||
|
p => !string.IsNullOrEmpty(p.Constraint) ? $"{p.Name} extends {p.Constraint}" : p.Name); |
||||||
|
|
||||||
|
return $"<{string.Join(", ", @params)}>"; |
||||||
|
} |
||||||
|
|
||||||
|
public string GetBaseClassTemplateParameters(BaseClassSpecifier baseClassSpec) |
||||||
|
{ |
||||||
|
if (!(baseClassSpec.Type is TemplateSpecializationType templateSpecType)) |
||||||
|
throw new NotSupportedException(); |
||||||
|
|
||||||
|
var args = templateSpecType.Arguments.Select(arg => |
||||||
|
{ |
||||||
|
arg.Type.Type.TryGetClass(out var @class); |
||||||
|
return @class; |
||||||
|
}); |
||||||
|
|
||||||
|
return $"<{string.Join(", ", args.Select(c => c.Name))}>"; |
||||||
|
} |
||||||
|
|
||||||
|
public override void GenerateClassSpecifier(Class @class) |
||||||
|
{ |
||||||
|
if (@class.IsAbstract) |
||||||
|
Write("abstract "); |
||||||
|
|
||||||
|
Write(@class.IsInterface ? "interface" : "class"); |
||||||
|
Write($" {@class.Name}"); |
||||||
|
if (@class.IsDependent) |
||||||
|
Write(GetClassTemplateParameters(@class)); |
||||||
|
|
||||||
|
if (!@class.IsStatic && @class.HasNonIgnoredBase) |
||||||
|
{ |
||||||
|
var baseClassSpec = @class.Bases.First(bs => bs.Class == @class.BaseClass); |
||||||
|
var baseClass = baseClassSpec.Class; |
||||||
|
var needsQualifiedName = baseClass.TranslationUnit != @class.TranslationUnit; |
||||||
|
var baseClassName = needsQualifiedName ? QualifiedIdentifier(baseClass) : baseClass.Name; |
||||||
|
|
||||||
|
Write($" extends {baseClassName}"); |
||||||
|
if (baseClass.IsDependent) |
||||||
|
Write($"{GetBaseClassTemplateParameters(baseClassSpec)}"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void GenerateClassProperties(Class @class) |
||||||
|
{ |
||||||
|
// Handle the case of struct (value-type) inheritance by adding the base
|
||||||
|
// properties to the managed value subtypes.
|
||||||
|
if (@class.IsValueType) |
||||||
|
{ |
||||||
|
foreach (var @base in @class.Bases.Where(b => b.IsClass && b.Class.IsDeclared)) |
||||||
|
{ |
||||||
|
GenerateClassProperties(@base.Class); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Indent(); |
||||||
|
foreach (var prop in @class.Properties.Where( |
||||||
|
prop => !ASTUtils.CheckIgnoreProperty(prop) && !TypeIgnored(prop.Type))) |
||||||
|
{ |
||||||
|
if (prop.IsInRefTypeAndBackedByValueClassField()) |
||||||
|
{ |
||||||
|
prop.Field.Visit(this); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
prop.Visit(this); |
||||||
|
} |
||||||
|
Unindent(); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual void GenerateIndexer(Property property) |
||||||
|
{ |
||||||
|
throw new System.NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override string GenerateEnumSpecifier(Enumeration @enum) |
||||||
|
{ |
||||||
|
Write($"enum {@enum.Name}"); |
||||||
|
return @enum.Name; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFieldDecl(Field field) |
||||||
|
{ |
||||||
|
PushBlock(BlockKind.Field, field); |
||||||
|
|
||||||
|
GenerateDeclarationCommon(field); |
||||||
|
|
||||||
|
var fieldType = field.Type.Visit(CTypePrinter); |
||||||
|
WriteLine($"{fieldType} {field.Name};"); |
||||||
|
|
||||||
|
PopBlock(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitEvent(Event @event) |
||||||
|
{ |
||||||
|
PushBlock(BlockKind.Event, @event); |
||||||
|
|
||||||
|
GenerateDeclarationCommon(@event); |
||||||
|
|
||||||
|
//WriteLine($"{@event.Name}: CppSharp.Signal;");
|
||||||
|
|
||||||
|
PopBlock(NewLineKind.BeforeNextBlock); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitProperty(Property property) |
||||||
|
{ |
||||||
|
GenerateDeclarationCommon(property); |
||||||
|
|
||||||
|
return base.VisitProperty(property); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitMethodDecl(Method method) |
||||||
|
{ |
||||||
|
if (ASTUtils.CheckIgnoreMethod(method) || FunctionIgnored(method)) |
||||||
|
return false; |
||||||
|
|
||||||
|
PushBlock(BlockKind.Method, method); |
||||||
|
GenerateDeclarationCommon(method); |
||||||
|
|
||||||
|
GenerateMethodSpecifier(method); |
||||||
|
WriteLine(";"); |
||||||
|
|
||||||
|
PopBlock(NewLineKind.BeforeNextBlock); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public override void GenerateMethodSpecifier(Method method, MethodSpecifierKind? kind = null) |
||||||
|
{ |
||||||
|
if (method.IsConstructor || method.IsDestructor || |
||||||
|
method.OperatorKind == CXXOperatorKind.Conversion || |
||||||
|
method.OperatorKind == CXXOperatorKind.ExplicitConversion) |
||||||
|
{ |
||||||
|
Write($"{GetMethodIdentifier(method)}("); |
||||||
|
GenerateMethodParameters(method); |
||||||
|
Write($")"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Write($"{GetMethodIdentifier(method)}("); |
||||||
|
GenerateMethodParameters(method); |
||||||
|
|
||||||
|
var returnType = method.ReturnType.Visit(CTypePrinter); |
||||||
|
Write($"): {returnType}"); |
||||||
|
} |
||||||
|
|
||||||
|
public override string GetMethodIdentifier(Function function, |
||||||
|
TypePrinterContextKind context = TypePrinterContextKind.Managed) |
||||||
|
{ |
||||||
|
if (function is Method method) |
||||||
|
{ |
||||||
|
if (method.IsConstructor) |
||||||
|
return "constructor"; |
||||||
|
} |
||||||
|
|
||||||
|
return base.GetMethodIdentifier(function, context); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitTypedefNameDecl(TypedefNameDecl typedef) |
||||||
|
{ |
||||||
|
if (!typedef.IsGenerated) |
||||||
|
return false; |
||||||
|
|
||||||
|
var functionType = typedef.Type as FunctionType; |
||||||
|
if (functionType != null || typedef.Type.IsPointerTo(out functionType)) |
||||||
|
{ |
||||||
|
PushBlock(BlockKind.Typedef, typedef); |
||||||
|
GenerateDeclarationCommon(typedef); |
||||||
|
|
||||||
|
var @delegate = string.Format(CTypePrinter.VisitDelegate(functionType), typedef.Name); |
||||||
|
WriteLine($"{@delegate};"); |
||||||
|
|
||||||
|
PopBlock(NewLineKind.BeforeNextBlock); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitFunctionDecl(Function function) |
||||||
|
{ |
||||||
|
if (!function.IsGenerated || FunctionIgnored(function)) |
||||||
|
return false; |
||||||
|
|
||||||
|
PushBlock(BlockKind.Function, function); |
||||||
|
|
||||||
|
GenerateDeclarationCommon(function); |
||||||
|
|
||||||
|
var retType = function.ReturnType.Visit(CTypePrinter); |
||||||
|
Write($"export function {function.Name}("); |
||||||
|
|
||||||
|
GenerateMethodParameters(function); |
||||||
|
WriteLine($"): {retType};"); |
||||||
|
|
||||||
|
PopBlock(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public static bool FunctionIgnored(Function function) |
||||||
|
{ |
||||||
|
return TypeIgnored(function.ReturnType.Type) || |
||||||
|
function.Parameters.Any(param => TypeIgnored(param.Type)); |
||||||
|
} |
||||||
|
|
||||||
|
public static bool TypeIgnored(CppSharp.AST.Type type) |
||||||
|
{ |
||||||
|
var desugared = type.Desugar(); |
||||||
|
var finalType = (desugared.GetFinalPointee() ?? desugared).Desugar(); |
||||||
|
Class @class; |
||||||
|
return finalType.TryGetClass(out @class) && (@class.CompleteDeclaration == null && @class.IsIncomplete); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,166 @@ |
|||||||
|
using System; |
||||||
|
using System.Linq; |
||||||
|
using CppSharp.AST; |
||||||
|
using CppSharp.AST.Extensions; |
||||||
|
using CppSharp.Generators.C; |
||||||
|
|
||||||
|
namespace CppSharp.Generators.TS |
||||||
|
{ |
||||||
|
public class TSTypePrinter : CppTypePrinter |
||||||
|
{ |
||||||
|
public override string NamespaceSeparator => "."; |
||||||
|
|
||||||
|
public override bool HasGlobalNamespacePrefix => false; |
||||||
|
|
||||||
|
public override bool PrefixSpecialFunctions => true; |
||||||
|
|
||||||
|
public TSTypePrinter(BindingContext context) : base(context) |
||||||
|
{ |
||||||
|
PrintTypeModifiers = false; |
||||||
|
} |
||||||
|
|
||||||
|
public override string GlobalNamespace(Declaration declaration) |
||||||
|
{ |
||||||
|
return declaration.TranslationUnit?.Module?.LibraryName; |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult GetDeclName(Declaration declaration, TypePrintScopeKind scope) |
||||||
|
{ |
||||||
|
var result = base.GetDeclName(declaration, scope); |
||||||
|
result.Type = result.Type.Replace("::", NamespaceSeparator); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitArrayType(ArrayType array, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return $"{array.Type.Visit(this)}[]"; |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return VisitPrimitiveType(builtin.Type); |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return VisitPrimitiveType(primitive); |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitTypedefType(TypedefType typedef, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (typedef.Declaration.QualifiedOriginalName == "std::nullptr_t") |
||||||
|
return VisitPrimitiveType(PrimitiveType.Null); |
||||||
|
|
||||||
|
if (typedef.Declaration.Type.IsPrimitiveType()) |
||||||
|
return typedef.Declaration.Type.Visit(this); |
||||||
|
|
||||||
|
return base.VisitTypedefType(typedef, quals); |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitPrimitiveType(PrimitiveType primitive) |
||||||
|
{ |
||||||
|
switch (primitive) |
||||||
|
{ |
||||||
|
case PrimitiveType.Bool: |
||||||
|
return "boolean"; |
||||||
|
case PrimitiveType.Void: |
||||||
|
return "void"; |
||||||
|
case PrimitiveType.Char16: |
||||||
|
case PrimitiveType.Char32: |
||||||
|
case PrimitiveType.WideChar: |
||||||
|
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: |
||||||
|
return "number"; |
||||||
|
case PrimitiveType.ULongLong: |
||||||
|
return "bigint"; |
||||||
|
case PrimitiveType.Half: |
||||||
|
case PrimitiveType.Float: |
||||||
|
case PrimitiveType.Double: |
||||||
|
return "number"; |
||||||
|
case PrimitiveType.LongDouble: |
||||||
|
case PrimitiveType.Float128: |
||||||
|
case PrimitiveType.Int128: |
||||||
|
case PrimitiveType.UInt128: |
||||||
|
case PrimitiveType.IntPtr: |
||||||
|
case PrimitiveType.UIntPtr: |
||||||
|
case PrimitiveType.Null: |
||||||
|
return "null"; |
||||||
|
case PrimitiveType.String: |
||||||
|
return "string"; |
||||||
|
case PrimitiveType.Decimal: |
||||||
|
return "number"; |
||||||
|
} |
||||||
|
|
||||||
|
throw new NotSupportedException(); |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitPointerType(PointerType pointer, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (pointer.IsConstCharString()) |
||||||
|
return VisitPrimitiveType(PrimitiveType.String); |
||||||
|
|
||||||
|
return base.VisitPointerType(pointer, quals); |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitTagType(TagType tag, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (FindTypeMap(tag, out var result)) |
||||||
|
return result; |
||||||
|
|
||||||
|
return tag.Declaration.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitTemplateSpecializationType(TemplateSpecializationType template, |
||||||
|
TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (!template.Desugared.Type.TryGetClass(out var @class)) |
||||||
|
return string.Empty; |
||||||
|
|
||||||
|
var args = template.Arguments.Select(a => a.Type.Visit(this)); |
||||||
|
return $"{@class.Visit(this)}<{string.Join(", ", args)}>"; |
||||||
|
} |
||||||
|
|
||||||
|
public override TypePrinterResult VisitParameter(Parameter param, bool hasName = true) |
||||||
|
{ |
||||||
|
var oldParam = Parameter; |
||||||
|
Parameter = param; |
||||||
|
var result = param.QualifiedType.Visit(this); |
||||||
|
result.Kind = GeneratorKind.TypeScript; |
||||||
|
Parameter = oldParam; |
||||||
|
|
||||||
|
var printName = hasName && !string.IsNullOrEmpty(param.Name); |
||||||
|
if (!printName) |
||||||
|
return result; |
||||||
|
|
||||||
|
result.Name = param.Name; |
||||||
|
|
||||||
|
/* |
||||||
|
if (param.DefaultArgument != null && Options.GenerateDefaultValuesForArguments) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
var expressionPrinter = new CSharpExpressionPrinter(this); |
||||||
|
var defaultValue = expressionPrinter.VisitParameter(param); |
||||||
|
return $"{result} = {defaultValue}"; |
||||||
|
} |
||||||
|
catch (Exception) |
||||||
|
{ |
||||||
|
var function = param.Namespace as Function; |
||||||
|
Diagnostics.Warning($"Error printing default argument expression: " + |
||||||
|
$"{function.QualifiedOriginalName}({param.OriginalName})"); |
||||||
|
} |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
return $"{result}"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
set -e |
||||||
|
dir=$(cd "$(dirname "$0")"; pwd) |
||||||
|
rootdir="$dir/../.." |
||||||
|
dotnet_configuration=Release |
||||||
|
configuration=debug |
||||||
|
platform=x64 |
||||||
|
jsinterp="$rootdir/deps/quickjs/qjs-debug" |
||||||
|
|
||||||
|
red=`tput setaf 1` |
||||||
|
green=`tput setaf 2` |
||||||
|
reset=`tput sgr0` |
||||||
|
|
||||||
|
generate=true |
||||||
|
|
||||||
|
if [ $generate = true ]; then |
||||||
|
echo "${green}Generating bindings${reset}" |
||||||
|
dotnet $rootdir/bin/${dotnet_configuration}_${platform}/CppSharp.CLI.dll \ |
||||||
|
--gen=ts -I$dir/.. -I$rootdir/include -o $dir/gen -m tests $dir/../*.h |
||||||
|
fi |
||||||
|
|
||||||
|
echo "${green}Building generated binding files${reset}" |
||||||
|
#make -C $dir/gen |
||||||
|
echo |
||||||
|
|
||||||
|
echo "${green}Typechecking generated binding files with tsc${reset}" |
||||||
|
#tsc --noEmit --strict --noImplicitAny --strictNullChecks --strictFunctionTypes --noImplicitThis gen/*.d.ts |
||||||
|
|
||||||
|
# echo "${green}Executing JS tests with QuickJS${reset}" |
||||||
|
# cp $dir/gen/bin/$configuration/libtest.so $dir |
||||||
|
# #cp $dir/gen/bin/$configuration/libtest.dylib $dir |
||||||
|
# $jsinterp --std $dir/test.js |
@ -0,0 +1,73 @@ |
|||||||
|
import * as test from "tests"; |
||||||
|
|
||||||
|
|
||||||
|
function builtins() |
||||||
|
{ |
||||||
|
eq(test.ReturnsVoid(), undefined) |
||||||
|
|
||||||
|
eq(test.ReturnsBool(), true) |
||||||
|
eq(test.PassAndReturnsBool(false), false) |
||||||
|
|
||||||
|
eq(test.ReturnsNullptr(), null) |
||||||
|
//eq(test.PassAndReturnsNullptr(null), null)
|
||||||
|
eq(test.ReturnsNullptr(), null) |
||||||
|
|
||||||
|
eq(test.ReturnsChar (), ascii('a')); |
||||||
|
eq(test.ReturnsSChar(), ascii('a')); |
||||||
|
eq(test.ReturnsUChar(), ascii('a')); |
||||||
|
|
||||||
|
eq(test.PassAndReturnsChar (ascii('a')), ascii('a')); |
||||||
|
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);
|
||||||
|
|
||||||
|
//floateq(test.PassAndReturnsFloat (1.32), 1.32);
|
||||||
|
floateq(test.PassAndReturnsDouble(1.32), 1.32); |
||||||
|
//float(test.PassAndReturnsLongDouble(1.32), 1.32);
|
||||||
|
|
||||||
|
eq(test.ReturnsInt8 (), -5); |
||||||
|
eq(test.ReturnsUInt8 (), 5); |
||||||
|
eq(test.ReturnsInt16 (), -5); |
||||||
|
eq(test.ReturnsUInt16(), 5); |
||||||
|
eq(test.ReturnsInt32 (), -5); |
||||||
|
eq(test.ReturnsUInt32(), 5); |
||||||
|
eq(test.ReturnsInt64 (), -5n); |
||||||
|
eq(test.ReturnsUInt64(), 5n); |
||||||
|
|
||||||
|
const int8 = { min: -(2**7), max: (2**7) - 1 }; |
||||||
|
eq(test.PassAndReturnsInt8(int8.min), int8.min); |
||||||
|
eq(test.PassAndReturnsInt8(int8.max), int8.max); |
||||||
|
|
||||||
|
const uint8 = { min: 0, max: (2**8) - 1 }; |
||||||
|
eq(test.PassAndReturnsUInt8(uint8.min), uint8.min); |
||||||
|
eq(test.PassAndReturnsUInt8(uint8.max), uint8.max); |
||||||
|
|
||||||
|
const int16 = { min: -(2**15), max: (2**15) - 1 }; |
||||||
|
eq(test.PassAndReturnsInt16(int16.min), int16.min); |
||||||
|
eq(test.PassAndReturnsInt16(int16.max), int16.max); |
||||||
|
|
||||||
|
const uint16 = { min: 0, max: (2**16) - 1 }; |
||||||
|
eq(test.PassAndReturnsUInt16(uint16.min), uint16.min); |
||||||
|
eq(test.PassAndReturnsUInt16(uint16.max), uint16.max); |
||||||
|
|
||||||
|
const int32 = { min: -(2**31), max: (2**31) - 1 }; |
||||||
|
eq(test.PassAndReturnsInt32(int32.min), int32.min); |
||||||
|
eq(test.PassAndReturnsInt32(int32.max), int32.max); |
||||||
|
|
||||||
|
const uint32 = { min: 0, max: (2**32) - 1 }; |
||||||
|
eq(test.PassAndReturnsUInt32(uint32.min), uint32.min); |
||||||
|
eq(test.PassAndReturnsUInt32(uint32.max), uint32.max); |
||||||
|
|
||||||
|
const int64 = { min: BigInt(2**63) * -1n, max: BigInt(2**63) - 1n }; |
||||||
|
eq(test.PassAndReturnsInt64(int64.min), int64.min); |
||||||
|
eq(test.PassAndReturnsInt64(int64.max), int64.max); |
||||||
|
|
||||||
|
const uint64 = { min: BigInt(0), max: BigInt(2**64) - 1n }; |
||||||
|
eq(test.PassAndReturnsUInt64(uint64.min), uint64.min); |
||||||
|
eq(test.PassAndReturnsUInt64(uint64.max), uint64.max); |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
{ |
||||||
|
"include": ["*.ts", "./gen/*.ts"], |
||||||
|
"compilerOptions": { |
||||||
|
"alwaysStrict": true, // Parse in strict mode and emit "use strict" for each source file. |
||||||
|
"noImplicitAny": true, |
||||||
|
"strictNullChecks": true, |
||||||
|
"sourceMap": true, |
||||||
|
"typeRoots": ["./gen"] |
||||||
|
}, |
||||||
|
"typeAcquisition": { |
||||||
|
"include": ["gen"] |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue