mirror of https://github.com/mono/CppSharp.git
11 changed files with 1219 additions and 0 deletions
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
set -ex |
||||
mono ../../build/vs2015/lib/Debug_x64/CppSharp.CLI.exe -gen=qjs -module=test -prefix=js_ test-native.h |
||||
../../build/premake5-osx --file=premake5.lua gmake |
||||
make clean |
||||
make |
||||
|
||||
cp gen/bin/release/libtest.dylib . |
||||
../../deps/QuickJS/qjs test.js |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
qjs_inc_dir = path.getabsolute("../../deps/QuickJS") |
||||
qjs_lib_dir = path.getabsolute("../../deps/QuickJS") |
||||
|
||||
workspace "qjs" |
||||
configurations { "release" } |
||||
|
||||
project "test-native" |
||||
kind "StaticLib" |
||||
files { "*-native.cpp" } |
||||
|
||||
include "gen/js_premake5.lua" |
||||
|
||||
project "test" |
||||
includedirs { "." } |
||||
links { "test-native" } |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
#include "test-native.h" |
||||
|
||||
extern "C" int plus(int a, int b) |
||||
{ |
||||
return a + b; |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
extern "C" int plus(int a, int b); |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
import { plus } from './libtest.dylib' |
||||
console.log(plus(1, 2)) |
@ -0,0 +1,150 @@
@@ -0,0 +1,150 @@
|
||||
using System.Collections.Generic; |
||||
using CppSharp.AST; |
||||
using CppSharp.Generators.Cpp; |
||||
|
||||
namespace CppSharp.Generators.C |
||||
{ |
||||
/// <summary>
|
||||
/// QuickJS generator responsible for driving the generation of binding files.
|
||||
/// QuickJS documentation: https://bellard.org/quickjs/
|
||||
/// </summary>
|
||||
public class QuickJSGenerator : CppGenerator |
||||
{ |
||||
public QuickJSGenerator(BindingContext context) : base(context) |
||||
{ |
||||
} |
||||
|
||||
public override List<GeneratorOutput> Generate() |
||||
{ |
||||
var outputs = base.Generate(); |
||||
|
||||
foreach (var module in Context.Options.Modules) |
||||
{ |
||||
if (module == Context.Options.SystemModule) |
||||
continue; |
||||
|
||||
var output = GenerateModule(module); |
||||
if (output != null) |
||||
{ |
||||
OnUnitGenerated(output); |
||||
outputs.Add(output); |
||||
} |
||||
|
||||
var premake = GeneratePremake(module); |
||||
if (premake != null) |
||||
{ |
||||
OnUnitGenerated(premake); |
||||
outputs.Add(premake); |
||||
} |
||||
} |
||||
|
||||
return outputs; |
||||
} |
||||
|
||||
public override List<CodeGenerator> Generate(IEnumerable<TranslationUnit> units) |
||||
{ |
||||
var outputs = new List<CodeGenerator>(); |
||||
|
||||
var header = new QuickJSHeaders(Context, units); |
||||
outputs.Add(header); |
||||
|
||||
var source = new QuickJSSources(Context, units); |
||||
outputs.Add(source); |
||||
|
||||
return outputs; |
||||
} |
||||
|
||||
public override GeneratorOutput GenerateModule(Module module) |
||||
{ |
||||
if (module == Context.Options.SystemModule) |
||||
return null; |
||||
|
||||
var moduleGen = new QuickJSModule(Context, module); |
||||
|
||||
var output = new GeneratorOutput |
||||
{ |
||||
TranslationUnit = new TranslationUnit |
||||
{ |
||||
FilePath = $"{module.LibraryName}_qjs_module.cpp", |
||||
Module = module |
||||
}, |
||||
Outputs = new List<CodeGenerator> { moduleGen } |
||||
}; |
||||
|
||||
output.Outputs[0].Process(); |
||||
|
||||
return output; |
||||
} |
||||
|
||||
public GeneratorOutput GeneratePremake(Module module) |
||||
{ |
||||
if (module == Context.Options.SystemModule) |
||||
return null; |
||||
|
||||
var premakeGen = new QuickJSPremakeBuildGenerator(Context, module); |
||||
|
||||
var output = new GeneratorOutput |
||||
{ |
||||
TranslationUnit = new TranslationUnit |
||||
{ |
||||
FilePath = "premake5.lua", |
||||
Module = module |
||||
}, |
||||
Outputs = new List<CodeGenerator> { premakeGen } |
||||
}; |
||||
|
||||
output.Outputs[0].Process(); |
||||
|
||||
return output; |
||||
} |
||||
} |
||||
|
||||
class QuickJSPremakeBuildGenerator : CodeGenerator |
||||
{ |
||||
readonly Module module; |
||||
|
||||
public QuickJSPremakeBuildGenerator(BindingContext context, Module module) |
||||
: base(context, (TranslationUnit)null) |
||||
{ |
||||
this.module = module; |
||||
} |
||||
|
||||
public override string FileExtension => "lua"; |
||||
|
||||
public override void Process() |
||||
{ |
||||
/* |
||||
var qjsPath = @"/Users/joao/Dev/CppSharp/examples/wxSharp/QuickJS"; |
||||
WriteLine($"qjs_inc_dir = \"{qjsPath}\""); |
||||
WriteLine($"qjs_lib_dir = \"{qjsPath}\""); |
||||
|
||||
WriteLine("workspace \"qjs\""); |
||||
WriteLineIndent("configurations { \"release\" }"); |
||||
*/ |
||||
|
||||
WriteLine($"project \"{module.LibraryName}\""); |
||||
Indent(); |
||||
|
||||
WriteLine("kind \"SharedLib\""); |
||||
WriteLine("language \"C++\""); |
||||
WriteLine("files { \"*.cpp\" }"); |
||||
WriteLine("includedirs { qjs_inc_dir }"); |
||||
WriteLine("libdirs { qjs_lib_dir }"); |
||||
WriteLine("filter { \"kind:StaticLib\" }"); |
||||
WriteLineIndent("links { \"quickjs\" }"); |
||||
WriteLine("filter { \"kind:SharedLib\" }"); |
||||
WriteLineIndent("defines { \"JS_SHARED_LIBRARY\" }"); |
||||
WriteLine("filter { \"kind:SharedLib\", \"system:macosx\" }"); |
||||
WriteLineIndent("linkoptions { \"-undefined dynamic_lookup\" }"); |
||||
|
||||
Unindent(); |
||||
} |
||||
} |
||||
|
||||
/* TODO: Write a build.sh aswell. |
||||
* set -ex |
||||
../../../../../build/premake5-osx gmake |
||||
make clean |
||||
make |
||||
*/ |
||||
} |
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic; |
||||
using CppSharp.AST; |
||||
using CppSharp.Generators.C; |
||||
|
||||
namespace CppSharp.Generators.Cpp |
||||
{ |
||||
/// <summary>
|
||||
/// Generates QuickJS C/C++ header files.
|
||||
/// QuickJS documentation: https://bellard.org/quickjs/
|
||||
/// </summary>
|
||||
public class QuickJSHeaders : CppHeaders |
||||
{ |
||||
public QuickJSHeaders(BindingContext context, IEnumerable<TranslationUnit> units) |
||||
: base(context, units) |
||||
{ |
||||
CTypePrinter.PushContext(TypePrinterContextKind.Managed); |
||||
} |
||||
|
||||
public override bool ShouldGenerateNamespaces => false; |
||||
|
||||
public override void Process() |
||||
{ |
||||
GenerateFilePreamble(CommentKind.BCPL); |
||||
|
||||
PushBlock(BlockKind.Includes); |
||||
WriteLine("#pragma once"); |
||||
NewLine(); |
||||
|
||||
var include = new CInclude() |
||||
{ |
||||
File = "quickjs.h", |
||||
Kind = CInclude.IncludeKind.Angled |
||||
}; |
||||
|
||||
WriteInclude(include); |
||||
NewLine(); |
||||
PopBlock(); |
||||
|
||||
VisitNamespace(TranslationUnit); |
||||
} |
||||
|
||||
public override bool VisitFunctionDecl(Function function) |
||||
{ |
||||
Write("extern \"C\" "); |
||||
WriteLine($"JSValue js_{function.Name}(JSContext* ctx, JSValueConst this_val,"); |
||||
WriteLineIndent("int argc, JSValueConst* argv);"); |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,721 @@
@@ -0,0 +1,721 @@
|
||||
using System; |
||||
using CppSharp.AST; |
||||
using CppSharp.AST.Extensions; |
||||
using CppSharp.Generators.C; |
||||
using CppSharp.Generators.CLI; |
||||
using CppSharp.Types; |
||||
using Delegate = CppSharp.AST.Delegate; |
||||
using Type = CppSharp.AST.Type; |
||||
|
||||
namespace CppSharp.Generators.Cpp |
||||
{ |
||||
public class QuickJSMarshalNativeToManagedPrinter : MarshalPrinter<MarshalContext> |
||||
{ |
||||
public QuickJSMarshalNativeToManagedPrinter(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.MarshalToManaged(GeneratorKind.QuickJS, 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 bool VisitPrimitiveType(PrimitiveType primitive) |
||||
{ |
||||
var retName = Generator.GeneratedIdentifier(Context.ArgName); |
||||
Context.Before.Write($"JSValue {retName} = "); |
||||
|
||||
switch (primitive) |
||||
{ |
||||
case PrimitiveType.Void: |
||||
return true; |
||||
case PrimitiveType.Bool: |
||||
case PrimitiveType.Char: |
||||
case PrimitiveType.Char16: |
||||
case PrimitiveType.WideChar: |
||||
case PrimitiveType.SChar: |
||||
case PrimitiveType.UChar: |
||||
case PrimitiveType.Short: |
||||
case PrimitiveType.UShort: |
||||
case PrimitiveType.Int: |
||||
case PrimitiveType.UInt: |
||||
Context.Before.WriteLine($"JS_NewInt32(ctx, {Context.ArgName});"); |
||||
break; |
||||
case PrimitiveType.Long: |
||||
case PrimitiveType.ULong: |
||||
case PrimitiveType.LongLong: |
||||
case PrimitiveType.ULongLong: |
||||
case PrimitiveType.Float: |
||||
case PrimitiveType.Double: |
||||
case PrimitiveType.LongDouble: |
||||
case PrimitiveType.Null: |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
Context.Return.Write(retName); |
||||
return true; |
||||
} |
||||
|
||||
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.MarshalToManaged(GeneratorKind.QuickJS, 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.MarshalToManaged(GeneratorKind.QuickJS, 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 QuickJSMarshalManagedToNativePrinter : MarshalPrinter<MarshalContext> |
||||
{ |
||||
public readonly TextGenerator VarPrefix; |
||||
public readonly TextGenerator ArgumentPrefix; |
||||
|
||||
public QuickJSMarshalManagedToNativePrinter(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.MarshalToNative(GeneratorKind.QuickJS, 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 bool VisitPrimitiveType(PrimitiveType primitive) |
||||
{ |
||||
var typePrinter = new CppTypePrinter(Context.Context); |
||||
var type = typePrinter.VisitPrimitiveType(primitive); |
||||
|
||||
Context.Before.WriteLine($"{type} {Context.ArgName};"); |
||||
|
||||
switch (primitive) |
||||
{ |
||||
case PrimitiveType.Void: |
||||
return true; |
||||
case PrimitiveType.Bool: |
||||
//JS_ToBool
|
||||
case PrimitiveType.Char: |
||||
case PrimitiveType.UChar: |
||||
case PrimitiveType.Short: |
||||
case PrimitiveType.UShort: |
||||
case PrimitiveType.Int: |
||||
Context.Before.WriteLine($"if (JS_ToInt32(ctx, &{Context.ArgName}, argv[{Context.ParameterIndex}]))"); |
||||
Context.Before.WriteLineIndent("return JS_EXCEPTION;"); |
||||
return true; |
||||
case PrimitiveType.UInt: |
||||
case PrimitiveType.Long: |
||||
case PrimitiveType.ULong: |
||||
case PrimitiveType.LongLong: |
||||
case PrimitiveType.ULongLong: |
||||
case PrimitiveType.Float: |
||||
case PrimitiveType.Double: |
||||
case PrimitiveType.WideChar: |
||||
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.MarshalToNative(GeneratorKind.QuickJS, 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.MarshalToNative(GeneratorKind.QuickJS, 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.MarshalToNative(GeneratorKind.QuickJS, 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(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,141 @@
@@ -0,0 +1,141 @@
|
||||
using System.IO; |
||||
using CppSharp.AST; |
||||
using CppSharp.Generators.C; |
||||
|
||||
namespace CppSharp.Generators.Cpp |
||||
{ |
||||
/// <summary>
|
||||
/// Generates QuickJS C/C++ module init files.
|
||||
/// QuickJS documentation: https://bellard.org/quickjs/
|
||||
/// </summary>
|
||||
public class QuickJSModule : CCodeGenerator |
||||
{ |
||||
readonly Module module; |
||||
|
||||
public QuickJSModule(BindingContext context, Module module) |
||||
: base(context, module.Units.GetGenerated()) |
||||
{ |
||||
this.module = module; |
||||
CTypePrinter.PushContext(TypePrinterContextKind.Managed); |
||||
} |
||||
|
||||
public override string FileExtension { get; } = "cpp"; |
||||
|
||||
public override void Process() |
||||
{ |
||||
GenerateFilePreamble(CommentKind.BCPL); |
||||
|
||||
PushBlock(BlockKind.Includes); |
||||
|
||||
WriteInclude(new CInclude() |
||||
{ |
||||
File = "quickjs.h", |
||||
Kind = CInclude.IncludeKind.Angled |
||||
}); |
||||
|
||||
foreach (var unit in TranslationUnits) |
||||
{ |
||||
WriteInclude(new CInclude() |
||||
{ |
||||
File = GetIncludeFileName(Context, unit), |
||||
Kind = CInclude.IncludeKind.Quoted |
||||
}); |
||||
} |
||||
|
||||
NewLine(); |
||||
PopBlock(); |
||||
|
||||
WriteLine("#define countof(x) (sizeof(x) / sizeof((x)[0]))"); |
||||
NewLine(); |
||||
|
||||
var moduleName = module.LibraryName; |
||||
|
||||
// Generate JS module function list.
|
||||
WriteLine($"static const JSCFunctionListEntry js_{moduleName}_funcs[] ="); |
||||
WriteOpenBraceAndIndent(); |
||||
|
||||
// Foreach translation unit, write the generated functions.
|
||||
foreach (var unit in TranslationUnits) |
||||
{ |
||||
var functionPrinter = new QuickJSModuleFunctionPrinter(Context); |
||||
functionPrinter.Indent(CurrentIndentation); |
||||
unit.Visit(functionPrinter); |
||||
|
||||
Write(functionPrinter.Generate()); |
||||
} |
||||
|
||||
Unindent(); |
||||
WriteLine("};"); |
||||
NewLine(); |
||||
|
||||
// Generate init function.
|
||||
WriteLine($"static int js_{moduleName}_init(JSContext* ctx, JSModuleDef* m)"); |
||||
WriteOpenBraceAndIndent(); |
||||
|
||||
WriteLine($"return JS_SetModuleExportList(ctx, m, js_{moduleName}_funcs," + |
||||
$" countof(js_{moduleName}_funcs));"); |
||||
UnindentAndWriteCloseBrace(); |
||||
NewLine(); |
||||
|
||||
// Generate module initializer.
|
||||
WriteLine("#ifdef JS_SHARED_LIBRARY"); |
||||
WriteLine("#define JS_INIT_MODULE js_init_module"); |
||||
WriteLine("#else"); |
||||
WriteLine($"#define JS_INIT_MODULE js_init_module_{moduleName}"); |
||||
WriteLine("#endif"); |
||||
NewLine(); |
||||
|
||||
Write("extern \"C\" "); |
||||
WriteLine("JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)"); |
||||
WriteOpenBraceAndIndent(); |
||||
|
||||
WriteLine("JSModuleDef* m;"); |
||||
WriteLine($"m = JS_NewCModule(ctx, module_name, js_{moduleName}_init);"); |
||||
WriteLine("if (!m)"); |
||||
WriteLineIndent("return NULL;"); |
||||
NewLine(); |
||||
|
||||
WriteLine($"JS_AddModuleExportList(ctx, m, js_{moduleName}_funcs," + |
||||
$" countof(js_{moduleName}_funcs));"); |
||||
WriteLine("return m;"); |
||||
|
||||
UnindentAndWriteCloseBrace(); |
||||
} |
||||
|
||||
public static string GetIncludeFileName(BindingContext context, |
||||
TranslationUnit unit) |
||||
{ |
||||
string file; |
||||
if (context.Options.GenerateName != null) |
||||
file = context.Options.GenerateName(unit); |
||||
else |
||||
file = Path.GetFileNameWithoutExtension(unit.FileName) |
||||
.Replace('\\', '/'); |
||||
|
||||
return $"{file}.h"; |
||||
} |
||||
} |
||||
|
||||
public class QuickJSModuleFunctionPrinter : CCodeGenerator |
||||
{ |
||||
public QuickJSModuleFunctionPrinter(BindingContext context) |
||||
: base(context, null) |
||||
{ |
||||
} |
||||
|
||||
public override bool VisitTranslationUnit(TranslationUnit unit) |
||||
{ |
||||
WriteLine($"// {QuickJSModule.GetIncludeFileName(Context, unit)}"); |
||||
|
||||
return base.VisitTranslationUnit(unit); |
||||
} |
||||
|
||||
public override bool VisitFunctionDecl(Function function) |
||||
{ |
||||
WriteLine($"JS_CFUNC_DEF(\"{function.Name}\"," + |
||||
$" {function.Parameters.Count}, js_{function.Name}),"); |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using CppSharp.AST; |
||||
using CppSharp.Generators.C; |
||||
|
||||
namespace CppSharp.Generators.Cpp |
||||
{ |
||||
/// <summary>
|
||||
/// Generates QuickJS C/C++ source files.
|
||||
/// QuickJS documentation: https://bellard.org/quickjs/
|
||||
/// </summary>
|
||||
public class QuickJSSources : CppSources |
||||
{ |
||||
public QuickJSSources(BindingContext context, IEnumerable<TranslationUnit> units) |
||||
: base(context, units) |
||||
{ |
||||
CTypePrinter.PushContext(TypePrinterContextKind.Managed); |
||||
} |
||||
|
||||
public override void Process() |
||||
{ |
||||
GenerateFilePreamble(CommentKind.BCPL); |
||||
|
||||
PushBlock(BlockKind.Includes); |
||||
|
||||
WriteInclude(new CInclude() |
||||
{ |
||||
File = "quickjs.h", |
||||
Kind = CInclude.IncludeKind.Angled |
||||
}); |
||||
|
||||
foreach (var unit in TranslationUnits) |
||||
{ |
||||
WriteInclude(new CInclude() |
||||
{ |
||||
File = unit.FileName, |
||||
Kind = CInclude.IncludeKind.Quoted |
||||
}); |
||||
} |
||||
|
||||
NewLine(); |
||||
PopBlock(); |
||||
|
||||
VisitNamespace(TranslationUnit); |
||||
} |
||||
|
||||
public override ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex, |
||||
Function function = null) |
||||
{ |
||||
var paramMarshal = new ParamMarshal { Name = param.Name, Param = param }; |
||||
|
||||
var argName = Generator.GeneratedIdentifier(param.Name); |
||||
|
||||
Parameter effectiveParam = param; |
||||
var isRef = param.IsOut || param.IsInOut; |
||||
var paramType = param.Type; |
||||
|
||||
var ctx = new MarshalContext(Context, CurrentIndentation) |
||||
{ |
||||
Parameter = effectiveParam, |
||||
ParameterIndex = paramIndex, |
||||
ArgName = argName, |
||||
Function = function |
||||
}; |
||||
|
||||
var marshal = new QuickJSMarshalManagedToNativePrinter(ctx); |
||||
effectiveParam.Visit(marshal); |
||||
|
||||
if (string.IsNullOrEmpty(marshal.Context.Before)) |
||||
throw new Exception($"Cannot marshal argument of function '{function.QualifiedOriginalName}'"); |
||||
|
||||
if (!string.IsNullOrWhiteSpace(marshal.Context.Before)) |
||||
Write(marshal.Context.Before); |
||||
|
||||
NewLine(); |
||||
|
||||
paramMarshal.Name = argName; |
||||
return paramMarshal; |
||||
} |
||||
|
||||
public override void GenerateFunctionCallReturnMarshal(Function function) |
||||
{ |
||||
var ctx = new MarshalContext(Context, CurrentIndentation) |
||||
{ |
||||
ArgName = Helpers.ReturnIdentifier, |
||||
ReturnVarName = Helpers.ReturnIdentifier, |
||||
ReturnType = function.ReturnType |
||||
}; |
||||
|
||||
var marshal = new QuickJSMarshalNativeToManagedPrinter(ctx); |
||||
function.ReturnType.Visit(marshal); |
||||
|
||||
if (!string.IsNullOrWhiteSpace(marshal.Context.Before)) |
||||
Write(marshal.Context.Before); |
||||
|
||||
NewLine(); |
||||
WriteLine($"return {marshal.Context.Return};"); |
||||
} |
||||
|
||||
public override bool VisitFunctionDecl(Function function) |
||||
{ |
||||
Write("extern \"C\" "); |
||||
WriteLine($"JSValue js_{function.Name}(JSContext* ctx, JSValueConst this_val,"); |
||||
WriteLineIndent("int argc, JSValueConst* argv)"); |
||||
WriteOpenBraceAndIndent(); |
||||
|
||||
GenerateFunctionCall(function); |
||||
|
||||
UnindentAndWriteCloseBrace(); |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue