Browse Source

Rework core implementation of QuickJS generator.

This adds more accurate overloading, default arguments handling and
events.
pull/1580/head
Joao Matos 4 years ago
parent
commit
b4c167b562
  1. 2
      src/AST/Event.cs
  2. 58
      src/Generator/Generators/C/CppDefaultValuePrinter.cs
  3. 5
      src/Generator/Generators/QuickJS/QuickJSHeaders.cs
  4. 705
      src/Generator/Generators/QuickJS/QuickJSSources.cs
  5. 129
      src/Generator/Generators/QuickJS/QuickJSTypeCheckGen.cs

2
src/AST/Event.cs

@ -15,5 +15,7 @@ namespace CppSharp.AST @@ -15,5 +15,7 @@ namespace CppSharp.AST
public List<Parameter> Parameters { get; } = new List<Parameter>();
public Declaration OriginalDeclaration { get; set; }
public int GlobalId { get; set; }
}
}

58
src/Generator/Generators/C/CppDefaultValuePrinter.cs

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
using System;
using CppSharp.AST;
namespace CppSharp.Generators.C
{
public class CppDefaultValuePrinter : CppTypePrinter
{
public CppDefaultValuePrinter(BindingContext context) : base(context)
{
}
public override TypePrinterResult VisitBuiltinType(BuiltinType builtin,
TypeQualifiers quals)
{
return VisitPrimitiveType(builtin.Type);
}
public override TypePrinterResult VisitPrimitiveType(PrimitiveType type)
{
switch (type)
{
case PrimitiveType.Bool:
return PrintFlavorKind == CppTypePrintFlavorKind.Cpp ? "false" : "0";
case PrimitiveType.WideChar:
case PrimitiveType.Char:
case PrimitiveType.SChar:
case PrimitiveType.UChar:
case PrimitiveType.Char16:
case PrimitiveType.Char32:
case PrimitiveType.Short:
case PrimitiveType.UShort:
case PrimitiveType.Int:
case PrimitiveType.UInt:
case PrimitiveType.Long:
case PrimitiveType.ULong:
case PrimitiveType.LongLong:
case PrimitiveType.ULongLong:
case PrimitiveType.Int128:
case PrimitiveType.UInt128:
case PrimitiveType.Half:
case PrimitiveType.Float:
case PrimitiveType.Double:
case PrimitiveType.LongDouble:
case PrimitiveType.Float128:
case PrimitiveType.Decimal:
return "0";
case PrimitiveType.Null:
case PrimitiveType.IntPtr:
case PrimitiveType.UIntPtr:
case PrimitiveType.String:
return PrintFlavorKind == CppTypePrintFlavorKind.Cpp ? "nullptr" : "0";
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
}
}

5
src/Generator/Generators/QuickJS/QuickJSHeaders.cs

@ -52,5 +52,10 @@ namespace CppSharp.Generators.Cpp @@ -52,5 +52,10 @@ namespace CppSharp.Generators.Cpp
{
return true;
}
public override bool VisitFieldDecl(Field field)
{
return true;
}
}
}

705
src/Generator/Generators/QuickJS/QuickJSSources.cs

@ -1,15 +1,33 @@ @@ -1,15 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using CppSharp.Generators.AST;
using CppSharp.Generators.C;
using static CppSharp.Generators.Cpp.NAPISources;
using static CppSharp.Generators.Cpp.NAPIInvokes;
using Type = CppSharp.AST.Type;
namespace CppSharp.Generators.Cpp
{
public class QuickJSCodeGenerator : NAPICodeGenerator
{
public QuickJSCodeGenerator(BindingContext context, IEnumerable<TranslationUnit> units)
: base(context, units)
{
}
public override MarshalPrinter<MarshalContext> GetMarshalManagedToNativePrinter(MarshalContext ctx)
{
return new QuickJSMarshalManagedToNativePrinter(ctx);
}
public override MarshalPrinter<MarshalContext> GetMarshalNativeToManagedPrinter(MarshalContext ctx)
{
return new QuickJSMarshalNativeToManagedPrinter(ctx);
}
}
/// <summary>
/// Generates QuickJS C/C++ source files.
/// QuickJS documentation: https://bellard.org/quickjs/
@ -42,6 +60,8 @@ namespace CppSharp.Generators.Cpp @@ -42,6 +60,8 @@ namespace CppSharp.Generators.Cpp
WriteLine("extern \"C\" {");
NewLine();
GenerateExternClassIds();
var registerGen = new QuickJSRegister(Context, TranslationUnits);
registerGen.Process();
WriteLine(registerGen.Generate());
@ -49,16 +69,88 @@ namespace CppSharp.Generators.Cpp @@ -49,16 +69,88 @@ namespace CppSharp.Generators.Cpp
PushBlock();
{
var name = GetTranslationUnitName(TranslationUnit);
WriteLine($"void register_{name}(JSContext *ctx, JSModuleDef *m, bool set)");
WriteLine($"void register_{name}(JSContext *ctx, JSModuleDef *m, bool set, int phase)");
WriteOpenBraceAndIndent();
TranslationUnit.Visit(this);
Block registerBlock;
PushBlock();
{
WriteLine("if (phase == 0)");
WriteOpenBraceAndIndent();
{
PushBlock();
VisitOptions.ClearFlags(VisitFlags.NamespaceClasses);
TranslationUnit.Visit(this);
registerBlock = PopBlock();
}
UnindentAndWriteCloseBrace();
}
var phase0Block = PopBlock(NewLineKind.IfNotEmpty);
phase0Block.CheckGenerate = () => !registerBlock.IsEmpty;
PushBlock();
{
VisitOptions.ResetFlags(VisitFlags.Default);
VisitOptions.SetFlags(VisitFlags.NamespaceClasses);
TranslationUnit.Visit(this);
}
PopBlock(NewLineKind.IfNotEmpty);
UnindentAndWriteCloseBrace();
}
PopBlock(NewLineKind.BeforeNextBlock);
NewLine();
WriteLine("} // extern \"C\"");
}
public static string SignalClassId = "classId__Signal";
private void GenerateExternClassIds()
{
PushBlock();
{
var classCollector = new RecordCollector(TranslationUnit);
TranslationUnit.Visit(classCollector);
var externClasses = new HashSet<Class>();
foreach (var record in classCollector.Declarations)
{
var @event = record.Value as Event;
Class @class;
if (@event != null)
{
foreach (var param in @event.Parameters)
{
if (!param.Type.GetFinalPointee().TryGetClass(out @class))
continue;
externClasses.Add(@class);
}
continue;
}
@class = record.Value as Class;
if (@class == null || !@class.IsGenerated)
continue;
externClasses.Add(@class);
}
foreach (var @class in externClasses)
{
WriteLine($"extern JSClassID classId_{GetCIdentifier(Context, @class)};");
}
}
WriteLine("}");
// If any of the classes have events, then also generate an external for Signal.
// TODO: Only generate if necessary and fix possible id conflict.
WriteLine($"extern JSClassID {SignalClassId};");
PopBlock(NewLineKind.BeforeNextBlock);
}
public override bool VisitClassDecl(Class @class)
@ -67,7 +159,7 @@ namespace CppSharp.Generators.Cpp @@ -67,7 +159,7 @@ namespace CppSharp.Generators.Cpp
return true;
PushBlock();
WriteLine($"register_class_{GetCIdentifier(Context, @class)}(ctx, m, set);");
WriteLine($"register_class_{GetCIdentifier(Context, @class)}(ctx, m, set, phase);");
PopBlock(NewLineKind.BeforeNextBlock);
return true;
}
@ -93,6 +185,59 @@ namespace CppSharp.Generators.Cpp @@ -93,6 +185,59 @@ namespace CppSharp.Generators.Cpp
PopBlock(NewLineKind.BeforeNextBlock);
return true;
}
public override bool VisitTypedefDecl(TypedefDecl typedef)
{
return true;
}
}
public class QuickJSClassFuncDef : NAPICodeGenerator
{
public QuickJSClassFuncDef(BindingContext context) : base(context, null)
{
}
public override void VisitClassConstructors(Class @class)
{
}
public override bool VisitEnumDecl(Enumeration @enum)
{
return true;
}
public override void GenerateMethodGroup(List<Method> @group)
{
GenerateFunctionGroup(@group.OfType<Function>().ToList());
}
public override void GenerateFunctionGroup(List<Function> @group)
{
var function = @group.FirstOrDefault();
var maxArgs = @group.Max(f => f.Parameters.Count);
var type = function is Method ? "method" : "function";
var callbackId = $"callback_{type}_{GetCIdentifier(Context, function)}";
WriteLine($"JS_CFUNC_DEF(\"{function.Name}\", {maxArgs}, {callbackId}),");
}
public override bool VisitEvent(Event @event)
{
var getterId = $"callback_event_getter_{GetCIdentifier(Context, @event)}";
WriteLine($"JS_CGETSET_DEF(\"{@event.Name}\", {getterId}, NULL),");
return true;
}
public override bool VisitFunctionTemplateDecl(FunctionTemplate template)
{
return true;
}
public void GenerateToString(Class @class)
{
var callbackId = $"callback_class_{GetCIdentifier(Context, @class)}_toString";
WriteLine($"JS_CFUNC_DEF(\"toString\", 0, {callbackId}),");
}
}
public class QuickJSRegister : NAPIRegister
@ -102,33 +247,110 @@ namespace CppSharp.Generators.Cpp @@ -102,33 +247,110 @@ namespace CppSharp.Generators.Cpp
{
}
public override MarshalPrinter<MarshalContext> GetMarshalManagedToNativePrinter(MarshalContext ctx)
{
return new QuickJSMarshalManagedToNativePrinter(ctx);
}
public override MarshalPrinter<MarshalContext> GetMarshalNativeToManagedPrinter(MarshalContext ctx)
{
return new QuickJSMarshalNativeToManagedPrinter(ctx);
}
public override bool VisitClassDecl(Class @class)
{
if (@class.IsIncomplete)
return true;
/*
if (!VisitDeclContext(@class))
return true;
PushBlock(BlockKind.InternalsClass, @class);
{
var callbacks = new QuickJSInvokes(Context);
@class.Visit(callbacks);
Write(callbacks.Generate());
WriteLine($"JSClassID classId_{GetCIdentifier(Context, @class)};");
NewLine();
GenerateClassExtraData(@class);
PushBlock();
{
var callbacks = new QuickJSInvokes(Context);
@class.Visit(callbacks);
callbacks.GenerateToString(@class);
Write(callbacks.Generate());
}
PopBlock(NewLineKind.BeforeNextBlock);
var finalizerId = $"finalizer_{GetCIdentifier(Context, @class)}";
PushBlock();
{
WriteLine($"void {finalizerId}(JSRuntime *rt, JSValue val)");
WriteOpenBraceAndIndent();
//WriteLine($"printf(\"Calling finalizer for {@class.QualifiedOriginalName}\\n\");");
if(ClassNeedsExtraData(@class))
{
// Remove the event connection from the delegate.
// var invokeId = $"event_invoke_{@event.OriginalName}";
// WriteLine($"data->instance->{@event.OriginalName}.bind(data, &{classDataId}::{invokeId});");
// NewLine();
var instanceKind = "JS_INTEROP_INSTANCE_SIGNAL_CONTEXT";
WriteLine($"JS_Interop_CleanupObject(val, {instanceKind});");
}
else
{
Write($"{@class.QualifiedOriginalName}* instance = ");
WriteLine($"({@class.QualifiedOriginalName}*) JS_GetOpaque(val, 0);");
}
UnindentAndWriteCloseBrace();
}
PopBlock(NewLineKind.BeforeNextBlock);
PushBlock();
{
WriteLine($"static JSClassDef classDef_{GetCIdentifier(Context, @class)}");
WriteOpenBraceAndIndent();
WriteLine($"\"{@class.Name}\",");
WriteLine($".finalizer = {finalizerId}");
Unindent();
WriteLine("};");
}
PopBlock(NewLineKind.BeforeNextBlock);
PushBlock();
{
WriteLine($"static JSCFunctionListEntry funcDef_{GetCIdentifier(Context, @class)}[]");
WriteOpenBraceAndIndent();
var funcGen = new QuickJSClassFuncDef(Context);
funcGen.Indent(CurrentIndentation);
funcGen.VisitClassDeclContext(@class);
funcGen.GenerateToString(@class);
Write(funcGen.Generate());
Unindent();
WriteLine("};");
}
PopBlock(NewLineKind.BeforeNextBlock);
}
PopBlock(NewLineKind.BeforeNextBlock);
*/
PushBlock(BlockKind.Class, @class);
{
Write($"static void register_class_{GetCIdentifier(Context, @class)}");
WriteLine("(JSContext *ctx, JSModuleDef *m, bool set)");
WriteLine("(JSContext *ctx, JSModuleDef *m, bool set, int phase)");
WriteOpenBraceAndIndent();
/*
var sources = new NAPIRegisterImpl(Context);
var sources = new QuickJSRegisterImpl(Context);
sources.Indent(CurrentIndentation);
@class.Visit(sources);
Write(sources.Generate());
*/
UnindentAndWriteCloseBrace();
}
@ -137,6 +359,128 @@ namespace CppSharp.Generators.Cpp @@ -137,6 +359,128 @@ namespace CppSharp.Generators.Cpp
return false;
}
public void GenerateClassExtraData(Class @class)
{
if (!ClassNeedsExtraData(@class))
return;
PushBlock();
{
var classDataId = $"data_{GetCIdentifier(Context, @class)}";
WriteLine($"struct {classDataId} : public JS_Interop_ClassData");
WriteOpenBraceAndIndent();
//WriteLine($"{@class.QualifiedOriginalName}* instance;");
foreach (var @event in @class.Events)
{
GenerateEventInvoke(@event);
}
Unindent();
WriteLine("};");
}
PopBlock(NewLineKind.BeforeNextBlock);
}
private void GenerateEventInvoke(Event @event)
{
var invokeId = $"event_invoke_{@event.OriginalName}";
PushBlock();
{
CTypePrinter.PushContext(TypePrinterContextKind.Native);
var functionType = @event.Type as FunctionType;
var retType = functionType.ReturnType.Visit(CTypePrinter);
CTypePrinter.PopContext();
Write($"{retType} {invokeId}(");
var @params = new List<string>();
foreach (var param in @event.Parameters)
{
CTypePrinter.PushContext(TypePrinterContextKind.Native);
var paramType = param.Type.Visit(CTypePrinter);
CTypePrinter.PopContext();
@params.Add($"{paramType} {param.Name}");
}
Write(string.Join(", ", @params));
WriteLine(")");
WriteOpenBraceAndIndent();
WriteLine($"JSValue event = JS_Interop_FindEvent(&events, {@event.GlobalId});");
WriteLine($"if (JS_IsUndefined(event))");
var isVoidReturn = functionType.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void);
if (isVoidReturn)
{
WriteLineIndent($"return;");
}
else
{
var defaultValuePrinter = new CppDefaultValuePrinter(Context);
var defaultValue = functionType.ReturnType.Visit(defaultValuePrinter);
WriteLineIndent($"return {defaultValue};");
}
NewLine();
// Marshal the arguments.
var marshalers = new List<MarshalPrinter<MarshalContext>>();
foreach (var param in @event.Parameters)
{
var ctx = new MarshalContext(Context, CurrentIndentation)
{
ArgName = param.Name,
ReturnVarName = param.Name,
ReturnType = param.QualifiedType,
Parameter = param
};
var marshal = GetMarshalNativeToManagedPrinter(ctx);
marshalers.Add(marshal);
param.Visit(marshal);
if (!string.IsNullOrWhiteSpace(marshal.Context.Before))
{
Write(marshal.Context.Before);
}
}
var args = marshalers.Select(m => m.Context.Return.ToString());
WriteLine($"JSValueConst argv[] = {{ { string.Join(", ", args)} }};");
WriteLine($"auto data = (JS_SignalContext*) JS_GetOpaque(event, 0);");
WriteLine($"JSValue ret = JS_Call(ctx, data->function, JS_UNDEFINED, {@event.Parameters.Count}, argv);");
WriteLine($"JS_FreeValue(ctx, ret);");
//WriteLine($"{@class.QualifiedOriginalName}* instance = data->instance;");
/*
if (!isVoidReturn)
{
CTypePrinter.PushContext(TypePrinterContextKind.Native);
var returnType = function.ReturnType.Visit(CTypePrinter);
CTypePrinter.PopContext();
Write($"{returnType} {Helpers.ReturnIdentifier} = ");
}
var @class = function.Namespace as Class;
*/
UnindentAndWriteCloseBrace();
}
PopBlock(NewLineKind.BeforeNextBlock);
}
public static bool ClassNeedsExtraData(Class @class)
{
return @class.Events.Any() ||
@class.Bases.Any(b => b.IsClass && ClassNeedsExtraData(b.Class));
}
public override void GenerateFunctionGroup(List<Function> group)
{
var function = group.First();
@ -213,6 +557,11 @@ namespace CppSharp.Generators.Cpp @@ -213,6 +557,11 @@ namespace CppSharp.Generators.Cpp
return true;
}
public override bool VisitFunctionTemplateDecl(FunctionTemplate template)
{
return true;
}
}
public class QuickJSRegisterImpl : QuickJSInvokes
@ -224,6 +573,70 @@ namespace CppSharp.Generators.Cpp @@ -224,6 +573,70 @@ namespace CppSharp.Generators.Cpp
public override bool VisitClassDecl(Class @class)
{
var ctors = @class.Constructors.Where(c => c.IsGenerated).ToArray();
var ctor = ctors.FirstOrDefault();
if (ctor == null)
return true;
var funcDef = $"funcDef_{GetCIdentifier(Context, @class)}";
var classId = $"classId_{GetCIdentifier(Context, @class)}";
var classDef = $"classDef_{GetCIdentifier(Context, @class)}";
WriteLine("if (!set)");
WriteOpenBraceAndIndent();
{
WriteLine($"JS_AddModuleExport(ctx, m, \"{ctor.Name}\");");
WriteLine("return;");
}
UnindentAndWriteCloseBrace();
NewLine();
WriteLine("if (phase == 0)");
WriteOpenBraceAndIndent();
{
WriteLine($"JS_NewClassID(&{classId});");
NewLine();
WriteLine($"JS_NewClass(JS_GetRuntime(ctx), {classId}, &{classDef});");
NewLine();
WriteLine("JSValue proto = JS_NewObject(ctx);");
WriteLine(
$"JS_SetPropertyFunctionList(ctx, proto, {funcDef}, sizeof({funcDef}) / sizeof({funcDef}[0]));");
WriteLine($"JS_SetClassProto(ctx, {classId}, proto);");
NewLine();
if (ctor.IsGenerated && !ctor.IsCopyConstructor)
{
var maxArgs = ctors.Max(m => m.Parameters.Count);
var callbackId = $"callback_method_{GetCIdentifier(Context, ctor)}";
Write($"JSValue ctor = JS_NewCFunction2(ctx,");
WriteLine($" {callbackId}, \"{ctor.Name}\", {maxArgs}, JS_CFUNC_constructor, 0);");
WriteLine($"JS_SetConstructor(ctx, ctor, proto);");
NewLine();
WriteLine($"JS_SetModuleExport(ctx, m, \"{ctor.Name}\", ctor);");
}
}
UnindentAndWriteCloseBrace();
if (@class.HasBaseClass && @class.BaseClass.IsGenerated)
{
WriteLine("else if (phase == 1)");
WriteOpenBraceAndIndent();
{
var baseClassId = $"classId_{GetCIdentifier(Context, @class.BaseClass)}";
WriteLine($"JSValue proto = JS_GetClassProto(ctx, {classId});");
WriteLine($"JSValue baseProto = JS_GetClassProto(ctx, {baseClassId});");
WriteLine("int err = JS_SetPrototype(ctx, proto, baseProto);");
WriteLine("assert(err != -1);");
WriteLine("JS_FreeValue(ctx, baseProto);");
WriteLine("JS_FreeValue(ctx, proto);");
}
UnindentAndWriteCloseBrace();
}
return true;
}
@ -263,9 +676,10 @@ namespace CppSharp.Generators.Cpp @@ -263,9 +676,10 @@ namespace CppSharp.Generators.Cpp
WriteLine("// " + item.Name);
WriteOpenBraceAndIndent();
{
var @enum = item.Namespace as Enumeration;
var ctx = new MarshalContext(Context, CurrentIndentation)
{
ArgName = item.Value.ToString(),
ArgName = @enum.GetItemValueAsString(item),
ReturnVarName = "item"
};
@ -287,6 +701,12 @@ namespace CppSharp.Generators.Cpp @@ -287,6 +701,12 @@ namespace CppSharp.Generators.Cpp
{
return true;
}
public override bool VisitEvent(Event @event)
{
Console.WriteLine(@event.QualifiedName);
return base.VisitEvent(@event);
}
}
public class QuickJSInvokes : NAPIInvokes
@ -313,34 +733,265 @@ namespace CppSharp.Generators.Cpp @@ -313,34 +733,265 @@ namespace CppSharp.Generators.Cpp
public override void GenerateFunctionGroup(List<Function> @group)
{
GenerateFunctionCallback(@group);
PushBlock(BlockKind.Function, @group);
{
GenerateFunctionCallback(@group);
}
PopBlock(NewLineKind.BeforeNextBlock);
}
public override void GenerateMethodGroup(List<Method> @group)
{
GenerateFunctionCallback(@group.OfType<Function>().ToList());
PushBlock(BlockKind.Method, @group);
{
GenerateFunctionCallback(@group.OfType<Function>().ToList());
}
PopBlock(NewLineKind.BeforeNextBlock);
}
public override void GenerateFunctionCallback(List<Function> @group)
{
var function = @group.First();
private void GenerateFunctionSignature(Function function)
{
var type = function is Method ? "method" : "function";
var callbackId = $"callback_{type}_{GetCIdentifier(Context, function)}";
PushBlock();
WriteLine($"static JSValue {callbackId}(JSContext* ctx, JSValueConst this_val,");
GenerateFunctionSignature(callbackId);
}
private void GenerateFunctionSignature(string id)
{
WriteLine($"static JSValue {id}(JSContext* ctx, JSValueConst this_val,");
WriteLineIndent("int argc, JSValueConst* argv)");
}
public override void GenerateFunctionCallback(List<Function> @group)
{
var function = @group.First();
WriteLine($"// {function.QualifiedName}");
GenerateFunctionSignature(function);
WriteOpenBraceAndIndent();
GenerateFunctionCall(function);
// Check if the arguments are in the expected range.
CheckArgumentsRange(@group);
NewLine();
var method = function as Method;
if (method != null && !method.IsStatic)
{
var @class = method.Namespace as Class;
if (method.IsConstructor)
{
WriteLine($"{@class.QualifiedOriginalName}* instance;");
}
else if(QuickJSRegister.ClassNeedsExtraData(@class))
{
var classDataId = $"data_{GetCIdentifier(Context, @class)}";
WriteLine($"auto data = ({classDataId}*) JS_GetOpaque(this_val, 0);");
WriteLine($"{@class.QualifiedOriginalName}* instance = ({@class.QualifiedOriginalName}*) data->instance;");
}
else
{
Write($"{@class.QualifiedOriginalName}* instance = ");
WriteLine($"({@class.QualifiedOriginalName}*) JS_GetOpaque(this_val, 0);");
}
NewLine();
}
// Handle the zero arguments case right away if one exists.
CheckZeroArguments(@group);
NewLineIfNeeded();
var needsReturn = !function.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void);
if (!needsReturn)
var needsArguments = @group.Any(f => f.Parameters.Any(p => p.IsGenerated));
if (needsArguments)
{
WriteLine("return JS_UNDEFINED;");
var stateMachine = CalculateOverloadStates(@group);
CheckArgumentsOverload(@group, stateMachine);
// Error state.
Unindent();
WriteLine($"error:");
Indent();
WriteLine("return JS_ThrowTypeError(ctx, \"Unsupported argument type\");");
NewLine();
GenerateOverloadCalls(@group, stateMachine);
}
else
{
GenerateFunctionCall(function);
}
if (method != null && method.IsConstructor)
{
NewLine();
Unindent();
{
WriteLine("wrap:");
}
Indent();
var @class = method.Namespace as Class;
var classId = $"classId_{GetCIdentifier(Context, @class)}";
GenerateJSClassInstance(classId);
NewLine();
var instanceKind = QuickJSRegister.ClassNeedsExtraData(@class)
? "JS_INTEROP_INSTANCE_SIGNAL_CONTEXT" : "JS_INTEROP_INSTANCE_RAW_POINTER";
WriteLine($"JS_Interop_InitObject(ctx, __obj, {instanceKind}, instance);");
var hasExternalInstanceField = @class.Fields.Any(f => f.OriginalName == "__ExternalInstance");
@class.FindHierarchy(c =>
{
hasExternalInstanceField |= c.Fields.Any(f => f.OriginalName == "__ExternalInstance");
return hasExternalInstanceField;
});
if (hasExternalInstanceField)
{
WriteLine("JSObject* __js_obj = JS_VALUE_GET_OBJ(__obj);");
WriteLine($"instance->__ExternalInstance = (void*) __js_obj;");
NewLine();
}
NewLine();
WriteLine($"return __obj;");
}
UnindentAndWriteCloseBrace();
}
public void GenerateJSClassInstance(string classId, bool lookupProtoFromInstance = true)
{
if (lookupProtoFromInstance)
{
WriteLine("JSValue proto;");
WriteLine("if (JS_IsUndefined(this_val))");
WriteLineIndent($"proto = JS_GetClassProto(ctx, {classId});");
WriteLine("else");
WriteLineIndent($"proto = JS_GetProperty(ctx, this_val, JS_ATOM_prototype);");
}
else
{
WriteLine($"JSValue proto = JS_GetClassProto(ctx, {classId});");
}
NewLine();
WriteLine("if (JS_IsException(proto))");
WriteLineIndent("return proto;");
NewLine();
WriteLine($"JSValue __obj = JS_NewObjectProtoClass(ctx, proto, {classId});");
WriteLine("JS_FreeValue(ctx, proto);");
}
public override void CheckArgumentsRange(IEnumerable<Function> @group)
{
var functions = @group as List<Function> ?? @group.ToList();
var (minArgs, maxArgs) = (functions.Min(m => m.Parameters.Count),
functions.Max(m => m.Parameters.Count));
var requiredArgs = functions.Min(f => f.Parameters.FindIndex(p => p.HasDefaultValue));
if (requiredArgs != -1)
minArgs = requiredArgs;
string rangeCheck;
if (minArgs > 0)
rangeCheck = minArgs == maxArgs ? $"argc != {minArgs}" : $"argc < {minArgs} || argc > {maxArgs}";
else
rangeCheck = $"argc > {maxArgs}";
WriteLine($"if ({rangeCheck})");
WriteLineIndent("return JS_ThrowRangeError(ctx, \"Unsupported number of arguments\");");
}
public override string GenerateTypeCheckForParameter(int paramIndex, Type type)
{
var typeChecker = new QuickJSTypeCheckGen(paramIndex);
type.Visit(typeChecker);
var condition = typeChecker.Generate();
if (string.IsNullOrWhiteSpace(condition))
throw new NotSupportedException();
return condition;
}
public override bool VisitEvent(Event @event)
{
var getterId = $"callback_event_getter_{GetCIdentifier(Context, @event)}";
PushBlock();
{
WriteLine($"JSValue {getterId}(JSContext *ctx, JSValueConst this_val)");
WriteOpenBraceAndIndent();
var @class = @event.Namespace as Class;
var classId = $"classId_{GetCIdentifier(Context, @class)}";
var classDataId = $"data_{GetCIdentifier(Context, @class)}";
WriteLine($"auto data = ({classDataId}*) JS_GetOpaque(this_val, 0);");
WriteLine($"if (data == nullptr)");
WriteLineIndent("return JS_ThrowTypeError(ctx, \"Could not find object instance\");");
NewLine();
WriteLine($"JSValue event = JS_Interop_FindEvent(&data->events, {@event.GlobalId});");
WriteLine($"if (!JS_IsUndefined(event))");
WriteLineIndent($"return JS_DupValue(ctx, event);");
NewLine();
//GenerateJSClassInstance(QuickJSSources.SignalClassId, lookupProtoFromInstance: false);
// Lookup Signal object constructor
WriteLine($"JSValue signalProto = JS_GetClassProto(ctx, {QuickJSSources.SignalClassId});");
WriteLine($"JSValue signalCtor = JS_GetProperty(ctx, signalProto, JS_ATOM_constructor);");
//WriteLine("JSValue argv[] = { JS_DupValue(ctx, this_val) };");
//WriteLine("JS_FreeValue(ctx, JS_CallConstructor2(ctx, signalCtor, __obj, 1, argv));");
WriteLine("JSValue argv[] = { this_val };");
WriteLine("JSValue __obj = JS_CallConstructor(ctx, signalCtor, 1, argv);");
WriteLine("JS_FreeValue(ctx, signalCtor);");
WriteLine("JS_FreeValue(ctx, signalProto);");
NewLine();
WriteLine($"JS_Interop_InsertEvent(&data->events, {@event.GlobalId}, JS_DupValue(ctx, __obj));");
//WriteLine($"data->events[{eventIndex}] = JS_DupValue(ctx, __obj);");
NewLine();
var invokeId = $"event_invoke_{@event.OriginalName}";
WriteLine($"(({@class.QualifiedOriginalName}*)data->instance)->{@event.OriginalName}.bind(data, &{classDataId}::{invokeId});");
NewLine();
WriteLine("return __obj;");
UnindentAndWriteCloseBrace();
}
PopBlock(NewLineKind.BeforeNextBlock);
return true;
}
public override bool VisitFunctionTemplateDecl(FunctionTemplate template)
{
return true;
}
public void GenerateToString(Class @class)
{
PushBlock();
{
var callbackId = $"callback_class_{GetCIdentifier(Context, @class)}_toString";
GenerateFunctionSignature(callbackId);
WriteOpenBraceAndIndent();
WriteLine($"return JS_NewString(ctx, \"{@class.Name}\");");
UnindentAndWriteCloseBrace();
}
PopBlock(NewLineKind.BeforeNextBlock);
}
}

129
src/Generator/Generators/QuickJS/QuickJSTypeCheckGen.cs

@ -0,0 +1,129 @@ @@ -0,0 +1,129 @@
using System;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using CppSharp.Extensions;
namespace CppSharp.Generators.Cpp
{
public class QuickJSTypeCheckGen : CodeGenerator
{
private int ParameterIndex;
public override string FileExtension { get; }
public QuickJSTypeCheckGen(int parameterIndex) : base(null)
{
ParameterIndex = parameterIndex;
}
public override void Process()
{
throw new System.NotImplementedException();
}
public override bool VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals)
{
// TODO: Use TargetInfo to check the actual width of types for the target.
var condition = string.Empty;
var arg = $"argv[{ParameterIndex}]";
switch (primitive)
{
case PrimitiveType.Bool:
condition = $"JS_IsBool({arg})";
break;
case PrimitiveType.Char:
case PrimitiveType.SChar:
condition = $"JS_IsInt8({arg})";
break;
case PrimitiveType.UChar:
condition = $"JS_IsUInt8({arg})";
break;
case PrimitiveType.WideChar:
condition = $"JS_IsUInt16({arg})";
break;
case PrimitiveType.Short:
condition = $"JS_IsInt16({arg})";
break;
case PrimitiveType.UShort:
condition = $"JS_IsUInt16({arg})";
break;
case PrimitiveType.Int:
case PrimitiveType.Long:
condition = $"JS_IsInt32({arg})";
break;
case PrimitiveType.ULong:
case PrimitiveType.UInt:
condition = $"JS_IsUInt32({arg})";
break;
case PrimitiveType.LongLong:
case PrimitiveType.ULongLong:
case PrimitiveType.Int128:
case PrimitiveType.UInt128:
condition = $"JS_IsBigInt(ctx, {arg})";
break;
case PrimitiveType.Half:
case PrimitiveType.Float:
case PrimitiveType.Double:
condition = $"JS_IsFloat({arg})";
break;
case PrimitiveType.LongDouble:
case PrimitiveType.Float128:
condition = $"JS_IsBigFloat({arg})";
break;
case PrimitiveType.String:
condition = $"JS_IsString({arg}) || JS_IsNull({arg})";
break;
case PrimitiveType.Decimal:
condition = $"JS_IsBigDecimal({arg})";
break;
case PrimitiveType.Null:
condition = $"JS_IsNull({arg})";
break;
case PrimitiveType.Void:
case PrimitiveType.Char16:
case PrimitiveType.Char32:
case PrimitiveType.IntPtr:
case PrimitiveType.UIntPtr:
default:
throw new NotImplementedException();
}
Write(condition);
return true;
}
public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals)
{
if (pointer.IsConstCharString())
return VisitPrimitiveType(PrimitiveType.String, quals);
return base.VisitPointerType(pointer, quals);
}
public override bool VisitFunctionType(FunctionType function, TypeQualifiers quals)
{
Write($"JS_IsFunction(ctx, argv[{ParameterIndex}])");
return true;
}
public override bool VisitTagType(TagType tag, TypeQualifiers quals)
{
if (tag.Declaration is Enumeration)
{
VisitPrimitiveType(PrimitiveType.Int, quals);
return true;
}
var arg = $"argv[{ParameterIndex}]";
Write($"JS_IsObject({arg}) || JS_IsNull({arg})");
return true;
}
public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
{
Write($"JS_IsArray(ctx, argv[{ParameterIndex}])");
return true;
}
}
}
Loading…
Cancel
Save