private static void GenerateGlobal(Context context, OutputBuffer output, ClassDeclarationSyntax @class) { output.AppendLine($"public static partial class {@class.Identifier}"); output.AppendLine("{"); output.AppendLine("\tprivate static JSObject? __js;"); output.AppendLine(); output.AppendLine("\tprivate static JSObject _js"); output.AppendLine("\t{"); output.AppendLine("\t\tget"); output.AppendLine("\t\t{"); output.AppendLine("\t\t\tif (__js == null)"); output.AppendLine($"\t\t\t\t__js = (JSObject)Runtime.GetGlobalObject(nameof({@class.Identifier}));"); output.AppendLine(); output.AppendLine("\t\t\treturn __js;"); output.AppendLine("\t\t}"); output.AppendLine("\t}"); output.AppendLine(); output.IncreaseIndent(); foreach (var member in @class.Members) { GenerateInterfaceMember(context, output, member, true); } output.DecreaseIndent(); output.AppendLine("}"); output.AppendLine(); }
private static void GenerateAssemblyInitializer(Context context, OutputBuffer output, string directory) { var assemblyName = Path.GetFileName(directory.TrimEnd(Path.DirectorySeparatorChar)); output.AppendLine($"namespace {assemblyName}"); output.AppendLine("{"); output.IncreaseIndent(); output.AppendLine("public static class WasmWranglerAssemblyInitializer"); output.AppendLine("{"); output.IncreaseIndent(); output.AppendLine("public static void Initialize()"); output.AppendLine("{"); output.IncreaseIndent(); context.Wrappers.Sort(); foreach (var wrapper in context.Wrappers) { output.AppendLine($"{wrapper}.Initialize();"); } output.DecreaseIndent(); output.AppendLine("}"); output.DecreaseIndent(); output.AppendLine("}"); output.DecreaseIndent(); output.AppendLine("}"); output.AppendLine(); }
private static void WriteAssemblyInitializer(Context context, string directory) { var outputFile = Path.Combine(directory, "WasmWranglerAssemblyInitializer.g.cs"); Console.WriteLine($"Wrting AssemblyInitializer => {outputFile}"); var output = new OutputBuffer(); output.AppendLine("// <auto-generated />"); output.AppendLine("#nullable enable"); GenerateAssemblyInitializer(context, output, directory); File.WriteAllText(outputFile, output.ToString()); }
private static void GenerateInterfaceMember(Context context, OutputBuffer output, MemberDeclarationSyntax member, bool asStatic) { switch (member) { case MethodDeclarationSyntax method: GenerateMethod(context, output, method, asStatic); break; case PropertyDeclarationSyntax property: GenerateProperty(context, output, property, asStatic); break; default: throw new InvalidOperationException(CreateErrorMessage(member, $"Unexpected member: {member}")); } }
private static void WriteBinding(Context context, string inputFile) { var outputFile = Path.Combine(Path.GetDirectoryName(inputFile) !, Path.GetFileNameWithoutExtension(inputFile) + ".g.cs"); Console.WriteLine($"{inputFile} => {outputFile}"); var syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(inputFile)); var output = new OutputBuffer(); output.AppendLine("// <auto-generated />"); output.AppendLine("#nullable enable"); GenerateSyntaxNodes(context, output, syntaxTree.GetRoot().ChildNodes()); File.WriteAllText(outputFile, output.ToString()); }
private static void GenerateSyntaxNodes(Context context, OutputBuffer output, IEnumerable <SyntaxNode> nodes) { foreach (var node in nodes) { switch (node) { case ClassDeclarationSyntax classDeclarationSyntax: GenerateClass(context, output, classDeclarationSyntax); break; case NamespaceDeclarationSyntax namespaceDeclarationSyntax: output.AppendLine(); output.AppendLine($"namespace {namespaceDeclarationSyntax.Name}"); output.AppendLine("{"); output.IncreaseIndent(); context.CurrentNamespace = namespaceDeclarationSyntax.Name.ToString(); GenerateSyntaxNodes(context, output, namespaceDeclarationSyntax.Members); context.CurrentNamespace = ""; output.DecreaseIndent(); output.AppendLine("}"); break; //case InterfaceDeclarationSyntax interfaceDeclarationSyntax: // GenerateInterface(context, output, interfaceDeclarationSyntax); // break; case UsingDirectiveSyntax usingDirectiveSyntax: output.AppendLine(usingDirectiveSyntax.ToString()); break; default: throw new InvalidOperationException(CreateErrorMessage(node, $"{node.Kind()} was not expected.")); } } }
private static void GenerateClass(Context context, OutputBuffer output, ClassDeclarationSyntax @class) { var classType = ""; var implements = new List <string>(); foreach (var attribute in @class.AttributeLists.Select(x => x.ToString().Trim('[', ']'))) { switch (attribute) { case "Global": classType = "Global"; break; case "Wrapper": classType = "Wrapper"; break; } if (attribute.StartsWith("Implements(") && attribute.EndsWith(")")) { var implement = attribute.Substring("Implements(".Length, attribute.Length - "Implements(".Length - ")".Length); implements.Add(implement); } } switch (classType) { case "Global": GenerateGlobal(context, output, @class); break; case "Wrapper": GenerateWrapper(context, output, @class, implements); break; default: throw new InvalidOperationException(CreateErrorMessage(@class, $"Unknown class type: {classType}")); } }
private static void GenerateProperty(Context context, OutputBuffer output, PropertyDeclarationSyntax property, bool asStatic) { if (property.HasDocumentation()) { output.AppendLine(property.GetDocumentation(output.GetIndent())); } if (property.AccessorList == null) { throw new InvalidOperationException(CreateErrorMessage(property, $"AccessorList was expected.")); } string?wrappedType = null; foreach (var attribute in property.AttributeLists.Select(x => x.ToString().Trim('[', ']'))) { if (attribute.StartsWith("Wrap(") && attribute.EndsWith(")")) { wrappedType = attribute.Substring("Wrap(".Length, attribute.Length - "Wrap(".Length - ")".Length); } } bool canRead = property.AccessorList.Accessors.Any(x => x.Keyword.ToString() == "get"); bool canWrite = property.AccessorList.Accessors.Any(x => x.Keyword.ToString() == "set"); if (canRead && !canWrite) // readonly { output.Append($"private "); if (asStatic) { output.Append("static "); } output.AppendLine($"{property.Type}? _{property.Identifier};"); output.AppendLine(); } output.Append($"public "); if (asStatic) { output.Append("static "); } output.AppendLine($"{property.Type} {property.Identifier}"); output.AppendLine("{"); if (canRead && canWrite) { if (wrappedType == null) { output.AppendLine($"\tget => _js.GetObjectProperty<{property.Type}>(nameof({property.Identifier}));"); output.AppendLine($"\tset => _js.SetObjectProperty(nameof({property.Identifier}), value);"); } else { throw new NotImplementedException("Wrap not implemented for read / write properties."); } } else if (canRead && !canWrite) // readonly { output.Append($"\tget => _{property.Identifier} ?? (_{property.Identifier} = "); if (wrappedType == null) { output.Append($"_js.GetObjectProperty<{property.Type}>(nameof({property.Identifier}))"); } else { output.Append($"new {property.Type}(_js.GetObjectProperty<JSObject>(nameof({property.Identifier})))"); } output.AppendLine(");"); } output.AppendLine("}"); output.AppendLine(); }
private static void GenerateMethod(Context context, OutputBuffer output, MethodDeclarationSyntax method, bool asStatic) { if (method.HasDocumentation()) { output.AppendLine(method.GetDocumentation(output.GetIndent())); } output.Append($"public "); if (asStatic) { output.Append("static "); } output.Append($"{method.ReturnType} {method.Identifier}"); if (method.TypeParameterList != null) { output.Append(method.TypeParameterList.ToString()); } output.AppendLine(method.ParameterList.ToString()); if (method.ConstraintClauses.Any()) { output.AppendLine("\t" + method.ConstraintClauses.ToString()); } output.AppendLine("{"); if (method.ReturnType.ToString() != "void") { output.Append($"\tvar result = _js.Invoke(nameof({method.Identifier})"); foreach (var parameter in method.ParameterList.Parameters) { output.Append($", {parameter.Identifier}"); } output.AppendLine(");"); output.AppendLine(); output.AppendLine("\tif (result == null)"); output.AppendLine("\t\treturn null;"); output.AppendLine(); var returnType = method.ReturnType.ToString(); if (returnType.EndsWith("?")) { returnType = returnType.TrimEnd('?'); } output.AppendLine($"\treturn JSObjectWrapperFactory.Create<{returnType}>(result);"); } else { output.Append($"\t_js.Invoke(nameof({method.Identifier})"); foreach (var parameter in method.ParameterList.Parameters) { output.Append($", {parameter.Identifier}"); } output.AppendLine(");"); } output.AppendLine("}"); output.AppendLine(); }
private static void GenerateWrapper(Context context, OutputBuffer output, ClassDeclarationSyntax @class, List <string> implements) { context.Wrappers.Add(context.CurrentNamespace + "." + @class.Identifier.ToString()); output.Append($"public partial class {@class.Identifier}"); if (@class.BaseList != null || implements.Any()) { output.Append(" : "); if (@class.BaseList != null) { output.Append(string.Join(", ", @class.BaseList.Types.Select(x => x.ToString()))); } if (implements.Any()) { // If we already output the BaseList we need to add a , if (@class.BaseList != null) { output.Append(", "); } output.Append(string.Join(", ", implements)); } } output.AppendLine(); output.AppendLine("{"); output.Append("\tinternal static "); if (@class.BaseList != null) { output.Append("new "); } output.AppendLine($"void Initialize() {{ JSObjectWrapperFactory.RegisterFactory(typeof({@class.Identifier}), x => new {@class.Identifier}(x)); }}"); output.AppendLine(); if (@class.BaseList == null) { output.AppendLine("\tprotected readonly JSObject _js;"); output.AppendLine(); output.AppendLine($"\tinternal {@class.Identifier}(object obj)"); output.AppendLine("\t{"); output.AppendLine("\t\tif (!(obj is JSObject))"); output.AppendLine("\t\t\tthrow new WasmWranglerException($\"Expected {nameof(obj)} to be an instance of JSObject.\");"); output.AppendLine(); output.AppendLine("\t\t_js = (JSObject)obj;"); output.AppendLine("\t}"); } else { output.AppendLine($"\tinternal {@class.Identifier}(object obj) : base(obj) {{ }}"); } output.AppendLine(); output.IncreaseIndent(); foreach (var member in @class.Members) { GenerateInterfaceMember(context, output, member, false); } output.DecreaseIndent(); output.AppendLine("}"); output.AppendLine(); }
private static void GenerateInterface(Context context, OutputBuffer output, InterfaceDeclarationSyntax @interface) { }