• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1{{/*
2 Copyright 2021 The Dawn Authors
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/}}
16
17{{- /*
18--------------------------------------------------------------------------------
19Template file for use with src/dawn_node/tools/cmd/idlgen/main.go to generate
20the WebGPU.cpp source file.
21
22See:
23* https://github.com/ben-clayton/webidlparser/blob/main/ast/ast.go for the AST
24  types used by this template
25* src/dawn_node/tools/cmd/idlgen/main.go for additional structures and functions
26  used by this template
27* https://golang.org/pkg/text/template/ for documentation on the template syntax
28--------------------------------------------------------------------------------
29*/ -}}
30
31{{- Include "WebGPUCommon.tmpl" -}}
32
33#include "src/dawn_node/interop/WebGPU.h"
34
35#include <unordered_map>
36
37#include "src/dawn_node/utils/Debug.h"
38
39namespace wgpu {
40namespace interop {
41
42namespace {
43
44{{template "Wrappers" $}}
45
46}  // namespace
47
48{{ range $ := .Declarations}}
49{{-        if IsDictionary $}}{{template "Dictionary" $}}
50{{-   else if IsInterface  $}}{{template "Interface"  $}}
51{{-   else if IsEnum       $}}{{template "Enum"       $}}
52{{-   end}}
53{{- end}}
54
55
56void Initialize(Napi::Env env) {
57  auto* wrapper = Wrappers::Init(env);
58  auto global = env.Global();
59{{ range $ := .Declarations}}
60{{-   if IsInterfaceOrNamespace $}}
61  global.Set(Napi::String::New(env, "{{$.Name}}"), wrapper->{{$.Name}}_ctor.Value());
62{{-   end}}
63{{- end}}
64}
65
66}  // namespace interop
67}  // namespace wgpu
68
69
70{{- /*
71--------------------------------------------------------------------------------
72-- Wrappers emits the C++ 'Wrappers' class, which holds all the interface and
73-- namespace interop wrapper classes.
74--------------------------------------------------------------------------------
75*/ -}}
76{{- define "Wrappers"}}
77// Wrappers holds all the Napi class constructors, and Napi::ObjectWrap type
78// declarations, for each of the WebIDL interface and namespace types.
79class Wrappers {
80  Wrappers(Napi::Env env) {
81{{-   range $ := .Declarations}}
82{{-     if IsInterfaceOrNamespace $}}
83    {{$.Name}}_ctor = Napi::Persistent(W{{$.Name}}::Class(env));
84{{-     end}}
85{{-   end}}
86  }
87
88  static Wrappers* instance;
89
90public:
91{{-   range $ := .Declarations}}
92{{-     if IsInterfaceOrNamespace $}}{{template "Wrapper" $}}
93{{-     end}}
94{{-   end}}
95
96  // Allocates and constructs the Wrappers instance
97  static Wrappers* Init(Napi::Env env) {
98    instance = new Wrappers(env);
99    return instance;
100  }
101
102  // Destructs and frees the Wrappers instance
103  static void Term(Napi::Env env) {
104    delete instance;
105    instance = nullptr;
106  }
107
108  static Wrappers* For(Napi::Env env) {
109    // Currently Napi only actually supports a single Env, so there's no point
110    // maintaining a map of Env to Wrapper. Note: This might not always be true.
111    return instance;
112  }
113
114{{   range $ := .Declarations}}
115{{-     if IsInterfaceOrNamespace $}}
116  Napi::FunctionReference {{$.Name}}_ctor;
117{{-     end}}
118{{-   end}}
119};
120
121Wrappers* Wrappers::instance = nullptr;
122{{- end}}
123
124
125{{- /*
126--------------------------------------------------------------------------------
127-- Wrapper emits the C++ wrapper class for the given ast.Interface or
128-- ast.Namespace.
129-- This wrapper class inherits from Napi::ObjectWrap, which binds the lifetime
130-- of the JavaScript object to the lifetime of the wrapper class instance.
131-- If the wrapper is for an interface, the wrapper object holds a unique_ptr to
132-- the interface implementation, and delegates all exposed method calls on to
133-- the implementation.
134-- See: https://github.com/nodejs/node-addon-api/blob/main/doc/object_wrap.md
135--------------------------------------------------------------------------------
136*/ -}}
137{{- define "Wrapper"}}
138  struct W{{$.Name}} : public Napi::ObjectWrap<W{{$.Name}}> {
139{{-  if IsInterface $}}
140    std::unique_ptr<{{$.Name}}> impl;
141{{-  end}}
142    static Napi::Function Class(Napi::Env env) {
143      return DefineClass(env, "{{$.Name}}", {
144{{   if $s := SetlikeOf $}}
145        InstanceMethod("has", &W{{$.Name}}::has),
146        InstanceMethod("keys", &W{{$.Name}}::keys),
147{{-  end}}
148{{-  range $m := MethodsOf $}}
149        InstanceMethod("{{$m.Name}}", &W{{$.Name}}::{{$m.Name}}),
150{{-  end}}
151{{-  range $a := AttributesOf $}}
152        InstanceAccessor("{{$a.Name}}", &W{{$.Name}}::get{{Title $a.Name}},
153{{-    if $a.Readonly}} nullptr{{else}} &W{{$.Name}}::set{{Title $a.Name}}{{end -}}
154        ),
155{{-  end}}
156{{-  range $c := ConstantsOf $}}
157        StaticValue("{{$c.Name}}", ToJS(env, {{$.Name}}::{{$c.Name}}), napi_default_jsproperty),
158{{-  end}}
159      });
160    }
161
162    W{{$.Name}}(const Napi::CallbackInfo& info) : ObjectWrap(info) {}
163
164{{   if $s := SetlikeOf $}}
165    Napi::Value has(const Napi::CallbackInfo& info) {
166      std::tuple<{{template "Type" $s.Elem}}> args;
167      auto res = FromJS(info, args);
168      if (res) {
169          return ToJS(info.Env(), impl->has(info.Env(), std::get<0>(args)));
170      }
171      Napi::Error::New(info.Env(), res.error).ThrowAsJavaScriptException();
172      return {};
173    }
174    Napi::Value keys(const Napi::CallbackInfo& info) {
175      return ToJS(info.Env(), impl->keys(info.Env()));
176    }
177{{-  end}}
178{{-  range $m := MethodsOf $}}
179    Napi::Value {{$m.Name}}(const Napi::CallbackInfo& info) {
180      std::string error;
181{{-    range $overload_idx, $o := $m.Overloads}}
182{{- $overloaded := gt (len $m.Overloads) 1}}
183      { {{if $overloaded}}// Overload {{$overload_idx}}{{end}}
184        std::tuple<
185{{-        range $i, $p := $o.Parameters}}
186{{-          if $i}}, {{end}}
187{{-          if      $p.Init    }}DefaultedParameter<{{template "Type" $p.Type}}>
188{{-          else if $p.Optional}}std::optional<{{template "Type" $p.Type}}>
189{{-          else               }}{{template "Type" $p.Type}}
190{{-          end}}
191{{-        end}}> args;
192
193{{-        range $i, $p := $o.Parameters}}
194{{-          if $p.Init}}
195        std::get<{{$i}} /* {{$p.Name}} */>(args).default_value = {{Eval "Literal" "Value" $p.Init "Type" $p.Type}};
196{{-          end}}
197{{-        end}}
198
199        auto res = FromJS(info, args);
200        if (res) {
201          {{/* indent */}}INTEROP_LOG(
202{{-        range $i, $p := $o.Parameters}}
203{{-          if $i}}, ", {{$p.Name}}: "{{else}}"{{$p.Name}}: "{{end}}, std::get<{{$i}}>(args)
204{{-        end}});
205          {{/* indent */}}
206{{-      if not (IsUndefinedType $o.Type) }}auto result = {{end -}}
207          impl->{{$o.Name}}(info.Env(){{range $i, $_ := $o.Parameters}}, std::get<{{$i}}>(args){{end}});
208          {{/* indent */ -}}
209{{-      if   IsUndefinedType $o.Type}}return info.Env().Null();
210{{-      else                        }}return ToJS(info.Env(), result);
211{{-      end                         }}
212        }
213        error = {{if $overloaded}}"\noverload {{$overload_idx}} failed to match:\n" + {{end}}res.error;
214      }
215{{-    end}}
216      Napi::Error::New(info.Env(), "no overload matched for {{$m.Name}}:\n" + error).ThrowAsJavaScriptException();
217      return {};
218    }
219{{-  end}}
220
221{{-  range $a := AttributesOf $}}
222    Napi::Value get{{Title $a.Name}}(const Napi::CallbackInfo& info) {
223      return ToJS(info.Env(), impl->get{{Title $a.Name}}(info.Env()));
224    }
225{{-   if not $a.Readonly}}
226    void set{{Title $a.Name}}(const Napi::CallbackInfo& info, const Napi::Value& value) {
227      {{template "Type" $a.Type}} v{};
228      auto res = FromJS(info.Env(), value, v);
229      if (res) {
230        impl->set{{Title $a.Name}}(info.Env(), std::move(v));
231      } else {
232        res = res.Append("invalid value to {{$a.Name}}");
233        Napi::Error::New(info.Env(), res.error).ThrowAsJavaScriptException();
234      }
235    }
236{{-   end }}
237{{-  end}}
238  };
239{{end}}
240
241
242{{- /*
243--------------------------------------------------------------------------------
244-- Dictionary emits the C++ method implementations and associated functions of
245-- the interop type that defines the given ast.Dictionary
246--------------------------------------------------------------------------------
247*/ -}}
248{{- define "Dictionary"}}
249Result Converter<{{$.Name}}>::FromJS(Napi::Env env, Napi::Value value, {{$.Name}}& out) {
250  auto object = value.ToObject();
251  Result res;
252{{- template "DictionaryMembersFromJS" $}};
253  return Success;
254}
255
256Napi::Value Converter<{{$.Name}}>::ToJS(Napi::Env env, {{$.Name}} value) {
257  auto object = Napi::Object::New(env);
258{{- template "DictionaryMembersToJS" $}}
259  return object;
260}
261
262std::ostream& operator<<(std::ostream& o, const {{$.Name}}& dict) {
263    o << "{{$.Name}} {";
264{{-    range $i, $m := $.Members}}
265    o << {{if $i}}", "{{else}}" "{{end}} << "{{$m.Name}}: ";
266    utils::Write(o, dict.{{$m.Name}});
267{{-    end          }}
268    o << "}" << std::endl;
269    return o;
270}
271{{ end}}
272
273
274{{- /*
275--------------------------------------------------------------------------------
276-- DictionaryMembersFromJS emits the C++ logic to convert each of the
277-- dictionary ast.Member fields from JavaScript to C++. Each call to ToJS() is
278-- emitted as a separate statement, and requires a 'Result res' local to be
279-- declared
280--------------------------------------------------------------------------------
281*/ -}}
282{{- define "DictionaryMembersFromJS"}}
283{{-    if $.Inherits}}{{template "DictionaryMembersFromJS" (Lookup $.Inherits)}}{{end}}
284{{-    range $i, $m := $.Members}}
285  {{/* indent */}}
286{{-      if   $m.Init }}res = interop::FromJSOptional(env, object.Get("{{$m.Name}}"), out.{{$m.Name}});
287{{-      else         }}res = interop::FromJS(env, object.Get("{{$m.Name}}"), out.{{$m.Name}});
288{{-      end          }}
289  if (!res) {
290    return res.Append("while converting member '{{$m.Name}}'");
291  }
292{{-    end}}
293{{- end}}
294
295
296{{- /*
297--------------------------------------------------------------------------------
298-- DictionaryMembersToJS emits the C++ logic to convert each of the
299-- dictionary ast.Member fields to JavaScript from C++. Each call to ToJS() is
300-- emitted as a separate statement
301--------------------------------------------------------------------------------
302*/ -}}
303{{- define "DictionaryMembersToJS"}}
304{{-    if $.Inherits}}{{template "DictionaryMembersToJS" (Lookup $.Inherits)}}{{end}}
305{{-    range $m := $.Members}}
306  object.Set(Napi::String::New(env, "{{$m.Name}}"), interop::ToJS(env, value.{{$m.Name}}));
307{{-    end}}
308{{- end}}
309
310
311{{- /*
312--------------------------------------------------------------------------------
313-- Interface emits the C++ method implementations that define the given
314-- ast.Interface.
315-- Note: Most of the actual binding logic lives in the interface wrapper class.
316--------------------------------------------------------------------------------
317*/ -}}
318{{- define "Interface"}}
319{{$.Name}}::{{$.Name}}() = default;
320
321{{$.Name}}* {{$.Name}}::Unwrap(Napi::Object object) {
322  auto* wrappers = Wrappers::For(object.Env());
323  if (!object.InstanceOf(wrappers->{{$.Name}}_ctor.Value())) {
324    return nullptr;
325  }
326  return Wrappers::W{{$.Name}}::Unwrap(object)->impl.get();
327}
328
329Interface<{{$.Name}}> {{$.Name}}::Bind(Napi::Env env, std::unique_ptr<{{$.Name}}>&& impl) {
330  auto* wrappers = Wrappers::For(env);
331  auto object = wrappers->{{$.Name}}_ctor.New({});
332  auto* wrapper = Wrappers::W{{$.Name}}::Unwrap(object);
333  wrapper->impl = std::move(impl);
334  return Interface<{{$.Name}}>(object);
335}
336
337{{$.Name}}::~{{$.Name}}() = default;
338{{ end}}
339
340
341{{- /*
342--------------------------------------------------------------------------------
343-- Enum emits the C++ associated functions of the interop type that defines the
344-- given ast.Enum
345--------------------------------------------------------------------------------
346*/ -}}
347{{- define "Enum"}}
348bool Converter<{{$.Name}}>::FromString(std::string str, {{$.Name}}& out) {
349{{-  range $e := $.Values}}
350  if (str == {{$e.Value}}) {
351    out = {{$.Name}}::{{EnumEntryName $e.Value}};
352    return true;
353  }
354{{-  end}}
355  return false;
356}
357
358const char* Converter<{{$.Name}}>::ToString({{$.Name}} value) {
359  switch (value) {
360{{-  range $e := $.Values}}
361  case {{$.Name}}::{{EnumEntryName $e.Value}}:
362    return {{$e.Value}};
363{{-  end}}
364  }
365  return nullptr;
366}
367
368Result Converter<{{$.Name}}>::FromJS(Napi::Env env, Napi::Value value, {{$.Name}}& out) {
369  std::string str = value.ToString();
370  if (FromString(str, out)) {
371    return Success;
372  }
373  return Error(str + " is not a valid enum value of {{$.Name}}");
374}
375
376Napi::Value Converter<{{$.Name}}>::ToJS(Napi::Env env, {{$.Name}} value) {
377  switch (value) {
378{{-  range $e := $.Values}}
379  case {{$.Name}}::{{EnumEntryName $e.Value}}:
380    return Napi::String::New(env, {{$e.Value}});
381{{-  end}}
382  }
383  return env.Undefined();
384}
385
386std::ostream& operator<<(std::ostream& o, {{$.Name}} value) {
387  if (auto* s = Converter<{{$.Name}}>::ToString(value)) {
388    return o << s;
389  }
390  return o << "undefined<{{$.Name}}>";
391}
392
393{{end}}
394