Browse Source

Add initial QuickJS generator.

wip
João Matos 5 years ago
parent
commit
e05cfa33d6
  1. 8
      examples/QuickJS/build.sh
  2. 15
      examples/QuickJS/premake5.lua
  3. 6
      examples/QuickJS/test-native.cpp
  4. 1
      examples/QuickJS/test-native.h
  5. 2
      examples/QuickJS/test.js
  6. 150
      src/Generator/Generators/C/QuickJS/QuickJSGenerator.cs
  7. 51
      src/Generator/Generators/C/QuickJS/QuickJSHeaders.cs
  8. 721
      src/Generator/Generators/C/QuickJS/QuickJSMarshal.cs
  9. 141
      src/Generator/Generators/C/QuickJS/QuickJSModule.cs
  10. 115
      src/Generator/Generators/C/QuickJS/QuickJSSources.cs
  11. 9
      src/Generator/Generators/C/QuickJS/QuickJSTypePrinter.cs

8
examples/QuickJS/build.sh

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

15
examples/QuickJS/premake5.lua

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

6
examples/QuickJS/test-native.cpp

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
#include "test-native.h"
extern "C" int plus(int a, int b)
{
return a + b;
}

1
examples/QuickJS/test-native.h

@ -0,0 +1 @@ @@ -0,0 +1 @@
extern "C" int plus(int a, int b);

2
examples/QuickJS/test.js

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
import { plus } from './libtest.dylib'
console.log(plus(1, 2))

150
src/Generator/Generators/C/QuickJS/QuickJSGenerator.cs

@ -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
*/
}

51
src/Generator/Generators/C/QuickJS/QuickJSHeaders.cs

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

721
src/Generator/Generators/C/QuickJS/QuickJSMarshal.cs

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

141
src/Generator/Generators/C/QuickJS/QuickJSModule.cs

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

115
src/Generator/Generators/C/QuickJS/QuickJSSources.cs

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

9
src/Generator/Generators/C/QuickJS/QuickJSTypePrinter.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
namespace CppSharp.Generators.C
{
public class QuickJSTypePrinter : CppTypePrinter
{
public QuickJSTypePrinter(BindingContext context) : base(context)
{
}
}
}
Loading…
Cancel
Save