1 module webidl.binding.generator;
2 
3 import std.stdio;
4 import webidl.grammar;
5 import pegged.grammar : ParseTree;
6 
7 import std.array : appender, array, Appender;
8 import std.algorithm;
9 import std.range : chain, enumerate;
10 import std.conv : text, to;
11 import std.range : zip, only, retro;
12 import std.typecons : Flag, No, Yes;
13 import openmethods;
14 
15 enum is32Bit = true;
16 
17 enum dKeywords = ["abstract","alias","align","asm","assert","auto","body","bool","break","byte","case","cast","catch","cdouble","cent","cfloat","char","class","const","continue","creal","dchar","debug","default","delegate","delete","deprecated","do","double","else","enum","export","extern","false","final","finally","float","for","foreach","foreach_reverse","function","goto","idouble","if","ifloat","immutable","import","in","inout","int","interface","invariant","ireal","is","lazy","long","macro","mixin","module","new","nothrow","null","out","override","package","pragma","private","protected","public","pure","real","ref","return","scope","shared","short","static","struct","super","switch","synchronized","template","this","throw","true","try","typedef","typeid","typeof","ubyte","ucent","uint","ulong","union","unittest","ushort","version","void","wchar","while","with","__FILE__","__FILE_FULL_PATH__","__MODULE__","__LINE__","__FUNCTION__","__PRETTY_FUNCTION__","__gshared","__traits","__vector","__parameters","__DATE__","__EOF__","__TIME__","__TIMESTAMP__","__VENDOR__","__VERSION__"];
18 
19 enum jsKeywords = ["default", "arguments"];
20 
21 enum nativeTypes = ["AudioParam", "ReadableStream", "ArrayBufferView", "nsIVariant", "nsISupports", "MIDIOutput", "MIDIInput", "BufferSource", "JSON", "JsObject", "ArrayPair", "Record", "Iterator", "FrozenArray", "InputStream", "ArrayBuffer", "DataView", "Uint8ClampedArray", "Float64Array", "Float32Array", "Uint32Array", "Uint16Array", "Uint8Array", "Int32Array", "Int16Array", "Int8Array", "TypedArray", "Sequence", "JsPromise", "Any"];
22 mixin(registerMethods);
23 
24 enum FunctionType { Function = 1, Attribute = 2, Static = 4, OpIndex = 8, OpIndexAssign = 16, OpDispatch = 32, Getter = 64, Setter = 128, Deleter = 256, Includes = 512, Partial = 1024, DictionaryConstructor = 2048, ExposedConstructor = 4096 };
25 
26 struct Argument {
27   string name;
28   ParseTree type;
29   ParseTree default_;
30   ParseTree argRest;
31   bool templated = false;
32 }
33 
34 struct JsExportFunction {
35   string parentTypeName;
36   string name;
37   Argument[] args;
38   ParseTree result;
39   FunctionType type;
40   string manglePostfix;
41 }
42 
43 struct DBindingFunction {
44   string parentTypeName;
45   string name;
46   Argument[] args;
47   ParseTree result;
48   FunctionType type;
49   string manglePostfix;
50   string baseType;
51   string customName;
52   string handle;
53   bool generic;
54 }
55 
56 bool[string] isGeneric;
57 struct DImportFunction {
58   string parentTypeName;
59   string name;
60   Argument[] args;
61   ParseTree result;
62   FunctionType type;
63   string manglePostfix;
64 }
65 
66 interface Node {
67   void toString(scope void delegate(const(char)[]) sink);
68 }
69 
70 void indentToString(scope void delegate(const(char)[]) sink, Node[] children) {
71   foreach(child; children) {
72     bool begin = true;
73     child.toString((const(char)[] line){
74         if (!line)
75           return;
76         if (begin)
77           sink("  ");
78         sink(line);
79         begin = line[$-1] == '\n';
80       });
81     if (!begin)
82       sink("\n");
83   }
84 }
85 class ModuleNode : Node {
86   Module module_;
87   Node[] children;
88   this(Module module_, Node[] children) {
89     this.module_ = module_;
90     this.children = children;
91   }
92   void toString(scope void delegate(const(char)[]) sink) {
93     sink("Module ");
94     sink(module_.name);
95     sink("\n");
96     sink.indentToString(children);
97   }
98 }
99 class ConstNode : Node {
100   string type;
101   string name;
102   string value;
103   this(string type, string name, string value) {
104     this.type = type;
105     this.name = name;
106     this.value = value;
107   }
108   void toString(scope void delegate(const(char)[]) sink) {
109     sink(name);
110   }
111 }
112 
113 class StructNode : Node {
114   string name;
115   ParseTree baseType;
116   Node[] children;
117   string[] functions;
118   Flag!"isStatic" isStatic;
119   this(string name, ParseTree baseType , Node[] children, string[] functions, Flag!"isStatic" isStatic = No.isStatic) {
120     this(name, baseType, children, isStatic);
121     this.functions = functions;
122   }
123   this(string name, ParseTree baseType , Node[] children, Flag!"isStatic" isStatic = No.isStatic) {
124     this.name = name;
125     this.baseType = baseType;
126     this.children = children;
127     this.isStatic = isStatic;
128   }
129   void toString(scope void delegate(const(char)[]) sink) {
130     sink("Struct ");
131     sink(name);
132     sink("\n");
133     sink.indentToString(children);
134   }
135   string getHandleSymbol() {
136     if (baseType != ParseTree.init)
137       return "_parent";
138     return "handle";
139   }
140 }
141 
142 void toDBinding(virtual!Node node, Semantics semantics, IndentedStringAppender* a);
143 void toDBinding(virtual!Node node, StructNode parent, Semantics semantics, IndentedStringAppender* a);
144 void toJsExport(virtual!Node node, Semantics semantics, string[] filter, IndentedStringAppender* a);
145 void toJsExport(virtual!Node node, StructNode parent, Semantics semantics, string[] filter, IndentedStringAppender* a);
146 void toDImport(virtual!Node node, Semantics semantics, IndentedStringAppender* a);
147 void toDImport(virtual!Node node, StructNode parent, Semantics semantics, IndentedStringAppender* a);
148 
149 @method void _toDBinding(Node node, Semantics semantics, IndentedStringAppender* a) {}
150 @method void _toDBinding(ModuleNode node, Semantics semantics, IndentedStringAppender* a) {
151   node.children.each!(c => toDBinding(c, semantics, a));
152 }
153 @method void _toJsExport(Node node, Semantics semantics, string[] filter, IndentedStringAppender* a) {}
154 @method void _toJsExport(ModuleNode node, Semantics semantics, string[] filter, IndentedStringAppender* a) {
155   node.children.each!(c => toJsExport(c, semantics, filter, a));
156 }
157 @method void _toJsExport(StructNode node, Semantics semantics, string[] filter, IndentedStringAppender* a) {
158   import std.algorithm : canFind;
159   bool[string] names;
160   foreach(child; node.children) {
161     auto fun = cast(FunctionNode)child;
162     if (fun) {
163       string name = mangleJsName(node, fun);
164       if (auto p = name in names)
165         continue;
166       if (filter.length > 0 && !filter.canFind(name))
167         continue;
168       names[name] = true;
169     }
170     toJsExport(child, node, semantics, filter, a);
171   }
172 }
173 @method void _toDImport(Node node, Semantics semantics, IndentedStringAppender* a) {}
174 @method void _toDImport(ModuleNode node, Semantics semantics, IndentedStringAppender* a) {
175   node.children.each!(c => toDImport(c, semantics, a));
176 }
177 @method void _toDImport(StructNode node, Semantics semantics, IndentedStringAppender* a) {
178   bool[string] names;
179   foreach(child; node.children) {
180     auto fun = cast(FunctionNode)child;
181     if (fun) {
182       string name = mangleJsName(node, fun);
183       if (auto p = name in names)
184         continue;
185       names[name] = true;
186     }
187     toDImport(child, node, semantics, a);
188   }
189 }
190 
191 @method void _toDBinding(StructNode node, Semantics semantics, IndentedStringAppender* a) {
192   a.putLn(["struct ", node.name.friendlyName, " {"]);
193   a.indent();
194   a.putLn("nothrow:");
195   if (node.isStatic == Yes.isStatic) {
196     a.putLn("static:");
197   } else if (node.baseType != ParseTree.init) {
198     auto p = node.baseType.matches[0] in semantics.types;
199     if (p is null) {
200       writeln("Error: Cannot find definition of type ", node.baseType.matches[0], ".");
201     } else {
202       a.put(["libwasm.bindings.", p.module_.name, "."]);
203     }
204     a.put(node.baseType.matches[0].friendlyName);
205     a.putLn(" _parent;");
206     a.putLn("alias _parent this;");
207     a.putLn("this(Handle h) {");
208     a.indent();
209     a.putLn(["_parent = .", node.baseType.matches[0].friendlyName,"(h);"]);
210     a.undent();
211     a.putLn("}");
212   } else {
213     a.putLn("JsHandle handle;");
214     a.putLn("alias handle this;");
215     a.putLn("this(Handle h) {");
216     a.indent();
217     a.putLn("this.handle = JsHandle(h);");
218     a.undent();
219     a.putLn("}");
220   }
221   node.children.each!(c => toDBinding(c, node, semantics, a));
222   a.undent();
223   a.putLn("}");
224 }
225 @method void _toDImport(MixinNode node, Semantics semantics, IndentedStringAppender* a) {
226   auto dummyParent = new StructNode(node.name, ParseTree.init, node.children);
227   bool[string] names;
228   foreach(child; node.children) {
229     auto fun = cast(FunctionNode)child;
230     if (fun) {
231       string name = mangleJsName(dummyParent, fun);
232       if (auto p = name in names)
233         continue;
234       names[name] = true;
235     }
236     toDImport(child, dummyParent, semantics, a);
237   }
238 }
239 
240 @method void _toDBinding(Node node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
241   // default od nothing
242 }
243 @method void _toJsExport(Node node, StructNode parent, Semantics semantics, string[] filter, IndentedStringAppender* a) {
244   // default od nothing
245 }
246 @method void _toDImport(Node node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
247   // default od nothing
248 }
249 @method void _toDBinding(ConstNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
250   a.putLn(["enum ", node.type, " ", node.name, " = ", node.value, ";"]);
251 }
252 
253 class StructIncludesNode : Node {
254   string name;
255   string baseType;
256   Node[] children;
257   this(string baseType, string name, Node[] children) {
258     this.name = name;
259     this.baseType = baseType;
260     this.children = children;
261   }
262   void toString(scope void delegate(const(char)[]) sink) {
263     sink("StructIncludes ");
264     sink(name);
265     sink("\n");
266     sink.indentToString(children);
267   }
268 }
269 
270 class MixinNode : Node {
271   string name;
272   Node[] children;
273   this(string name, Node[] children) {
274     this.name = name;
275     this.children = children;
276   }
277   void toString(scope void delegate(const(char)[]) sink) {
278     sink("MixinNode ");
279     sink(name);
280     sink("\n");
281     sink.indentToString(children);
282   }
283 }
284 @method void _toDBinding(StructIncludesNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
285   auto dummyParent = new StructNode(node.name, parent.baseType, node.children, parent.functions);
286   node.children.each!(c => toDBinding(c, dummyParent, semantics, a));
287 }
288 @method void _toJsExport(MixinNode node, Semantics semantics, string[] filter, IndentedStringAppender* a) {
289   import std.algorithm : canFind;
290   auto dummyParent = new StructNode(node.name, ParseTree.init, node.children);
291   bool[string] names;
292   foreach(child; node.children) {
293     auto fun = cast(FunctionNode)child;
294     if (fun) {
295       string name = mangleJsName(dummyParent, fun);
296       if (auto p = name in names)
297         continue;
298       if (filter.length > 0 && !filter.canFind(name))
299         continue;
300       names[name] = true;
301     }
302     toJsExport(child, dummyParent, semantics, filter, a);
303   }
304 }
305 
306 @method void _toDImport(StructIncludesNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
307   // auto dummyParent = new StructNode(node.name, ParseTree.init, node.children);
308   // node.children.each!(c => toDImport(c, dummyParent, semantics, a));
309 }
310 
311 class FunctionNode : Node {
312   string name;
313   Argument[] args;
314   ParseTree result;
315   FunctionType type;
316   string manglePostfix;
317   string baseType;
318   string customName;
319   this(string name, Argument[] args, ParseTree result, FunctionType type, string manglePostfix, string baseType, string customName) {
320     this.name = name;
321     this.args = args;
322     this.result = result;
323     this.type = type;
324     this.manglePostfix = manglePostfix;
325     this.baseType = baseType;
326     this.customName = customName;
327   }
328   void toString(scope void delegate(const(char)[]) sink) {
329     sink("Function ");
330     if (customName.length > 0)
331       sink(customName);
332     else
333       sink(name);
334     if (manglePostfix.length) {
335       sink("_");
336       sink(manglePostfix);
337     }
338     sink("(");
339     sink(args.map!(a => a.name).joiner(", ").text());
340     sink(")");
341   }
342 }
343 
344 @method void _toDBinding(FunctionNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
345   auto tmp = DBindingFunction(parent.name, node.name, node.args, node.result, node.type, node.manglePostfix, node.baseType, node.customName, parent.getHandleSymbol());
346   if (parent.isStatic == Yes.isStatic)
347     tmp.type |= FunctionType.Static;
348   semantics.dump(tmp, a, parent.functions);
349   // TODO: use parent.functions to avoid local shadowing
350 }
351 @method void _toJsExport(FunctionNode node, StructNode parent, Semantics semantics, string[] filter, IndentedStringAppender* a) {
352   if (node.type & (FunctionType.OpDispatch) || node.type & (FunctionType.DictionaryConstructor))
353     return;
354   auto tmp = JsExportFunction(parent.name, node.customName != "" ? node.customName : node.name, node.args, node.result, node.type, node.manglePostfix);
355   if (parent.isStatic == Yes.isStatic)
356     tmp.type |= FunctionType.Static;
357   auto context = Context(semantics);
358   context.dump(tmp, a);
359 }
360 @method void _toDImport(FunctionNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
361   if (node.type & (FunctionType.OpDispatch) || node.type & (FunctionType.DictionaryConstructor))
362     return;
363   auto tmp = DImportFunction(parent.name, node.customName != "" ? node.customName : node.name, node.args, node.result, node.type, node.manglePostfix);
364   if (parent.isStatic == Yes.isStatic)
365     tmp.type |= FunctionType.Static;
366   semantics.dump(tmp, a);
367 }
368 
369 class ExposedConstructorNode : FunctionNode {
370   this(string name, Argument[] args, ParseTree result, string baseType, string manglePostfix) {
371     super(name, args, result, FunctionType.ExposedConstructor, manglePostfix, baseType, "");
372   }
373   override void toString(scope void delegate(const(char)[]) sink) {
374     sink("ExposedConstructor ");
375     sink(name);
376     if (manglePostfix) {
377       sink("_");
378       sink(manglePostfix);
379     }
380     sink("(");
381     sink(args.map!(a => a.name).joiner(", ").text());
382     sink(")");
383   }
384 }
385 @method void _toDBinding(ExposedConstructorNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
386   if (parent.name != node.baseType)
387     return;
388   auto tmp = DBindingFunction(parent.name, node.name, node.args, node.result, FunctionType.ExposedConstructor, node.manglePostfix, "", "", parent.getHandleSymbol());
389   semantics.dump(tmp, a, parent.functions);
390 }
391 @method void _toJsExport(ExposedConstructorNode node, StructNode parent, Semantics semantics, string[] filter, IndentedStringAppender* a) {
392   if (parent.name != node.baseType)
393     return;
394   string mangledName = mangleName(parent.name, node.name, node.manglePostfix);
395   if (filter.length > 0 && !filter.canFind(mangledName))
396     return;
397   auto tmp = JsExportFunction(parent.name, node.name, node.args, node.result, FunctionType.ExposedConstructor, node.manglePostfix);
398   auto context = Context(semantics);
399   context.dump(tmp, a);
400 }
401 @method void _toDImport(ExposedConstructorNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
402   if (parent.name != node.baseType)
403     return;
404   auto tmp = DImportFunction(parent.name, node.name, node.args, node.result, FunctionType.ExposedConstructor, node.manglePostfix);
405   semantics.dump(tmp, a);
406 }
407 
408 class TypedefNode : Node {
409   string name;
410   string def;
411   ParseTree rhs;
412   this(string n, string d, ParseTree rhs) {
413     name = n;
414     def = d;
415     this.rhs = rhs;
416   }
417   void toString(scope void delegate(const(char)[]) sink) {
418     sink("Typedef ");
419     sink(name);
420     sink(" = ");
421     sink(def);
422   }
423 }
424 @method void _toDBinding(TypedefNode node, Semantics semantics, IndentedStringAppender* a) {
425   a.putLn(["alias ", node.name, " = ", node.def, ";"]);
426 }
427 
428 class EnumNode : Node {
429   string name;
430   string content;
431   this(string n, string c) {
432     name = n;
433     content = c;
434   }
435   void toString(scope void delegate(const(char)[]) sink) {
436     sink("Enum ");
437     sink(name);
438   }
439 }
440 
441 @method void _toDBinding(EnumNode node, Semantics semantics, IndentedStringAppender* a) {
442   a.putLn(["enum ", node.name, " {"]);
443   a.indent();
444   a.putLn(node.content);
445   a.undent();
446   a.putLn("}");
447 }
448 
449 class MaplikeNode : Node {
450   ParseTree keyType;
451   ParseTree valueType;
452   this(ParseTree keyType, ParseTree valueType) {
453     this.keyType = keyType;
454     this.valueType = valueType;
455   }
456   void toString(scope void delegate(const(char)[]) sink) {
457     sink("Maplike");
458   }
459 }
460 
461 @method void _toDBinding(MaplikeNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
462   auto context = Context(semantics);
463   string keyType = node.keyType.generateDType(context);
464   string valueQualifiers = (semantics.isPrimitive(node.valueType) || semantics.isNullable(node.valueType)) ? "" : "scope ref ";
465   string valueType = node.valueType.generateDType(context);
466   string mangleKeyType = node.keyType.generateDImports(Context(semantics));
467   string mangleValueType = node.valueType.generateDImports(Context(semantics));
468   string manglePrefix = "Maplike_" ~ mangleKeyType ~ "_" ~ mangleValueType ~ "_";
469   a.putLn("uint size() {");
470   a.putLn(["  return ", manglePrefix, "size(this.handle);"]);
471   a.putLn("}");
472   a.putLn("void clear() {");
473   a.putLn(["  ", manglePrefix, "clear(this.handle);"]);
474   a.putLn("}");
475   a.putLn(["void delete_(",keyType," key) {"]);
476   a.putLn(["  ", manglePrefix, "delete(this.handle, key);"]);
477   a.putLn("}");
478   a.putLn(["Iterator!(ArrayPair!(",keyType,", ",valueType,")) entries() {"]);
479   a.putLn(["  return Iterator!(ArrayPair!(",keyType,", ",valueType,"))(", manglePrefix, "entries(this.handle));"]);
480   a.putLn("}");
481   a.putLn(["extern(C) void forEach(void delegate(",keyType,", Handle, Handle) callback) {"]);
482   a.putLn(["  ", manglePrefix, "forEach(this.handle, callback);"]);
483   a.putLn("}");
484   a.putLn(["",valueType," get(",keyType," key) {"]);
485   a.putLn(["  return ",valueType,"(", manglePrefix, "get(this.handle, key));"]);
486   a.putLn("}");
487   a.putLn(["bool has(",keyType," key) {"]);
488   a.putLn(["  return ", manglePrefix, "has(this.handle, key);"]);
489   a.putLn("}");
490   a.putLn(["Iterator!(",keyType,") keys() {"]);
491   a.putLn(["  return Iterator!(",keyType,")(", manglePrefix, "keys(this.handle));"]);
492   a.putLn("}");
493   a.putLn(["void set(",keyType," key, ", valueQualifiers, valueType," value) {"]);
494   a.putLn(["  ", manglePrefix, "set(this.handle, key, value.handle);"]);
495   a.putLn("}");
496   a.putLn(["Iterator!(",valueType,") values() {"]);
497   a.putLn(["  return Iterator!(",valueType,")(", manglePrefix, "values(this.handle));"]);
498   a.putLn("}");
499 }
500 @method void _toDImport(MaplikeNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) {
501   auto context = Context(semantics);
502   string keyType = node.keyType.generateDType(context);
503   string valueType = node.valueType.generateDType(context);
504   string mangleKeyType = node.keyType.generateDImports(Context(semantics));
505   string mangleValueType = node.valueType.generateDImports(Context(semantics));
506   string manglePrefix = "Maplike_" ~ mangleKeyType ~ "_" ~ mangleValueType ~ "_";
507   a.putLn(["extern (C) uint ", manglePrefix, "size(Handle);"]);
508   a.putLn(["extern (C) void ", manglePrefix, "clear(Handle);"]);
509   a.putLn(["extern (C) void ", manglePrefix, "delete(Handle, ", mangleKeyType," key);"]);
510   a.putLn(["extern (C) Handle ", manglePrefix, "entries(Handle);"]);
511   a.putLn(["extern (C) void ", manglePrefix, "forEach(Handle, void delegate(", keyType,", Handle, Handle));"]);
512   a.putLn(["extern (C) ", valueType, " ", manglePrefix, "get(Handle, ", mangleKeyType,");"]);
513   a.putLn(["extern (C) bool ", manglePrefix, "has(Handle, ", mangleKeyType,");"]);
514   a.putLn(["extern (C) Handle ", manglePrefix, "keys(Handle);"]);
515   a.putLn(["extern (C) void ", manglePrefix, "set(Handle, ", mangleKeyType, " key, ", mangleValueType, " value);"]);
516   a.putLn(["extern (C) Handle ", manglePrefix, "values(Handle);"]);
517 }
518 
519 class CallbackNode : Node {
520   string name;
521   ParseTree result;
522   Argument[] args;
523   this(string name, ParseTree result, Argument[] args) {
524     this.name = name;
525     this.result = result;
526     this.args = args;
527   }
528   void toString(scope void delegate(const(char)[]) sink) {
529     sink("Callback ");
530     sink(name);
531   }
532 }
533 
534 @method void _toDBinding(CallbackNode node, Semantics semantics, IndentedStringAppender* a) {
535   a.put(["alias ", node.name, " = "]);
536   if (node.result.matches[0] == "void") {
537     a.put("void");
538   } else
539     node.result.generateDType(a, Context(semantics));
540   auto types = node.args.map!(arg => arg.type).map!(type => type.generateDType(Context(semantics))).joiner(", ").text;
541   a.putLn([" delegate(", types, ");"]);
542 }
543 
544 void dumpJsArgument(Appender)(ref Semantics semantics, Argument arg, ref Appender a) {
545   if (semantics.isNullable(arg.type)) {
546     a.put(arg.name.friendlyJsName);
547     a.put("Defined ? ");
548   }
549   if (semantics.isTypedef(arg.type) && semantics.isCallback(arg.type)) {
550     auto name = getTypeName(arg.type);
551     auto aliased = semantics.getAliasedType(name);
552     auto argument = Argument(arg.name, aliased.stripNullable);
553     semantics.dumpJsArgument(argument, a);
554   } else if (semantics.isUnion(arg.type) || semantics.isEnum(arg.type)) {
555     a.put("libwasm_decode_");
556     arg.type.mangleTypeJsImpl(a, semantics, MangleTypeJsContext(true));
557     a.put("(");
558     a.put(arg.name.friendlyJsName);
559     a.put(")");
560   } else if (semantics.isStringType(arg.type)) {
561     a.put(["libwasm_decode_string(",arg.name.friendlyJsName,"Len, ",arg.name.friendlyJsName,"Ptr)"]);
562   } else if (semantics.isCallback(arg.type)){
563     auto signature = getTypeName(arg.type) in semantics.types;
564     auto argList = semantics.getArgumentList(signature.tree);
565     auto arguments = extractArguments(argList);
566     auto types = extractTypes(argList);
567     string base = arg.name.friendlyJsName;
568     a.put([ "(", arguments.joiner(", ").text, ")=>{"]);
569     size_t offset = 0;
570     zip(arguments, types).enumerate.each!((t) {
571         auto index = t.index;
572         auto arg = t.value[0];
573         auto type = t.value[1];
574         if (semantics.isStringType(type) || semantics.isUnion(type)
575             || semantics.isNullable(type) || semantics.isEnum(type)) {
576           a.put(["libwasm_encode_"]);
577           if (type.name == "WebIDL.TypeWithExtendedAttributes")
578             type.children[1].mangleTypeJs(a, semantics);
579           else
580             type.mangleTypeJs(a, semantics);
581           a.put(["(", offset.to!string, ", ", arg, ");"]);
582           offset += semantics.getSizeOf(type);
583         } else if (!semantics.isPrimitive(type)) {
584           a.put(["encode_handle(", offset.to!string, ", ", arg, ");"]);
585           offset += semantics.getSizeOf(type);
586         }
587       });
588     auto resultPtr = offset;
589     bool needsClose = false;
590     auto result = signature.tree.children[1];
591     bool hasResult = result.matches[0] != "void";
592     bool resultIsPassedViaFirstArgument = hasResult && (semantics.isUnion(result) || semantics.isEnum(result) || semantics.isNullable(result) || semantics.isAny(result));
593     if (hasResult && !resultIsPassedViaFirstArgument)
594       a.put("return ");
595 
596     a.put(["libwasm_indirect_function_get(", base, "Ptr)("]);
597     if (resultIsPassedViaFirstArgument)
598       a.put([resultPtr.to!string, ", "]);
599     a.put([base, "Ctx"]);
600     offset = 0;
601     zip(arguments, types).enumerate.each!((t) {
602         a.put(", ");
603         auto index = t.index;
604         auto arg = t.value[0];
605         auto type = t.value[1];
606         if (semantics.isStringType(type) || semantics.isUnion(type)
607             || semantics.isNullable(type) || semantics.isEnum(type)
608             || !semantics.isPrimitive(type)) {
609           a.put(offset.to!string);
610           offset += semantics.getSizeOf(type);
611         } else
612           a.put(arg);
613       });
614     a.put(")");
615     if (resultIsPassedViaFirstArgument) {
616       a.put("; return ");
617       if (semantics.isUnion(result) || semantics.isEnum(result) || semantics.isNullable(result)) {
618         a.put("libwasm_decode_");
619         result.mangleTypeJsImpl(a, semantics, MangleTypeJsContext(true));
620         a.put(["(", resultPtr.to!string, ")"]);
621       } else if (semantics.isAny(result)) {
622         a.put(["libwasm_decode_Handle(", resultPtr.to!string, ")"]);
623       }
624     }
625     a.put("}");
626   } else if (semantics.isPrimitive(arg.type)) {
627     a.put(arg.name.friendlyJsName);
628   } else {
629     a.put(["objects[",arg.name.friendlyJsName,"]"]);
630   }
631   if (semantics.isNullable(arg.type)) {
632     a.put(" : undefined");
633   }
634 }
635 
636 void dumpJsArguments(Appender)(ref Semantics semantics, Argument[] args, ref Appender a) {
637   if (args.length == 0)
638     return;
639   foreach(arg; args[0..$-1]) {
640     semantics.dumpJsArgument(arg, a);
641     a.put(", ");
642   }
643   semantics.dumpJsArgument(args[$-1], a);
644 }
645 
646 void dump(Appender)(ref Context context, JsExportFunction item, ref Appender a) {
647   auto semantics = context.semantics;
648   a.put(mangleName(item.parentTypeName,item.name,item.manglePostfix));
649   a.put(": (");
650   bool rawResult = item.result != ParseTree.init && semantics.isRawResultType(item.result);
651   if (rawResult)
652     a.put("rawResult");
653   if (!(item.type & FunctionType.Static)) {
654     if (rawResult)
655       a.put(", ");
656     a.put("ctx");
657   }
658   if ((rawResult || (!(item.type & FunctionType.Static))) && item.args.length > 0)
659     a.put(", ");
660   item.args.enumerate.each!((e){
661       auto arg = e.value;
662       if (e.index > 0)
663         a.put(", ");
664       a.put(arg.name.friendlyJsName);
665       if (semantics.isNullable(arg.type))
666         a.put(["Defined, ", arg.name.friendlyJsName]);
667       if (semantics.isCallback(arg.type))
668         a.put(["Ctx, ", arg.name.friendlyJsName, "Ptr"]);
669       else if (semantics.isStringType(arg.type))
670         a.put(["Len, ", arg.name.friendlyJsName, "Ptr"]);
671     });
672   a.putLn(") => {");
673   a.indent();
674   a.putLn("setupMemory();");
675   bool returns = item.result != ParseTree.init && item.result.matches[0] != "void";
676   bool needsClose = false;
677   if (returns) {
678     if (!rawResult)
679       a.put("return ");
680     if (semantics.isStringType(item.result) || semantics.isUnion(item.result) || semantics.isNullable(item.result) || semantics.isEnum(item.result)) {
681       a.put("libwasm_encode_");
682       if (item.result.name == "WebIDL.TypeWithExtendedAttributes")
683         item.result.children[1].mangleTypeJs(a, semantics);
684       else
685         item.result.mangleTypeJs(a, semantics);
686       needsClose = true;
687       if (rawResult)
688         a.put("(rawResult, ");
689       else
690         a.put("(");
691     } else if (!semantics.isPrimitive(item.result)) {
692       a.put(["addObject("]);
693       needsClose = true;
694     }
695   }
696   if (item.type & FunctionType.Deleter)
697     a.put("delete ");
698   if (item.type & FunctionType.Static)
699     a.put(item.parentTypeName);
700   else {
701     if (item.type & FunctionType.ExposedConstructor)
702       a.put("new ");
703     a.put("objects[ctx]");
704   }
705   if (item.type & (FunctionType.Getter | FunctionType.Setter | FunctionType.Deleter)) {
706     a.put("[");
707     semantics.dumpJsArgument(item.args[0], a);
708     a.put("]");
709     if (item.type & FunctionType.Setter) {
710       a.put(" = ");
711       semantics.dumpJsArgument(item.args[1], a);
712     }
713   } else {
714     a.put(".");
715     a.put(item.name);
716     if (item.type & FunctionType.Attribute) {
717       if (!returns) {
718         a.put(" = ");
719         semantics.dumpJsArgument(item.args[0], a);
720       }
721     } else {
722       a.put("(");
723       semantics.dumpJsArguments(item.args, a);
724       a.put(")");
725     }
726   }
727   if (needsClose)
728     a.put(")");
729   a.putLn(";");
730   a.undent();
731   a.putLn("},");
732 }
733 
734 void dump(Appender)(ref Semantics semantics, DImportFunction item, ref Appender a) {
735   if(mangleName(item.parentTypeName,item.name,item.manglePostfix) in isGeneric) return;
736   auto context = Context(semantics);
737   
738   a.put("extern (C) ");
739   if (item.result == ParseTree.init || item.result.matches[0] == "void")
740     a.put("void");
741   else {
742     if (!semantics.isPrimitive(item.result) && !semantics.isUnion(item.result) && !semantics.isNullable(item.result)) {
743       a.put("Handle");
744     } else {
745       item.result.generateDType(*a, context);
746     }
747   }
748   a.put(" ");
749   a.put(mangleName(item.parentTypeName,item.name,item.manglePostfix));
750   a.put("(");
751   if (!(item.type & FunctionType.Static)) {
752     a.put("Handle");
753     if (item.args.length > 0)
754       a.put(", ");
755   }
756   if (item.args.length > 0) {
757     item.args.map!(arg => arg.type).array.putWithDelimiter!(generateDImports)(", ", *a, context);
758   }
759   a.putLn(");");
760 }
761 
762 auto getTemplatedTypeName(size_t idx) {
763   import std.conv : to;
764   return "T"~idx.to!string;
765 }
766 auto getSymbolInfo(string symbol) {
767   struct SymbolInfo {
768     string module_;
769     string name;
770   }
771   import std.algorithm : splitter;
772   import std.demangle;
773   import std.range : drop;
774   import std.typecons : tuple;
775   auto parts = demangle("_D"~symbol[1..$]~"v")[5..$].splitter(".").drop(2).array();
776   return SymbolInfo(parts[0], parts[1]);
777 }
778 string generateJsGlobalBindings(IR ir, string[] jsExportFilters, ref IndentedStringAppender app) {
779   auto generatePromiseThenBindings(IR ir, string symbol, string mangled, ref IndentedStringAppender app) {
780     auto getDecoder(string mangled) {
781       if (mangled == "Aya") {
782         return "decode_string";
783       } else if (mangled == "handle" || mangled[0] == 'S') {
784         return "decode_handle";
785       } else if (mangled == "v")
786         return "void";
787       else if (mangled[0] == 'E') {
788         auto info = getSymbolInfo(mangled);
789         return "libwasm_decode_" ~ info.name;
790       } else {
791         if (mangled.startsWith("S8optional")) {
792           throw new Error("JsPromise!T.then is not yet implemented for Optional!T.");
793         }
794         auto info = getSymbolInfo(mangled);
795         if (ir.semantics.isTypedef(info.name)) {
796           throw new Error("JsPromise!T.then is not yet implemented for Typedefs.");
797         }
798         if (mangled.canFind("sumtype")) {
799           throw new Error("JsPromise!T.then is not yet implemented for SumType!Ts.");
800         }
801       }
802       return "";
803     }
804     auto getEncoder(string mangled) {
805       if (mangled == "Aya") {
806         return "encode_string";
807       } else if (mangled == "handle" || mangled[0] == 'S') {
808         return "encode_handle";
809       } else if (mangled == "v")
810         return "void";
811       else if (mangled[0] == 'E') {
812         auto info = getSymbolInfo(mangled);
813         return "libwasm_encode_" ~ info.name;
814       } else {
815         if (mangled.startsWith("S8optional")) {
816           throw new Error("JsPromise!T.then is not yet implemented for Optional!T.");
817         }
818         auto info = getSymbolInfo(mangled);
819         if (ir.semantics.isTypedef(info.name)) {
820           throw new Error("JsPromise!T.then is not yet implemented for Typedefs.");
821         }
822         if (mangled.canFind("sumtype")) {
823           throw new Error("JsPromise!T.then is not yet implemented for SumType!Ts.");
824         }
825       }
826       return "";
827     }
828     import std.ascii : isDigit;
829     auto len = mangled.until!(a => !a.isDigit).to!int;
830     auto prefixLen = mangled.countUntil!(a => !a.isDigit);
831     len += prefixLen+1;
832     auto argEncoder = getEncoder(mangled[prefixLen+1..len]);
833     auto resultDecoder = getDecoder(mangled[len..$]);
834     bool returns = resultDecoder != "void" && resultDecoder != "";
835     app.putLn(["promise_then_", mangled,": (handle, ctx, ptr) => {"]);
836     app.indent();
837     app.putLn("return addObject(objects[handle].then((r)=>{");
838     app.indent();
839     if (argEncoder != "" && argEncoder != "void")
840       app.putLn([argEncoder, "(0,r);"]);
841     app.put("libwasm_indirect_function_get(ptr)(");
842     if (returns) {
843       app.put("512, ");
844     }
845     if (argEncoder == "void") {
846       app.putLn("ctx);");
847     } else if (argEncoder != "") {
848       app.putLn("ctx, 0);");
849     } else {
850       app.putLn("ctx, r);");
851     }
852     if (returns) {
853       app.putLn(["return ", resultDecoder, "(512);"]);
854     }
855     app.undent();
856     app.putLn("}));");
857     app.undent();
858     app.putLn("},");
859   }
860   auto mappings = ["promise_then_": &generatePromiseThenBindings];
861   foreach(filter; jsExportFilters) {
862     foreach(key, func; mappings) {
863       if (filter.startsWith(key))
864         func(ir, filter, filter[commonPrefix(filter,key).length .. $], app);
865     }
866   }
867   return app.data;
868 }
869 void dump(Appender)(ref Semantics semantics, DBindingFunction item, ref Appender a, string[] locals) {
870   // if (item.result != ParseTree.init) {
871   //   item.result.generateDType(a, Context(semantics));
872   //   a.put(" ");
873   // } else
874   //   a.put("void ");
875   if (item.type & FunctionType.DictionaryConstructor) {
876     // 
877     a.putLn("static auto create() {");
878     a.indent();
879     a.putLn(["return ", item.parentTypeName, "(libwasm_add__object());"]);
880     a.undent();
881     a.putLn("}");
882     return;
883   }
884   bool returns = item.result != ParseTree.init && item.result.matches[0] != "void";
885   if (returns) {
886     
887     //if (item.type == FunctionType.ExposedConstructor)
888     //  a.put(item.name);
889     if (semantics.isPrimitive(item.result)){
890       if (item.type == FunctionType.ExposedConstructor)
891         a.put(item.name);
892       else item.result.generateDType(a, Context(semantics));
893     }
894     else a.put("auto");
895     a.put(" ");
896    } else a.put("void ");
897   void putFuncName() {
898     switch (item.type & (FunctionType.OpIndex | FunctionType.OpDispatch | FunctionType.OpIndexAssign)) {
899     case FunctionType.OpIndex:
900       a.put("opIndex");
901       break;
902     case FunctionType.OpDispatch:
903       a.put("opDispatch");
904       break;
905     case FunctionType.OpIndexAssign:
906       a.put("opIndexAssign");
907       break;
908     default:
909       a.put(item.name.friendlyName);
910       break;
911     }
912   }
913   putFuncName();
914   auto templArgs = item.args.filter!(a => a.templated).array();
915   auto runArgs = item.args.filter!(a => !a.templated).array();
916   auto anyOrOptArgs = item.args.enumerate.filter!(a => semantics.isAny(a.value.type) || semantics.isNullable(a.value.type)).array();
917   auto anys = anyOrOptArgs.filter!(a => semantics.isAny(a.value.type)).array();
918   auto optArgs = anyOrOptArgs.filter!(a => semantics.isNullable(a.value.type)).array();
919   bool has_template_args = true;
920   if (templArgs.length > 0) {
921     assert(anys.length == 0);
922     a.put("(");
923     semantics.dumpDParameters(templArgs, a, locals);
924     a.put(")");
925   } else if (anyOrOptArgs.length > 0) {
926     a.put("(");
927     foreach(anyOrOpt; anyOrOptArgs[0..$-1]) {
928       a.put(getTemplatedTypeName(anyOrOpt.index));
929       a.put(", ");
930     }
931     a.put(getTemplatedTypeName(anyOrOptArgs[$-1].index));
932     a.put(")");
933   } else {
934     has_template_args = false;
935     a.put("()");
936   }
937   a.put("(");
938   if (item.type & FunctionType.OpIndexAssign) {
939     assert(runArgs.length > 1);
940     semantics.dumpDParameter(runArgs[$-1], a, locals, 0);
941     a.put(", ");
942     semantics.dumpDParameters(runArgs[0..$-1], a, locals);
943   } else
944     semantics.dumpDParameters(runArgs, a, locals);
945   a.put(") ");
946   if (optArgs.length > 0) {
947     a.put("if (");
948     foreach(idx, opt; optArgs) {
949       a.put("isTOrPointer!(");
950       a.put(getTemplatedTypeName(opt.index));
951       a.put(", ");
952       import std.string : replace;
953       auto real_type_name = opt.value.type.stripNullable.generateDType(Context(semantics).withLocals(locals)).replace("EventHandler", "EventHandlerNonNull"); // todo: make this work with aliases
954       a.put(real_type_name);
955       a.put(")");
956       if (idx+1 < optArgs.length)
957         a.put(" && ");
958     }
959     a.put(") ");
960   }
961   a.putLn("{");
962   a.indent();
963   // short path for primary Any types
964   if (!(item.type & FunctionType.Static) && anys.length == 1 && item.args.length == 1 && !returns) {
965     import std.format : format;
966     a.putLn("import std.traits : isNumeric, isFloatingPoint, isSomeString;");
967     a.putLn("if (isSomeString!T0) {");
968     a.indent();
969     a.putLn(format(`Object_Call_string__void(this.%s, "%s", cast(string) %s);`, item.handle, item.name, anys[0].value.name.friendlyName));
970     a.putLn("return;");
971     a.undent();
972     a.putLn("} else if (isNumeric!T0 && !isFloatingPoint!T0) {");
973     a.indent();
974     a.putLn(format(`Object_Call_long__void(this.%s, "%s", cast(long) %s);`, item.handle, item.name, anys[0].value.name.friendlyName));
975     a.putLn("return;");
976     a.undent();
977     a.putLn("} else if (isFloatingPoint!T0) {");
978     a.indent();
979     a.putLn(format(`Object_Call_double__void(this.%s, "%s", cast(double) %s);`, item.handle, item.name, anys[0].value.name.friendlyName));
980     a.putLn("return;");
981     a.undent();
982     a.putLn("}");
983   } 
984   foreach(any; anys) {
985     a.put("// ");
986     any.value.type.generateDType(a, Context(semantics).withLocals(locals));
987     a.putLn("");
988     a.putLn(["Handle _handle_", any.value.name, " = getOrCreateHandle(", any.value.name.friendlyName, ");"]);
989   }
990   bool needDropHandle = anys.length != 0;
991   if (returns) {
992     if (needDropHandle)
993       a.put("auto result = ");
994     else
995       a.put("return ");
996       
997     if (!semantics.isPrimitive(item.result) && !semantics.isUnion(item.result)) {
998       import std.string : replace;
999       bool optional = (semantics.isNullable(item.result) || item.result.children[$-1].name == "WebIDL.Null" && item.result.children[$-1].matches[0] == "?");
1000       if (optional) 
1001         a.put(item.result.generateDType(Context(semantics).withLocals(locals)).replace("Optional", "recastOpt"));
1002       else if (item.type == FunctionType.ExposedConstructor)
1003         a.put([".",item.name]);
1004       else
1005         item.result.generateDType(a, Context(semantics).withLocals(locals));
1006       a.put("(");
1007     }
1008   }
1009 /*
1010 struct DBindingFunction {
1011   string parentTypeName;
1012   string name;
1013   Argument[] args;
1014   ParseTree result;
1015   FunctionType type;
1016   string manglePostfix;
1017   string baseType;
1018   string customName;
1019   string handle;
1020 }*/
1021 
1022 // optional 1856
1023   void dumpCallArgs(AppenderObj)(ref AppenderObj app, string generic_member, string static_name = null) 
1024   {
1025     app.put("(");
1026     if (!static_name) {
1027       app.put("this.");
1028       app.put(item.handle);
1029     } else {     
1030       app.put("\"");
1031       app.put(static_name);
1032       app.put("\"");
1033     }
1034     if (generic_member) {        
1035       app.put(", \"");
1036       app.put(generic_member);
1037       app.put("\"");
1038     }
1039     if (item.args.length > 0)
1040       app.put(", ");
1041     semantics.dumpDJsArguments(item.args, app);
1042     
1043   }
1044 
1045   void dumpCallVarArgs(AppenderObj)(ref AppenderObj app, string generic_member) 
1046   {
1047     app.put("(");
1048     app.put("this.");
1049     app.put(item.handle);
1050     if (generic_member) {
1051       app.put(", \"");
1052       app.put(generic_member);
1053       app.put("\"");
1054     }
1055     if (runArgs.length == 0) {
1056       app.put(", \"\", tuple()");
1057       return;
1058     }
1059     app.put(", ");
1060   
1061     app.put("\"");
1062 
1063     void addType(T)(T arg) {
1064       auto type_name = arg.type.getTypeName();
1065       bool is_native_type = false;
1066       foreach (native_type; nativeTypes) {
1067         
1068         if (type_name == native_type || type_name == "."~native_type || type_name.startsWith(native_type ~ "!")) {
1069           is_native_type = true;
1070           break;
1071         }
1072       }
1073       if ((is_native_type || (!semantics.isPrimitive(arg.type) && !semantics.isUnion(arg.type))) && !semantics.isNullable(arg.type)) {
1074         app.put("Handle");
1075       } else if ((is_native_type || (!semantics.isPrimitive(arg.type) && !semantics.isUnion(arg.type))) && semantics.isNullable(arg.type)) {        
1076         app.put("Optional!Handle");
1077       } else if (semantics.isUnion(arg.type)) {
1078         string[] children_;
1079         bool optional = (semantics.isNullable(arg.type) || arg.type.children[$-1].name == "WebIDL.Null" && arg.type.children[$-1].matches[0] == "?");
1080         if (optional) {
1081           app.put("Optional!");
1082           children_ = getChildrenOfSumType(arg.type, semantics, locals);
1083         }
1084         else  children_ = getChildrenOfSumType(arg.type, semantics, locals);
1085         app.put("SumType!(");
1086         
1087         void generateSumTypes(string[] children) {
1088           foreach (i, child; children) {
1089             
1090             if (i > 0) app.put(",");
1091             auto p = child in semantics.types;
1092             import std.string : replace;
1093             if (!p) p = child.replace(".","") in semantics.types;
1094             // enum ?
1095             if (p) {
1096               //writeln("Got Type: ", p.tree.name, " for ", child);
1097 
1098               if (p.tree.name == "WebIDL.Enum"){
1099                 // todo: How to see if it's optional?
1100                 app.put("int");
1101               }
1102               else if (p.tree.name == "WebIDL.Typedef") {
1103 
1104 
1105                 auto baseType = semantics.getAliasedType(child);
1106                 if ((is_native_type || (!semantics.isPrimitive(baseType) && !semantics.isUnion(baseType))) && !semantics.isNullable(baseType)) {
1107                   app.put("Handle");
1108                 } else if ((is_native_type || (!semantics.isPrimitive(baseType) && !semantics.isUnion(baseType))) && semantics.isNullable(baseType)) {        
1109                   app.put("Optional!Handle");
1110                 } else if (semantics.isEnum(baseType)) {
1111                   if (semantics.isNullable(baseType))
1112                     app.put("Optional!");
1113                   app.put("int");
1114                   
1115                 } else if (semantics.isTypedef(baseType)) {
1116                   writeln("Typedef redirected to other Typedef: ", child, " => ", getTypeName(baseType));
1117                 } else if (semantics.isUnion(baseType)) {
1118                   // flatten
1119                   string[] subchildren = getChildrenOfSumType(baseType, semantics, locals);
1120                   generateSumTypes(subchildren);
1121                 
1122                 } else {
1123                   app.put(generateDType(baseType, Context(semantics).withLocals(locals)));
1124                 }
1125               }
1126               else app.put("Handle");
1127               
1128               
1129             } else {
1130               bool found = false;
1131               foreach (native_type; nativeTypes) {
1132                 if (child == native_type || child == "."~native_type || child.startsWith(native_type ~ "!")) {
1133                   app.put("Handle");
1134                   found = true;
1135                   break;
1136                 }
1137               }
1138               if (!found)
1139                 app.put(child);
1140             }
1141           } 
1142         }
1143         generateSumTypes(children_);
1144 
1145         app.put(")");
1146 
1147       } else if (semantics.isEnum(arg.type)) {
1148         if (semantics.isNullable(arg.type))
1149           app.put("Optional!");
1150         app ~= "Enum";
1151       } else
1152         app ~= generateDType(arg.type, Context(semantics).withLocals(locals));
1153     }
1154   
1155     foreach(arg; runArgs[0 .. $-1]) {
1156       addType(arg);
1157       app.put(";");
1158     }
1159     
1160     addType(runArgs[$-1]);
1161     app.put("\", ");
1162 
1163     app.put("tuple(");
1164     semantics.dumpDJsArguments(item.args, app, true, locals);
1165     app.put(")");    
1166   }
1167 
1168   string findVarArgMangle(){
1169     static import std.array;
1170     std.array.Appender!string app;
1171 
1172     // we ignore static (console.log)
1173     if (item.type & FunctionType.Static) return null;
1174 
1175     // we ignore callbacks
1176     foreach (arg; runArgs) {
1177       if (isCallback(semantics, arg.type) || (!isPrimitive(semantics, arg.type) && arg.name.friendlyName == "listener" || arg.name.friendlyName == "handler")) return null;
1178     }
1179 
1180     auto context = Context(semantics).withLocals(locals);
1181 
1182     if (!returns) {
1183       // setter // caller
1184       // generics that return void
1185       app ~= "Serialize_Object_VarArgCall!void";
1186       dumpCallVarArgs(app, item.name);
1187     } else {
1188       auto tree = item.result;
1189 
1190       bool optional = !context.skipOptional && (semantics.isNullable(tree) || tree.children[$-1].name == "WebIDL.Null" && tree.children[$-1].matches[0] == "?");
1191 
1192       // skip optional returns for now
1193       //if (optional) return null;
1194 
1195       if (semantics.isAny(tree) || (!semantics.isNullable(tree) && !semantics.isPrimitive(tree) && !context.sumType && !(context.returnType && tree.children[0].name == "WebIDL.SequenceType")))
1196       {
1197         app ~= "Serialize_Object_VarArgCall!Handle";
1198         dumpCallVarArgs(app, item.name);
1199       }
1200       else if (optional && !semantics.isCallback(tree)) 
1201       {
1202         // generics that return Optional!(T)
1203         auto baseType = tree.stripNullable;
1204 
1205         // T = baseType
1206         // sub member
1207         
1208         if (semantics.isPrimitive(baseType) && !semantics.isCallback(baseType) && !semantics.isUnion(baseType)) {
1209           // Optional! string, int, float, etc          
1210           auto retSubType = generateDType(baseType, context);
1211 
1212           switch (retSubType){
1213             case "string":
1214               app ~= "Serialize_Object_VarArgCall!(Optional!string)";
1215               dumpCallVarArgs(app, item.name);
1216               break;
1217             case "u32":
1218             case "uint":
1219               app ~= "Serialize_Object_VarArgCall!(Optional!uint)";
1220               dumpCallVarArgs(app, item.name);
1221 
1222               break;
1223             case "double":
1224               app ~= "Serialize_Object_VarArgCall!(Optional!double)";
1225               dumpCallVarArgs(app, item.name);
1226               break;
1227             case "bool":
1228               app ~= "Serialize_Object_VarArgCall!(Optional!bool)";
1229               dumpCallVarArgs(app, item.name);
1230               break;
1231             default: 
1232               //writeln("Ignored Optional!~", retSubType);
1233             break;
1234           }
1235         }
1236         else if (!semantics.isPrimitive(baseType) && !semantics.isCallback(baseType) && !semantics.isUnion(baseType))
1237         {
1238           // Optional!Handle / Objects that resolve to a handle
1239           app ~= "Serialize_Object_VarArgCall!(Optional!Handle)";
1240           dumpCallVarArgs(app, item.name);
1241         }
1242       }
1243       else if (semantics.isEnum(tree) || (context.isPrimitive(tree) && !semantics.isCallback(tree))) 
1244       {
1245 
1246         auto retSubType = generateDType(tree, context);
1247         import std.string : indexOf;
1248         assert(retSubType.indexOf("!") == -1);
1249         switch (retSubType) {
1250           case "string":
1251             app ~= "Serialize_Object_VarArgCall!string";
1252             dumpCallVarArgs(app, item.name);
1253             break;
1254           case "int":
1255             app ~= "Serialize_Object_VarArgCall!int";
1256             dumpCallVarArgs(app, item.name);
1257             break;
1258           case "u32":
1259           case "uint":
1260             app ~= "Serialize_Object_VarArgCall!uint";
1261             dumpCallVarArgs(app, item.name);
1262             break;
1263           case "ushort":
1264             app ~= "Serialize_Object_VarArgCall!ushort";
1265             dumpCallVarArgs(app, item.name);
1266             break;
1267           case "short":
1268             app ~= "Serialize_Object_VarArgCall!short";
1269             dumpCallVarArgs(app, item.name);
1270             break;
1271           case "bool":
1272             app ~= "Serialize_Object_VarArgCall!bool";
1273             dumpCallVarArgs(app, item.name);
1274             break;
1275           case "float":
1276             app ~= "Serialize_Object_VarArgCall!float";
1277             dumpCallVarArgs(app, item.name);
1278             break;
1279           case "double":
1280             app ~= "Serialize_Object_VarArgCall!double";
1281             dumpCallVarArgs(app, item.name);
1282             break;
1283           case "long":
1284             app ~= "Serialize_Object_VarArgCall!long";
1285             dumpCallVarArgs(app, item.name);
1286             break;
1287           case "ulong":
1288             app ~= "Serialize_Object_VarArgCall!ulong";
1289             dumpCallVarArgs(app, item.name);
1290             break;
1291           default:
1292             if (semantics.isEnum(tree)) {                
1293               app ~= "Serialize_Object_VarArgCall!int";
1294               dumpCallVarArgs(app, item.name);
1295               break;
1296             } else {
1297               //writeln("Ignored Primitive ", retSubType);
1298               break;
1299             }
1300         }
1301 
1302       }
1303     }
1304     return app.data;
1305 
1306   }
1307 
1308   string findGenericMangle() {
1309     static import std.array;
1310     std.array.Appender!string app;
1311     bool static_func = !!(item.type & FunctionType.Static);
1312     string static_name = item.parentTypeName;
1313     auto context = Context(semantics).withLocals(locals);
1314     if (!returns) {
1315       // setter // caller
1316       // TODO: EventHandler
1317       // generics that return void
1318       switch (item.args.length) {
1319         case 0:              
1320             app ~= (static_func ? "Static" : "Object") ~ "_Call__void";
1321             dumpCallArgs(app, item.name, static_func ? static_name : null);
1322             break;              
1323         case 1:
1324           string arg0type = item.args[0].type.generateDType(context);
1325           if (semantics.isEnum(item.args[0].type))
1326             arg0type = "int";
1327           switch (arg0type) {
1328             case "string":
1329               app ~= (static_func ? "Static" : "Object") ~ "_Call_string__void";
1330               dumpCallArgs(app, item.name, static_func ? static_name : null);
1331               break;
1332             case "u32":
1333             case "uint":
1334               app ~= (static_func ? "Static" : "Object") ~ "_Call_uint__void";
1335               dumpCallArgs(app, item.name, static_func ? static_name : null);
1336               break;
1337             case "int":
1338               app ~= (static_func ? "Static" : "Object") ~ "_Call_int__void";
1339               dumpCallArgs(app, item.name, static_func ? static_name : null);
1340               break;
1341             case "bool":
1342               app ~= (static_func ? "Static" : "Object") ~ "_Call_bool__void";
1343               dumpCallArgs(app, item.name, static_func ? static_name : null);
1344               break;
1345             case "double":
1346               app ~= (static_func ? "Static" : "Object") ~ "_Call_double__void";
1347               dumpCallArgs(app, item.name, static_func ? static_name : null);
1348               break;
1349             case "float":
1350               app ~= (static_func ? "Static" : "Object") ~ "_Call_float__void";
1351               dumpCallArgs(app, item.name, static_func ? static_name : null);
1352               break;
1353             case "EventHandler":
1354               app ~= (static_func ? "Static" : "Object") ~ "_Call_EventHandler__void";
1355               dumpCallArgs(app, item.name, static_func ? static_name : null);
1356               break;
1357             case "Any":
1358             case "Handle":
1359               app ~= (static_func ? "Static" : "Object") ~ "_Call_Handle__void";
1360               dumpCallArgs(app, item.name, static_func ? static_name : null);
1361               break;
1362             default:
1363               if (!semantics.isNullable(item.args[0].type) && !semantics.isPrimitive(item.args[0].type) && !semantics.isUnion(item.args[0].type) && !semantics.isCallback(item.args[0].type))
1364               {
1365                 // may have a handle?
1366                 app ~= (static_func ? "Static" : "Object") ~ "_Call_Handle__void";
1367                 dumpCallArgs(app, item.name, static_func ? static_name : null);
1368               }
1369             break;
1370           }
1371           break;
1372         case 2:
1373           if (item.args[0].type.generateDType(context) == "string"
1374           && item.args[1].type.generateDType(context) == "string") {
1375               app ~= (static_func ? "Static" : "Object") ~ "_Call_string_string__void";
1376               dumpCallArgs(app, item.name, static_func ? static_name : null);
1377               break;
1378           }
1379           if (item.args[0].type.generateDType(context) == "string"
1380           && (item.args[1].type.generateDType(context) == "uint" || item.args[1].type.generateDType(context) == "u32")) {
1381               app ~= (static_func ? "Static" : "Object") ~ "_Call_string_uint__void";
1382               dumpCallArgs(app, item.name, static_func ? static_name : null);
1383               break;
1384           }
1385           if (item.args[0].type.generateDType(context) == "double"
1386           && item.args[1].type.generateDType(context) == "double") {
1387               app ~= (static_func ? "Static" : "Object") ~ "_Call_double_double__void";
1388               dumpCallArgs(app, item.name, static_func ? static_name : null);
1389               break;
1390           }
1391           break;
1392         default: break;  
1393       }
1394       
1395     } else {
1396       // getter // caller
1397 
1398       // resolve return type
1399 
1400       auto tree = item.result;
1401 
1402       bool optional = !context.skipOptional && (semantics.isNullable(tree) || tree.children[$-1].name == "WebIDL.Null" && tree.children[$-1].matches[0] == "?");
1403 
1404       if (semantics.isAny(tree) || (!semantics.isNullable(tree) && !semantics.isPrimitive(tree) && !context.sumType && !(context.returnType && tree.children[0].name == "WebIDL.SequenceType")))
1405       {
1406         // generics that return Handle
1407           //auto subType = semantics.getAliasedType(tree.getTypeName());
1408           auto retSubType = generateDType(tree, context);
1409           import std.string : indexOf;
1410           
1411           switch (item.args.length) {
1412             case 0:              
1413                 app ~= (static_func ? "Static" : "Object") ~ "_Getter__Handle";
1414                 dumpCallArgs(app, item.name, static_func ? static_name : null);
1415                 break;              
1416             case 1:
1417               string arg0type = item.args[0].type.generateDType(context);
1418               if (semantics.isEnum(item.args[0].type))
1419                 arg0type = "int";
1420               switch (arg0type) {
1421                 case "string":
1422                   app ~= (static_func ? "Static" : "Object") ~ "_Call_string__Handle";
1423                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1424                   break;
1425                 case "u32":
1426                 case "uint":
1427                   app ~= (static_func ? "Static" : "Object") ~ "_Call_uint__Handle";
1428                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1429                   break;
1430                 case "int":
1431                   app ~= (static_func ? "Static" : "Object") ~ "_Call_int__Handle";
1432                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1433                   break;
1434                 case "bool":
1435                   app ~= (static_func ? "Static" : "Object") ~ "_Call_bool__Handle";
1436                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1437                   break;
1438                 case "Any":
1439                 case "Handle":
1440                   app ~= (static_func ? "Static" : "Object") ~ "_Call_Handle__Handle";
1441                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1442                   break;
1443                 default: 
1444                 
1445                   if (!semantics.isNullable(item.args[0].type) && !semantics.isPrimitive(item.args[0].type) && !semantics.isUnion(item.args[0].type) && !semantics.isCallback(item.args[0].type))
1446                   {
1447                     // may have a handle?
1448                     app ~= (static_func ? "Static" : "Object") ~ "_Call_Handle__Handle";
1449                     dumpCallArgs(app, item.name, static_func ? static_name : null);
1450 
1451                   }
1452                 break;
1453               }
1454               break;
1455             case 2:
1456               if (item.args[0].type.generateDType(context) == "string"
1457               && item.args[1].type.generateDType(context) == "string") {
1458                   app ~= (static_func ? "Static" : "Object") ~ "_Call_string_string__Handle";
1459                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1460                   break;
1461               }
1462               if (item.args[0].type.generateDType(context) == "string"
1463               && (item.args[1].type.generateDType(context) == "uint" || item.args[1].type.generateDType(context) == "u32" )) {
1464                   app ~= (static_func ? "Static" : "Object") ~ "_Call_string_uint__Handle";
1465                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1466                   break;
1467               }
1468               break;
1469             default: break;  
1470           }
1471       }  
1472       else if (optional && !semantics.isCallback(tree)) 
1473       {
1474         // generics that return Optional!(T)
1475         auto baseType = tree.stripNullable;
1476 
1477         // T = baseType
1478         // sub member
1479         
1480         if (semantics.isPrimitive(baseType) && !semantics.isCallback(baseType) && !semantics.isUnion(baseType)) {
1481           // Optional! string, int, float, etc          
1482           auto retSubType = generateDType(baseType, context);
1483           switch (item.args.length) {
1484             case 0:              
1485               switch (retSubType){
1486                 case "string":
1487                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__OptionalString";
1488                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1489                   break;
1490                 case "u32":
1491                 case "uint":
1492                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__OptionalUint";
1493                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1494                   break;
1495                 case "double":
1496                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__OptionalDouble";
1497                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1498                   break;
1499                 case "bool":
1500                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__OptionalBool";
1501                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1502                   break;
1503                 default: 
1504                   //writeln("Ignored Optional!~", retSubType);
1505                 break;
1506               }
1507               break;
1508             case 1:
1509               if (retSubType == "string") switch (item.args[0].type.generateDType(context)){
1510                 case "string":
1511                   app ~= (static_func ? "Static" : "Object") ~ "_Call_string__OptionalString";
1512                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1513                   break;
1514                  default:
1515                   //writeln("Ignored Optional!~", retSubType);
1516                  break;
1517               }
1518               break;
1519             default:
1520               break;
1521           }
1522 
1523 
1524         }
1525         else if (!semantics.isPrimitive(baseType) && !semantics.isCallback(baseType) && !semantics.isUnion(baseType))
1526         {
1527           // Optional!Handle / Objects that resolve to a handle
1528           ParseTree subType = baseType;
1529           string retSubType;
1530           if (semantics.isTypedef(baseType)) {
1531             subType = semantics.getAliasedType(baseType.getTypeName());
1532           }
1533           retSubType = generateDType(subType, context); 
1534           
1535           switch (item.args.length) {
1536             case 0:              
1537                 app ~= (static_func ? "Static" : "Object") ~ "_Getter__OptionalHandle";
1538                 dumpCallArgs(app, item.name, static_func ? static_name : null);
1539                 break;              
1540             case 1:
1541               switch (item.args[0].type.generateDType(context)) {
1542                 case "string":
1543                   app ~= (static_func ? "Static" : "Object") ~ "_Call_string__OptionalHandle";
1544                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1545                   break;
1546                 case "u32":
1547                 case "uint":
1548                   app ~= (static_func ? "Static" : "Object") ~ "_Call_uint__OptionalHandle";
1549                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1550                   break;
1551                 case "int":
1552                   app ~= (static_func ? "Static" : "Object") ~ "_Call_int__OptionalHandle";
1553                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1554                   break;
1555                 case "bool":
1556                   app ~= (static_func ? "Static" : "Object") ~ "_Call_bool__OptionalHandle";
1557                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1558                   break;
1559                 default: 
1560                   //writeln("Ignored Optional!~", retSubType);
1561                 break;
1562               }
1563               break;
1564             default:
1565               break;          
1566           }
1567           
1568         }
1569       }
1570       else if (context.isPrimitive(tree) && !semantics.isCallback(tree)) 
1571       {
1572 
1573         // generics that return string, int, float, etc.
1574           auto retSubType = generateDType(tree, context);
1575           import std.string : indexOf;
1576           assert(retSubType.indexOf("!") == -1);
1577           
1578           if (semantics.isEnum(tree))
1579             retSubType = "int";
1580           switch (item.args.length) {
1581             case 0:
1582               switch (retSubType) {
1583                 case "string":
1584                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__string";
1585                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1586                   break;
1587                 case "int":
1588                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__int";
1589                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1590                   break;
1591                 case "u32":
1592                 case "uint":
1593                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__uint";
1594                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1595                   break;
1596                 case "ushort":
1597                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__ushort";
1598                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1599                   break;
1600                 case "bool":
1601                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__bool";
1602                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1603                   break;
1604                 case "float":
1605                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__float";
1606                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1607                   break;
1608                 case "double":
1609                   app ~= (static_func ? "Static" : "Object") ~ "_Getter__double";
1610                   dumpCallArgs(app, item.name, static_func ? static_name : null);
1611                   break;
1612                 default:
1613                   //writeln("Ignored Primitive ", retSubType);
1614                   break;
1615               }
1616               break;  
1617             case 1:
1618               switch (item.args[0].type.generateDType(context)) {
1619                 case "string":
1620                   switch(retSubType) {
1621                     case "bool":                      
1622                       app ~= (static_func ? "Static" : "Object") ~ "_Call_string__bool";
1623                       dumpCallArgs(app, item.name, static_func ? static_name : null);
1624                       break;
1625                     case "string":
1626                       app ~= (static_func ? "Static" : "Object") ~ "_Call_string__string";
1627                       dumpCallArgs(app, item.name, static_func ? static_name : null);
1628                       break;
1629                     default:                      
1630                       //writeln("Ignored Primitive1 ", retSubType);
1631                     break;
1632                   }
1633                   break;
1634                 case "u32":
1635                 case "uint":
1636                   switch (retSubType) {
1637                     case "string":
1638                       app ~= (static_func ? "Static" : "Object") ~ "_Call_uint__string";
1639                       dumpCallArgs(app, item.name, static_func ? static_name : null);
1640                       break;
1641                     default: break;
1642                   }
1643                   break;
1644                 default: break;
1645               }
1646               break;
1647             case 2:
1648                 switch (item.args[0].type.generateDType(context)) {
1649                   case "uint":
1650                   case "u32":
1651                     switch (item.args[1].type.generateDType(context)) {
1652                       case "uint":
1653                       case "u32":
1654                         switch (retSubType) {
1655                           case "string":
1656                             app ~= (static_func ? "Static" : "Object") ~ "_Call_uint_uint__string";
1657                             dumpCallArgs(app, item.name, static_func ? static_name : null);
1658                             break;
1659                           default: break;
1660                         }
1661                         break;
1662                       default: break;
1663                     }
1664                     break;
1665                   default: break;
1666                 }
1667                 break;
1668             default:
1669               break;          
1670           }
1671       }
1672     }
1673     if (app.data.length == 0 && returns && semantics.isCallback(item.result)) {
1674       // returns EventHandler
1675       auto retSubType = generateDType(item.result, context);
1676       if (item.args.length == 0 && retSubType == "EventHandler") {
1677         app ~= (static_func ? "Static" : "Object") ~ "_Getter__EventHandler";
1678         dumpCallArgs(app, item.name, static_func ? static_name : null);
1679       }
1680     }
1681 
1682     return app.data;
1683   }
1684 
1685   ///// if generic is available
1686   //// AccessibleNode_roleDescription_Set => Object_setter_String__void("roleDescription", Handle, Value)
1687   //// AccessibleNode_roleDescription_Get => Object_getter__String("roleDescription", Handle)
1688   string generic_call = findGenericMangle();
1689   string vararg_call;
1690   if (!generic_call) vararg_call = findVarArgMangle();
1691 
1692   if (generic_call) {
1693     isGeneric[mangleName(item.parentTypeName, item.customName.length > 0 ? item.customName : item.name,item.manglePostfix)] = true;
1694     a.put(generic_call);
1695   }
1696   else if (vararg_call && !vararg_call.canFind("%Error%")) {
1697     isGeneric[mangleName(item.parentTypeName, item.customName.length > 0 ? item.customName : item.name,item.manglePostfix)] = true;
1698     
1699     a.put(vararg_call);
1700 
1701   }
1702   ///// if no generic available we check for varargs
1703   /*
1704     import libwasm.rt.allocator;
1705     import fast.json;
1706     import std.typecons: tuple;
1707     char[] buf = cast(char[]) ThreadMemAllocator.allocate(serializationLength(tuple(btoa)));
1708     scope(exit) ThreadMemAllocator.deallocate(buf);
1709     auto args = serializeJSON(buf, tuple(btoa));
1710     return Object_VarArgCall__string(this._parent, "btoa", "string", cast(string)args);
1711   */
1712   else {
1713     a.put(mangleName(item.parentTypeName, item.customName.length > 0 ? item.customName : item.name,item.manglePostfix));
1714     dumpCallArgs(a, null);
1715   }
1716   if (returns) {
1717     if (!semantics.isPrimitive(item.result) && !semantics.isUnion(item.result)) {
1718       a.put(")");
1719     }
1720   }
1721   a.putLn(");");
1722   if (item.generic) {
1723     a.put("// ");
1724     a.put(mangleName(item.parentTypeName, item.customName.length > 0 ? item.customName : item.name,item.manglePostfix));
1725     dumpCallArgs(a, null);
1726     a.putLn("");
1727   }
1728   foreach(any; anys) {
1729     a.putLn(["dropHandle!(T", any.index.to!string, ")(_handle_", any.value.name, ");"]);
1730   }
1731   if (returns && needDropHandle)
1732     a.putLn("return result;");
1733   a.undent();
1734   a.putLn("}");
1735 }
1736 void dumpDParameters(Appender)(ref Semantics semantics, Argument[] args, ref Appender a, string[] locals) {
1737   if (args.length == 0)
1738     return;
1739   foreach(i, arg; args[0..$-1]) {
1740     semantics.dumpDParameter(arg, a, locals, i);
1741     a.put(", ");
1742   }
1743   semantics.dumpDParameter(args[$-1], a, locals, args.length-1);
1744 }
1745 void dumpDParameter(Appender)(ref Semantics semantics, Argument arg, ref Appender a, string[] locals, size_t idx) {
1746   if (semantics.isAny(arg.type)) {
1747     a.put("scope auto ref ");
1748     a.put(getTemplatedTypeName(idx));
1749   } else {
1750     if (semantics.isNullable(arg.type)) {
1751       a.put("scope auto ref Optional!(");
1752       a.put(getTemplatedTypeName(idx));
1753       a.put(")");
1754     } else {
1755       if (!semantics.isPrimitive(arg.type))
1756         a.put("scope ref ");
1757       arg.type.generateDType(a, Context(semantics).withLocals(locals));
1758     }
1759   }
1760   a.put(" ");
1761   a.putCamelCase(arg.name.friendlyName);
1762   if (arg.default_.matches.length > 1) {
1763     a.put(" ");
1764     if (arg.default_.children[0].matches[0] == "null") {
1765       if (semantics.isNullable(arg.type)) {
1766         a.put("/* = no!(");
1767         arg.type.generateDType(a, Context(semantics).withSkipOptional.withLocals(locals).setDParameter);
1768         a.put(") */");
1769         return;
1770       }
1771     }
1772     arg.default_.generateDType(a, Context(semantics).withLocals(locals).setDParameter);
1773   }
1774 }
1775 
1776 void dumpDJsArguments(Appender)(ref Semantics semantics, Argument[] args, ref Appender a, bool varargs = false, string[] locals = null) {
1777   if (args.length == 0)
1778     return;
1779   foreach(arg; args[0..$-1]) {
1780     semantics.dumpDJsArgument(arg, a, varargs);
1781     a.put(", ");
1782   }
1783   semantics.dumpDJsArgument(args[$-1], a, varargs);
1784 }
1785 
1786 string[] getChildrenOfSumType(T)(T baseType, ref Semantics semantics, string[] locals) {
1787   import std.regex;
1788   string typeName = baseType.getTypeName();
1789   import std.string : split;
1790   string children_str;
1791   if (semantics.isTypedef(typeName))
1792     children_str = generateDType(semantics.getAliasedType(typeName).children[1], Context(semantics).withLocals(locals));
1793   else
1794     children_str = generateDType(baseType, Context(semantics).withLocals(locals));
1795     
1796   if (children_str.startsWith("Optional!")) {
1797     children_str = children_str["Optional!(SumType!(".length .. $-2];
1798   }
1799   else {      
1800     children_str = children_str["SumType!(".length .. $-1];
1801   }
1802   if (children_str.canFind("SumType!")) {
1803     writeln("%ERROR%");
1804     return ["%ERRROR"];
1805   }
1806   return replaceAll(children_str, r"!\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)".regex, "").split(", ");
1807 }
1808 
1809 void dumpDJsArgument(Appender)(ref Semantics semantics, Argument arg, ref Appender a, bool varargs = false, string[] locals = null) {
1810 
1811   
1812   if (semantics.isAny(arg.type)) {
1813     a.put("_handle_");
1814     a.put(arg.name.friendlyName);
1815     return;
1816   }
1817   bool optional = semantics.isNullable(arg.type);
1818   
1819   void dumpUnion() {
1820 
1821     // todo: also add the type as a string first
1822     
1823 
1824     bool comma_needed1 = false;
1825     import std.string : split;
1826     string[] children = getChildrenOfSumType(arg.type, semantics, locals);
1827     string[] flattenChildren(string[] children_) {
1828       string[] flattened_children;
1829       foreach (child; children_) {
1830         bool has_subchildren = false;
1831         auto p = child in semantics.types;
1832         import std.string : replace;
1833         if (!p) p = child.replace(".","") in semantics.types;
1834         // enum ?
1835         if (p) {
1836           if (p.tree.name == "WebIDL.Typedef") {
1837             auto baseType = semantics.getAliasedType(child);
1838             if (semantics.isUnion(baseType)) {
1839               // flatten
1840               string[] subchildren = getChildrenOfSumType(baseType, semantics, locals);
1841               flattened_children ~= flattenChildren(subchildren);
1842               has_subchildren = true;
1843             }
1844           }
1845         }
1846 
1847         if (!has_subchildren) flattened_children ~= child;
1848         
1849       }
1850       return flattened_children;
1851     }
1852     children = flattenChildren(children);
1853 
1854     bool childIsHandle(string child) {
1855       bool is_handle;
1856       auto p = child in semantics.types;
1857       import std.string : replace;
1858       if (!p) p = child.replace(".","") in semantics.types;
1859       if (p) {      
1860         if (p.tree.name != "WebIDL.Enum" && p.tree.name != "WebIDL.Typedef")
1861         {
1862          is_handle = true;
1863         } else if (p.tree.name == "WebIDL.Typedef") {
1864           auto baseType = semantics.getAliasedType(child);
1865           if (!semantics.isPrimitive(baseType) && !semantics.isUnion(baseType) && !semantics.isNullable(baseType)) {
1866             is_handle = true;
1867           }
1868         }
1869       } else {
1870         
1871           bool is_native_type = false;
1872           foreach (native_type; nativeTypes) {
1873             
1874             if (child == native_type || child == "."~native_type || child.startsWith(native_type ~ "!")) {
1875               is_native_type = true;
1876               break;
1877             }
1878           }
1879           if (is_native_type) {
1880             is_handle = true;
1881           }          
1882       }
1883       return is_handle;
1884 
1885     }
1886 
1887     a.put("libwasm.sumtype.match!(");
1888 
1889     foreach(i, child; children) {     
1890       if (comma_needed1) 
1891         a.put("),");
1892       comma_needed1 = true;     
1893       a.put("((");
1894       if (childIsHandle(child)){
1895         
1896           a.put("ref ");
1897           a.put(arg.name.friendlyName);
1898           a.put(".Types[");
1899           a.put(i.to!string);
1900           a.put("]");
1901       }
1902       else a.put(child);
1903       a.put(" v) => ");
1904       a.put(i.to!string);
1905     }
1906     
1907     a.put("))(");
1908     
1909     a.put(arg.name.friendlyName);
1910     a.put("),tuple(");
1911     comma_needed1 = false;
1912 
1913     foreach(i, child; children) {
1914       
1915       if (comma_needed1) {        
1916         a.put(")(");
1917         a.put(arg.name.friendlyName);
1918         a.put("),"); 
1919       }
1920       comma_needed1 = true;
1921       
1922       a.put("libwasm.sumtype.match!(");
1923       string match_type = child;
1924       bool comma_needed2 = false;
1925       bool is_main_type_handle = childIsHandle(child);
1926       foreach(j, child2; children) {   
1927         bool is_handle = childIsHandle(child2);
1928         if (comma_needed2) 
1929           a.put("),");
1930         comma_needed2 = true;     
1931         string type_name = child2;
1932         a.put("((");
1933         if (is_handle) {
1934           a.put("ref ");
1935           a.put(arg.name.friendlyName);
1936           a.put(".Types[");
1937           a.put(j.to!string);
1938           a.put("]");
1939         } else a.put(type_name);
1940         if (type_name == match_type)
1941           if (is_handle) a.put(" v) => cast(Handle)v.handle");
1942           else a.put(" v) => v");
1943         else {
1944           a.put(" v) => ");
1945           if (is_main_type_handle) {
1946             a.put("Handle");
1947           } 
1948           else a.put(match_type);
1949           a.put(".init");
1950         }
1951       }
1952     
1953       a.put(")");  
1954 
1955     }
1956     a.put(")(");
1957     a.put(arg.name.friendlyName);
1958     a.put("))"); 
1959   }
1960   if (optional)
1961     a.put("!");
1962     
1963   if (!optional && !semantics.isPrimitive(arg.type) && !semantics.isUnion(arg.type) && varargs) a.put("cast(Handle)");
1964 
1965   if (varargs && !optional && semantics.isUnion(arg.type)){
1966     dumpUnion();
1967     return;
1968   }
1969 
1970   a.put(arg.name.friendlyName);
1971   if (optional) {
1972     if (!semantics.isUnion(arg.type)) {
1973       a.put(".empty, ");
1974       if (!semantics.isPrimitive(arg.type) && !semantics.isUnion(arg.type) && varargs) a.put("cast(Handle)");
1975       a.put(arg.name.friendlyName);
1976       a.put(".front");
1977     } else {
1978       a.put(".empty, ");
1979       if (varargs) {
1980         dumpUnion();
1981       } else {
1982         if (!semantics.isPrimitive(arg.type) && !semantics.isUnion(arg.type) && varargs) a.put("cast(Handle)");
1983         a.put("*");
1984         a.put(arg.name.friendlyName);
1985         a.put(".frontRef");
1986       }   
1987     }
1988   } 
1989   if (!semantics.isPrimitive(arg.type) && !semantics.isUnion(arg.type)) {
1990 
1991     auto s = arg.type.matches[0] in semantics.types;
1992     if (s !is null) {
1993       if (s.tree.children[1].matches.length > 1)
1994         a.put("._parent");
1995       else
1996         a.put(".handle");
1997     } else
1998       a.put(".handle");
1999   }
2000 }
2001 
2002 bool isSequence(Semantics semantics, ParseTree tree) {
2003   if (tree.name == "WebIDL.ReturnType") {
2004     if (tree.matches[0] == "void")
2005       return false;
2006     return semantics.isSequence(tree.children[0]);
2007   }
2008   if (tree.name == "WebIDL.Type") {
2009     if (tree.children[0].name == "WebIDL.SingleType")
2010       return false;
2011     return tree.children[0].children[0].children[0].name == "WebIDL.SequenceType";
2012   }
2013   if (tree.name == "WebIDL.UnionMemberType")
2014     return tree.children[1].children[0].name == "WebIDL.SequenceType";
2015   if (tree.name == "WebIDL.NonAnyType")
2016     return tree.children[0].name == "WebIDL.SequenceType";
2017   return false;
2018 }
2019 
2020 bool isAny(ref Semantics semantics, ParseTree tree) {
2021   return tree.matches[0] == "any";
2022 }
2023 bool isDKeyword(string s) {
2024   import std.algorithm : canFind;
2025   return dKeywords.canFind(s);
2026 }
2027 bool isJsKeyword(string s) {
2028   import std.algorithm : canFind;
2029   return jsKeywords.canFind(s);
2030 }
2031 
2032 string friendlyJsName(string s) {
2033   import std.ascii;
2034   import std.conv : text;
2035   import std.utf : byChar;
2036   if (s.length == 0)
2037     return s;
2038   if (s.isJsKeyword)
2039     return s~"_";
2040   return s;
2041 }
2042 
2043 string friendlyName(string s) {
2044   import std.ascii;
2045   import std.conv : text;
2046   import std.utf : byChar;
2047   if (s.length == 0)
2048     return s;
2049   if (s.isDKeyword)
2050     return s~"_";
2051   string clean = s.byChar.map!(c => c.isAlphaNum ? c : '_').text;
2052   if (!clean[0].isAlpha && clean[0] != '_')
2053     return '_'~clean;
2054   return clean;
2055 }
2056 
2057 struct IndentedStringAppender {
2058   import std.array : Appender;
2059   import std.algorithm : each;
2060   bool beginLine = true;
2061   Appender!string appender;
2062   int i = 0;
2063   void put(char c) {
2064     putIndent();
2065     appender.put(c);
2066   }
2067   void put(string s) {
2068     putIndent();
2069     appender.put(s);
2070   }
2071   void put(string[] ss) {
2072     putIndent();
2073     ss.each!(s => appender.put(s));
2074   }
2075   void putLn(char c) {
2076     put(c);
2077     appender.put("\n");
2078     beginLine = true;
2079   }
2080   void putLn(string s) {
2081     put(s);
2082     appender.put("\n");
2083     beginLine = true;
2084   }
2085   void putLn(string[] ss) {
2086     put(ss);
2087     appender.put("\n");
2088     beginLine = true;
2089   }
2090   void putIndent() {
2091     if (!beginLine)
2092       return;
2093     beginLine = false;
2094     import std.range : repeat;
2095     import std.algorithm : copy;
2096     ' '.repeat(i*2).copy(appender);
2097   }
2098   void indent() {
2099     i++;
2100   }
2101   void undent() {
2102     import std.algorithm : max;
2103     i = max(0, i-1);
2104   }
2105   auto data() {
2106     return appender.data;
2107   }
2108 }
2109 
2110 struct Context {
2111   Semantics semantics;
2112   ParseTree extendedAttributeList;
2113   ParseTree partial;
2114   ParseTree includes;
2115   bool readonly = false;
2116   bool primitiveType = false;
2117   bool sumType = false;
2118   bool optional = false;
2119   bool returnType = false;
2120   bool isIncludes = false;
2121   bool skipOptional = false;
2122   bool dParameter = false;
2123   string typeName;
2124   string customName;
2125   string[] locals;
2126 }
2127 
2128 auto withLocals(Context c, string[] locals) {
2129   c.locals = locals;
2130   return c;
2131 }
2132 
2133 auto setDParameter(Context c) {
2134   c.dParameter = true;
2135   return c;
2136 }
2137 
2138 auto withSkipOptional(Context c) {
2139   c.skipOptional = true;
2140   return c;
2141 }
2142 
2143 bool isEnum(ref Context context, ParseTree tree) {
2144   return context.semantics.isEnum(tree);
2145 }
2146 bool isEnum(ref Semantics semantics, ParseTree tree) {
2147   if (tree.name == "WebIDL.TypeWithExtendedAttributes" || tree.name == "WebIDL.UnionMemberType")
2148     return semantics.isEnum(tree.children[1]);
2149   return semantics.isEnum(tree.matches[0]);
2150 }
2151 
2152 bool isEnum(ref Semantics semantics, string typeName) {
2153   if (auto p = typeName in semantics.types) {
2154     return p.tree.name == "WebIDL.Enum";
2155   }
2156   return false;
2157 }
2158 bool isNullableTypedef(ref Semantics semantics, ParseTree tree) {
2159   if (tree.name == "WebIDL.ReturnType") {
2160     if (tree.matches[0] == "void")
2161       return false;
2162     return semantics.isNullableTypedef(tree.children[0]);
2163   }
2164   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
2165     return semantics.isNullableTypedef(tree.children[1]);
2166   assert(tree.name == "WebIDL.Type" || tree.name == "WebIDL.UnionMemberType");
2167   if (tree.name == "WebIDL.UnionMemberType" && tree.children[0].name == "WebIDL.UnionType")
2168     return false;
2169   string typeName = tree.getTypeName();
2170   if (!semantics.isTypedef(typeName))
2171     return false;
2172   if (tree.matches[$-1] == "?")
2173     return true;
2174   return false;
2175 }
2176 
2177 bool isTypedef(ref Context context, ParseTree tree) {
2178   return context.semantics.isTypedef(tree);
2179 }
2180 
2181 bool isTypedef(ref Semantics semantics, ParseTree tree) {
2182   string typeName = tree.getTypeName();
2183   return semantics.isTypedef(typeName);
2184 }
2185 
2186 bool isTypedef(ref Context context, string typeName) {
2187   return context.semantics.isTypedef(typeName);
2188 }
2189 bool isTypedef(ref Semantics semantics, string typeName) {
2190   if (auto p = typeName in semantics.types) {
2191     return p.tree.name == "WebIDL.Typedef";
2192   }
2193   return false;
2194 }
2195 bool isCallback(ref Context context, string typeName) {
2196   return context.semantics.isCallback(typeName);
2197 }
2198 bool isCallback(ref Semantics semantics, ParseTree tree) {
2199   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
2200     return semantics.isCallback(tree.children[1].matches[0]);
2201   return semantics.isCallback(tree.children[0].matches[0]);
2202 }
2203 bool isCallback(ref Semantics semantics, string typeName) {
2204   if (semantics.isTypedef(typeName)) {
2205     auto aliased = semantics.getAliasedType(typeName);
2206     return semantics.isCallback(aliased);
2207   }
2208   if (auto p = typeName in semantics.types) {
2209     return p.tree.name == "WebIDL.CallbackRest";
2210   }
2211   return false;
2212 }
2213 
2214 bool isPartial(ref Context context) {
2215   return context.partial.matches.length > 0;
2216 }
2217 
2218 void putCamelCase(Appender)(ref Appender a, string s) {
2219   import std.algorithm : until;
2220   import std.uni : isUpper, isLower, asLowerCase;
2221   import std.conv : text;
2222   if (s.length == 0)
2223     return;
2224   if (s[0].isLower) {
2225     a.put(s);
2226     return;
2227   }
2228   import std.string : toLower;
2229   auto head = s.until!(isLower).asLowerCase.text;
2230   if (head.length == 1) {
2231     a.put(head);
2232     a.put(s[head.length .. $]);
2233     return;
2234   }
2235   auto tail = s[head.length-1 .. $];
2236   a.put(head[0..$-1]);
2237   a.put(tail);
2238 }
2239 
2240 string toCamelCase(string s) {
2241   auto app = appender!string;
2242   app.putCamelCase(s);
2243   return app.data;
2244 }
2245 
2246 string mangleJsName(StructNode node, FunctionNode fun) {
2247   return mangleName(node.name, fun.customName != "" ? fun.customName : fun.name, fun.manglePostfix);
2248 }
2249 
2250 string mangleName(string typeName, string name, string appendix = "") {
2251   import std.ascii : toLower, toUpper;
2252   import std.array : appender;
2253   auto app = appender!string;
2254   app.put(typeName);
2255   app.put("_");
2256   app.put(name);
2257   if (appendix.length > 0) {
2258     app.put("_");
2259     app.put(appendix);
2260   }
2261   return app.data;
2262 }
2263 
2264 bool isNullable(ref Semantics semantics, ParseTree tree) {
2265   if (tree.name == "WebIDL.ReturnType") {
2266     if (tree.matches[0] == "void")
2267       return false;
2268     return semantics.isNullable(tree.children[0]);
2269   }
2270   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
2271     return semantics.isNullable(tree.children[1]);
2272   if (tree.name == "WebIDL.UnionMemberType")
2273     return tree.matches[$-1] == "?";
2274   if (tree.name == "WebIDL.InterfaceRest")
2275     return false;
2276   assert(tree.name == "WebIDL.Type");
2277   if (tree.matches[$-1] == "?")
2278     return true;
2279   string typeName = tree.getTypeName();
2280   if (semantics.isTypedef(typeName)) {
2281     return semantics.isNullable(semantics.getAliasedType(typeName));
2282   }
2283   return false;
2284 }
2285 
2286 bool isRawResultType(ref Semantics semantics, ParseTree tree) {
2287   if (tree.name == "WebIDL.ReturnType") {
2288     if (tree.matches[0] == "void")
2289       return false;
2290     return semantics.isRawResultType(tree.children[0]);
2291   }
2292   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
2293     return semantics.isRawResultType(tree.children[1]);
2294   if (tree.name == "WebIDL.InterfaceRest")
2295     return false;
2296   assert(tree.name == "WebIDL.Type");
2297   return semantics.isNullable(tree) ||
2298     semantics.isStringType(tree) || semantics.isUnion(tree);
2299 }
2300 
2301 bool isStringType(ref Semantics semantics, ParseTree tree) {
2302   if (tree.name == "WebIDL.ReturnType") {
2303     if (tree.matches[0] == "void")
2304       return false;
2305     return semantics.isStringType(tree.children[0]);
2306   }
2307   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
2308     return semantics.isStringType(tree.children[1]);
2309   if (tree.name == "WebIDL.InterfaceRest")
2310     return false;
2311   assert(tree.name == "WebIDL.Type");
2312   string typeName = tree.getTypeName();
2313   if (semantics.isTypedef(typeName)) {
2314     return semantics.isStringType(semantics.getAliasedType(typeName));
2315   }
2316   if (tree.children[0].name != "WebIDL.SingleType")
2317     return false;
2318   if (tree.children[0].matches[0] == "any")
2319     return false;
2320   if (tree.children[0].children[0].children[0].name == "WebIDL.StringType")
2321     return true;
2322   return false;
2323 }
2324 bool isUnion(ref Context context, ParseTree tree) {
2325   return context.semantics.isUnion(tree);
2326 }
2327 
2328 bool isUnion(ref Semantics semantics, ParseTree tree) {
2329   if (tree.name == "WebIDL.ReturnType") {
2330     if (tree.matches[0] == "void")
2331       return false;
2332     return semantics.isUnion(tree.children[0]);
2333   }
2334   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
2335     return semantics.isUnion(tree.children[1]);
2336   if (tree.name == "WebIDL.InterfaceRest")
2337     return false;
2338   assert(tree.name == "WebIDL.Type" || tree.name == "WebIDL.UnionMemberType");
2339   if (tree.children[0].name == "WebIDL.UnionType")
2340     return true;
2341   string typeName = tree.getTypeName();
2342   if (semantics.isTypedef(typeName)) {
2343     return semantics.isUnion(semantics.getAliasedType(typeName));
2344   }
2345   return false;
2346 }
2347 auto getAliasedType(ref Context context, string typeName) {
2348   assert(typeName in context.semantics.types);
2349   assert(context.semantics.types[typeName].tree.name == "WebIDL.Typedef");
2350   return context.semantics.types[typeName].tree.children[0];
2351 }
2352 
2353 auto getAliasedType(ref Semantics semantics, string typeName) {
2354   assert(typeName in semantics.types);
2355   assert(semantics.types[typeName].tree.name == "WebIDL.Typedef");
2356   return semantics.types[typeName].tree.children[0];
2357 }
2358 
2359 bool isPrimitive(Context context, ParseTree tree) {
2360   return context.semantics.isPrimitive(tree);
2361 }
2362 
2363 bool isPrimitive(ref Semantics semantics, ParseTree tree) {
2364   if (tree.name == "WebIDL.ReturnType") {
2365     if (tree.matches[0] == "void")
2366       return false;
2367     return semantics.isPrimitive(tree.children[0]);
2368   }
2369   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
2370     return semantics.isPrimitive(tree.children[1]);
2371   string typeName = tree.getTypeName();
2372   if (tree.name == "WebIDL.UnionMemberType") {
2373     if (tree.children[1].name != "WebIDL.NonAnyType")
2374       return false;
2375     return tree.children[1].children[0].name == "WebIDL.PrimitiveType";
2376   }
2377   if (tree.name == "WebIDL.InterfaceRest")
2378     return false;
2379   assert(tree.name == "WebIDL.Type");
2380   if (semantics.isEnum(typeName) || semantics.isCallback(typeName))
2381     return true;
2382   if (semantics.isTypedef(typeName)) {
2383     return semantics.isPrimitive(semantics.getAliasedType(typeName));
2384   }
2385   if (tree.children[0].name != "WebIDL.SingleType")
2386     return false;
2387   if (tree.children[0].matches[0] == "any")
2388     return false;
2389   if (semantics.isStringType(tree))
2390     return true;
2391   if (tree.children[0].children[0].name != "WebIDL.NonAnyType")
2392     return false;
2393   return tree.children[0].children[0].children[0].name == "WebIDL.PrimitiveType";
2394 }
2395 
2396 string getTypeName(ParseTree tree) {
2397   if (tree.name == "WebIDL.ReturnType") {
2398     assert(tree.matches[0] != "void");
2399     return tree.children[0].getTypeName();
2400   }
2401   if (tree.name == "WebIDL.InterfaceRest") {
2402     return tree.matches[0];
2403   }
2404   assert(tree.name == "WebIDL.TypeWithExtendedAttributes" || tree.name == "WebIDL.Type" || tree.name == "WebIDL.UnionMemberType");
2405   if (tree.name == "WebIDL.UnionMemberType") {
2406     assert(tree.children[1].name == "WebIDL.NonAnyType");
2407     return tree.children[1].matches[0];
2408   }
2409   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
2410     return tree.children[1].matches[0];
2411   return tree.matches[0];
2412 }
2413 
2414 auto isEmpty(string s) {
2415   return s == "";
2416 }
2417 auto isEmpty(string[] matches) {
2418   import std.algorithm : all;
2419   return matches.length == 0 || matches.all!(m => m.isEmpty);
2420 }
2421 
2422 template putWithDelimiter(alias Generator)
2423 {
2424   void putWithDelimiter(Appender, Ts...)(ParseTree[] children, string delimiter,ref Appender a, Ts args) {
2425     import std.algorithm : each, filter;
2426     import std.array : array;
2427     auto nonEmpty = children.filter!(c => !c.matches.isEmpty).array;
2428     // need to filter first
2429     if (nonEmpty.length > 0) {
2430       nonEmpty[0..$-1].each!((c){if (c.matches.isEmpty) return; Generator(c, a, args);a.put(delimiter);});
2431       Generator(nonEmpty[$-1], a, args);
2432     }
2433   }
2434 }
2435 
2436 auto extractArgument(ParseTree tree) {
2437   assert(tree.name == "WebIDL.Argument");
2438   auto argRest = tree.children[$-1];
2439   if (argRest.matches[0] == "optional") {
2440     return argRest.children[1].matches[0];
2441   } else {
2442     return argRest.children[2].matches[0];
2443   }
2444 }
2445 auto extractDefault(ParseTree tree) {
2446   assert(tree.name == "WebIDL.Argument");
2447   auto argRest = tree.children[$-1];
2448   string typeName;
2449   if (argRest.matches[0] == "optional") {
2450     return argRest.children[2];
2451   } else {
2452     return ParseTree.init;
2453   }
2454 }
2455 auto extractType(ParseTree tree) {
2456   assert(tree.name == "WebIDL.Argument");
2457   auto argRest = tree.children[$-1];
2458   string typeName;
2459   if (argRest.matches[0] == "optional") {
2460     return argRest.children[0].children[1];
2461   } else {
2462     return argRest.children[0];
2463   }
2464 }
2465 
2466 string extractTypeName(ParseTree tree) {
2467   if (tree.name == "WebIDL.Argument") {
2468     auto argRest = tree.children[$-1];
2469     if (argRest.matches[0] == "optional") {
2470       return extractTypeName(argRest.children[0].children[1]);
2471     }
2472     return extractTypeName(argRest.children[0]);
2473   }
2474   assert(tree.name == "WebIDL.Type");
2475   string typeName = tree.matches[0];
2476   if (typeName == "any")
2477     return "Any";
2478   if (typeName == "DOMString")
2479     return "string";
2480   if (typeName == "boolean")
2481     return "bool";
2482   return typeName;
2483 }
2484 auto extractArguments(ParseTree tree) {
2485   import std.algorithm : map;
2486   assert(tree.name == "WebIDL.ArgumentList");
2487   return tree.children.map!(c => c.extractArgument);
2488 }
2489 auto extractArgumentRests(ParseTree tree) {
2490   assert(tree.name == "WebIDL.ArgumentList");
2491   return tree.children.map!(c => c.children[1]);
2492 }
2493 auto extractDefaults(ParseTree tree) {
2494   import std.algorithm : map;
2495   assert(tree.name == "WebIDL.ArgumentList");
2496   return tree.children.map!(c => c.extractDefault);
2497 }
2498 auto extractTypes(ParseTree tree) {
2499   import std.algorithm : map;
2500   assert(tree.name == "WebIDL.ArgumentList");
2501   return tree.children.map!(c => c.extractType);
2502 }
2503 auto extractTypeNames(ParseTree tree) {
2504   import std.algorithm : map;
2505   assert(tree.name == "WebIDL.ArgumentList");
2506   return tree.children.map!(c => c.extractTypeName);
2507 }
2508 ParseTree getArgumentList(ref Semantics semantics, ParseTree tree) {
2509   auto p = tree.matches[0] in semantics.types;
2510   assert(p != null);
2511   assert(p.tree.name == "WebIDL.CallbackRest");
2512   return p.tree.children[2];
2513 }
2514 auto getType(ref Semantics semantics, ParseTree tree) {
2515   return semantics.getType(tree.matches[0]);
2516 }
2517 auto getType(ref Semantics semantics, string name) {
2518   auto p = name in semantics.types;
2519   if (p is null) {
2520     writeln("Failed to find "~name);
2521     return ParseTree.init;
2522   }
2523   return p.tree;
2524 }
2525 auto getType(ref Context context, string name) {
2526   return context.semantics.getType(name);
2527 }
2528 auto getMatchingPartials(ref Semantics semantics, string name) {
2529   auto isInterface = (Type p) => p.tree.children[0].children[0].name == "WebIDL.PartialInterfaceOrPartialMixin";
2530   auto matches = (Type p) => p.tree.children[0].children[0].children[0].children[0].matches[0] == name;
2531   auto matches2 = (Type p) => p.tree.children[0].children[0].children[0].matches[0] == name;
2532   return semantics.partials.filter!(p => (isInterface(p) && matches(p)) || matches2(p) ).map!(t => t.tree).array();
2533 }
2534 
2535 auto getMatchingPartials(ref Context context, string name) {
2536   return context.semantics.getMatchingPartials(name);
2537 }
2538 
2539 uint getSizeOf(ref Semantics semantics, ParseTree tree) {
2540   switch(tree.name) {
2541     case "WebIDL.IntegerType":
2542       if (tree.matches[0] == "long") {
2543         if (tree.matches.length > 1 && !is32Bit)
2544           return 8;
2545         return 4;
2546       }
2547       return 2;
2548   case "WebIDL.StringType":
2549     return 8;
2550   case "WebIDL.FloatType":
2551     if (tree.matches[0] == "float")
2552       return 4;
2553     return 8;
2554   case "WebIDL.PrimitiveType":
2555     if (tree.children.length == 0) {
2556       if (tree.matches[0] == "boolean")
2557         return 4;
2558       return 1;
2559     } else
2560       goto default;
2561   case "WebIDL.SingleType":
2562     if (tree.matches[0] == "any")
2563       return 4;
2564     goto default;
2565   case "WebIDL.Identifier":
2566   case "WebIDL.SequenceType":
2567   case "WebIDL.Enum":
2568   case "WebIDL.RecordType":
2569   case "WebIDL.PromiseType":
2570   case "WebIDL.BufferRelatedType":
2571     return 4;
2572   case "WebIDL.UnionType":
2573     return 4 + tree.children.map!(c => semantics.getSizeOf(c)).maxElement;
2574   case "WebIDL.NonAnyType":
2575     if (tree.matches[0] == "object" || tree.matches[0] == "symbol" || tree.matches[0] == "Error" || tree.matches[0] == "FrozenArray") {
2576       return 4 + semantics.getSizeOf(tree.children[$-1]);
2577     }
2578   goto default;
2579   case "WebIDL.Null":
2580     return tree.matches[0] == "?" ? 4 : 0;
2581   default:
2582     return tree.children.map!(c => semantics.getSizeOf(c)).sum;
2583   }
2584 }
2585 string mangleTypeJs(ParseTree tree, ref Semantics semantics) {
2586   auto app = appender!string;
2587   tree.mangleTypeJs(app, semantics);
2588   return app.data;
2589 }
2590 string mangleTypeJs(ParseTree tree, ref Semantics semantics, MangleTypeJsContext context) {
2591   auto app = appender!string;
2592   tree.mangleTypeJsImpl(app, semantics, context);
2593   return app.data;
2594 }
2595 
2596 struct MangleTypeJsContext {
2597   bool skipOptional;
2598   bool inUnion = false;
2599 }
2600 void mangleTypeJs(Appender)(ParseTree tree, ref Appender a, ref Semantics semantics) {
2601   MangleTypeJsContext context;
2602   tree.mangleTypeJsImpl(a, semantics, context);
2603 }
2604 void mangleTypeJsImpl(Appender)(ParseTree tree, ref Appender a, ref Semantics semantics, MangleTypeJsContext context) {
2605   switch (tree.name) {
2606   case "WebIDL.CallbackRest":
2607     a.put("callback_");
2608     tree.children[1].mangleTypeJsImpl(a, semantics, context);
2609     if (tree.children[2].children.length > 0) {
2610       a.put("_");
2611       tree.children[2].mangleTypeJsImpl(a, semantics, context);
2612     }
2613     break;
2614   case "WebIDL.ArgumentList":
2615     tree.children.putWithDelimiter!(mangleTypeJsImpl)("_", a, semantics, context);
2616     break;
2617   case "WebIDL.Argument":
2618     auto type = tree.extractType;
2619     if (!semantics.isPrimitive(type) && !semantics.isUnion(type)) {
2620       a.put("Handle");
2621       break;
2622     }
2623     tree.children[1].mangleTypeJsImpl(a, semantics, context);
2624     break;
2625   case "WebIDL.UnsignedIntegerType":
2626     if (tree.matches[0] == "unsigned")
2627       a.put("u");
2628     tree.children[0].mangleTypeJsImpl(a, semantics, context);
2629     break;
2630   case "WebIDL.IntegerType":
2631     if (tree.matches[0] == "long") {
2632       if (tree.matches.length > 1)
2633         a.put(is32Bit ? "int" : "long");
2634       else
2635         a.put("int");
2636     } else
2637       a.put(tree.matches[0]);
2638     break;
2639   case "WebIDL.RecordType":
2640     a.put("record");
2641     // tree.children.putWithDelimiter!(mangleTypeJsImpl)("_", a, semantics, context);
2642     break;
2643   case "WebIDL.SequenceType":
2644     if (!context.skipOptional && tree.matches[$-1] == "?")
2645       a.put("optional_");
2646     a.put("sequence");
2647     // tree.children[0].mangleTypeJsImpl(a, semantics, context);
2648     break;
2649   case "WebIDL.SingleType":
2650     if (tree.matches[0] == "any") {
2651       a.put("Handle");
2652     } else if (tree.matches[0] == "void") {
2653       a.put("void");
2654     } else {
2655       tree.children[0].mangleTypeJsImpl(a, semantics, context);
2656     }
2657     break;
2658   case "WebIDL.Type":
2659     string typeName = getTypeName(tree);
2660     if (semantics.isTypedef(typeName)) {
2661       if (tree.matches[$-1] == "?") {
2662         if (context.skipOptional)
2663           context.skipOptional = false;
2664         else
2665           a.put("optional_");
2666       }
2667       auto aliasMangled = semantics.getAliasedType(typeName).mangleTypeJs(semantics, context);
2668       if (aliasMangled.length < typeName.length)
2669         a.put(aliasMangled);
2670       else
2671         a.put(typeName);
2672       return;
2673     }
2674     if (tree.children.length == 2 && tree.children[$-1].matches[0] == "?") {
2675       if (context.skipOptional)
2676         context.skipOptional = false;
2677       else
2678         a.put("optional_");
2679     }
2680     tree.children[0].mangleTypeJsImpl(a, semantics, context);
2681     break;
2682   case "WebIDL.UnionType":
2683     a.put("union");
2684     a.put(tree.children.length.to!string);
2685     a.put("_");
2686     context.inUnion = true;
2687     tree.children.putWithDelimiter!(mangleTypeJsImpl)("_", a, semantics, context);
2688     break;
2689   case "WebIDL.UnionMemberType":
2690     if (tree.children[1].name == "WebIDL.NonAnyType")
2691       tree.children[1].mangleTypeJsImpl(a, semantics, context);
2692     else {
2693       tree.children[0].mangleTypeJsImpl(a, semantics, context);
2694       if (tree.children[$-1].matches[0] == "?")
2695         a.put("_Null");
2696     }
2697     break;
2698   case "WebIDL.NonAnyType":
2699     if (tree.children.length > 1 && tree.children[$-1].matches[0] == "?") {
2700       if (context.skipOptional)
2701         context.skipOptional = false;
2702       else
2703         a.put("optional_");
2704     }
2705     if (tree.children[0].name == "WebIDL.Null")
2706       a.put(tree.matches[0]); // Maybe return here?
2707     if (tree.matches[0] == "FrozenArray")
2708       assert(false);
2709     tree.children[0].mangleTypeJsImpl(a, semantics, context);
2710     break;
2711   case "WebIDL.FloatType":
2712     a.put(tree.matches[0]);
2713     break;
2714   case "WebIDL.TypeWithExtendedAttributes":
2715     tree.children[1].mangleTypeJsImpl(a, semantics, context);
2716     break;
2717   case "WebIDL.Identifier":
2718     auto typeName = tree.matches[0];
2719     if (!context.inUnion && !semantics.isEnum(tree)) {
2720       a.put("Handle");
2721       return;
2722     }
2723     // TODO: could be nullable typedef
2724     if (semantics.isTypedef(typeName)) {
2725       auto aliasMangled = semantics.getAliasedType(typeName).mangleTypeJs(semantics, context);
2726       if (aliasMangled.length < tree.matches[0].length) {
2727         a.put(aliasMangled);
2728         return;
2729       }
2730     }
2731     a.put(tree.matches[0]);
2732     break;
2733   case "WebIDL.StringType":
2734     a.put("string");
2735     break;
2736   case "WebIDL.ArgumentRest":
2737     tree.children[0].mangleTypeJsImpl(a, semantics, context);
2738     break;
2739   case "WebIDL.PrimitiveType":
2740     if (tree.children.length == 1) {
2741       tree.children[0].mangleTypeJsImpl(a, semantics, context);
2742     } else {
2743       switch (tree.matches[0]) {
2744       case "byte": a.put("byte"); break;
2745       case "octet": a.put("ubyte"); break;
2746       case "boolean": a.put("bool"); break;
2747       default: a.put(tree.matches[0]); break;
2748       }
2749     }
2750     break;
2751   default:
2752     tree.children.each!(c => c.mangleTypeJsImpl(a, semantics, context));
2753   }
2754 }
2755 
2756 string generateDImports(ParseTree tree, Context context) {
2757   auto app = IndentedStringAppender();
2758   tree.generateDImports(app, context);
2759   return app.data;
2760 }
2761 void generateDImports(Appender)(ParseTree tree, ref Appender a, Context context) {
2762   import std.algorithm : each, joiner, map;
2763   import std.range : chain;
2764   import std.conv : text;
2765   switch (tree.name) {
2766   case "WebIDL.InterfaceRest":
2767   case "WebIDL.InterfaceMembers":
2768   case "WebIDL.InterfaceMember":
2769   case "WebIDL.ReadOnlyMember":
2770   case "WebIDL.AttributeRest":
2771     break;
2772   case "WebIDL.TypeWithExtendedAttributes":
2773     tree.children[1].generateDImports(a, context);
2774     break;
2775   case "WebIDL.NonAnyType":
2776     bool optional = !context.skipOptional && (context.optional || tree.children[$-1].name == "WebIDL.Null" && tree.children[$-1].matches[0] == "?");
2777     if (optional) {
2778       if (context.returnType)
2779         a.put("Optional!(");
2780       else
2781         a.put("bool, ");
2782     }
2783     if (!(optional && context.returnType) && !context.primitiveType && !context.sumType && !(context.returnType && tree.children[0].name == "WebIDL.SequenceType"))
2784       a.put("Handle");
2785     else
2786       tree.children.each!(c => c.generateDType(a, context));
2787     if (optional && context.returnType)
2788       a.put(")");
2789     break;
2790   case "WebIDL.SingleType":
2791     if (tree.matches[0] == "any") {
2792       a.put("Handle");
2793     } else
2794       tree.children[0].generateDImports(a, context);
2795     break;
2796   case "WebIDL.PrimitiveType":
2797     if (tree.children.length == 0) {
2798       switch (tree.matches[0]) {
2799       case "byte": a.put("byte"); break;
2800       case "octet": a.put("ubyte"); break;
2801       case "boolean": a.put("bool"); break;
2802       default: a.put(tree.matches[0]); break;
2803       }
2804     } else
2805       tree.children[0].generateDImports(a, context);
2806     break;
2807   case "WebIDL.StringType":
2808     a.put("string");
2809     break;
2810   case "WebIDL.ArgumentName":
2811   case "WebIDL.AttributeName":
2812     break;
2813   case "WebIDL.UnrestrictedFloatType":
2814     // TODO: handle unrestricted
2815     tree.children[0].generateDImports(a, context);
2816     break;
2817   case "WebIDL.UnsignedIntegerType":
2818     if (tree.matches[0] == "unsigned")
2819       a.put("u");
2820     tree.children[0].generateDType(a, context);
2821     break;
2822   case "WebIDL.IntegerType":
2823     if (tree.matches[0] == "long") {
2824       if (tree.matches.length > 1)
2825         a.put(is32Bit ? "int" : "long");
2826       else
2827         a.put("int");
2828     } else
2829       a.put(tree.matches[0]);
2830     break;
2831   case "WebIDL.ExtendedAttributeList":
2832     break;
2833   case "WebIDL.FloatType":
2834   case "WebIDL.Identifier":
2835     a.put(tree.matches[0]);
2836     break;
2837   case "WebIDL.SpecialOperation":
2838   case "WebIDL.RegularOperation":
2839     break;
2840   case "WebIDL.Type":
2841     context.optional = false;
2842     if (context.isUnion(tree)) {
2843       bool optional = tree.matches[$-1] == "?";
2844       context.optional = optional;
2845       if (optional) {
2846         if (context.returnType)
2847           a.put("Optional!(");
2848         else
2849           a.put("bool, ");
2850       }
2851       a.put("scope ref ");
2852       if (!context.isTypedef(tree)) {
2853         a.put("SumType!(");
2854       }
2855       context.sumType = true;
2856       tree.children[0].children.putWithDelimiter!(generateDImports)(", ", a, context.withSkipOptional);
2857       if (optional && context.returnType)
2858         a.put(")");
2859       if (!context.isTypedef(tree))
2860         a.put(")");
2861       break;
2862     } else if (context.isTypedef(tree) && context.semantics.isNullable(tree)) {
2863       auto typeName = tree.getTypeName();
2864       auto aliasedType = context.semantics.getAliasedType(typeName);
2865       aliasedType.generateDImports(a, context);
2866     } else {
2867       context.primitiveType = context.isPrimitive(tree);
2868       tree.children[0].generateDImports(a, context);
2869     }
2870     break;
2871   case "WebIDL.UnionMemberType":
2872     context.optional = false;
2873     if (!context.isTypedef(tree) && context.isUnion(tree)) {
2874       bool optional = tree.matches[$-1] == "?";
2875       context.optional = optional;
2876       if (optional) {
2877         if (context.returnType)
2878           a.put("Optional!(");
2879         else
2880           a.put("bool, ");
2881       }
2882       a.put("SumType!(");
2883       tree.children[0].children.putWithDelimiter!(generateDImports)(", ", a, context);
2884       if (optional && context.returnType)
2885         a.put(")");
2886       a.put(")");
2887       break;
2888     } else
2889       tree.children.each!(c => c.generateDImports(a, context));
2890     break;
2891   case "WebIDL.IncludesStatement":  
2892       writeln("No D Imports for include statement");
2893     break;
2894   case "WebIDL.ReturnType":
2895     context.returnType = true;
2896     if (tree.children.length > 0) {
2897       if (context.isPrimitive(tree.children[0]) || context.isUnion(tree.children[0]) || tree.matches[$-1] == "?")
2898         tree.children[0].generateDImports(a, context);
2899       else if (tree.matches[0] != "void")
2900         a.put("Handle");
2901       else
2902         a.put("void");
2903     }
2904     else
2905       a.put(tree.matches[0]);
2906     break;
2907   case "WebIDL.OperationRest":
2908   case "WebIDL.Enum":
2909   case "WebIDL.Dictionary":
2910   case "WebIDL.DictionaryMember":
2911   case "WebIDL.Iterable":
2912   case "WebIDL.Typedef":
2913   case "WebIDL.SetlikeRest":
2914   case "WebIDL.MaplikeRest":
2915   case "WebIDL.CallbackRest":
2916   case "WebIDL.MixinRest":
2917     break;
2918   case "WebIDL.SequenceType":
2919     a.put("Handle");
2920     break;
2921   case "WebIDL.MixinMember":
2922   case "WebIDL.Const":
2923   case "WebIDL.Partial":
2924     break;
2925   case "WebIDL.PromiseType":
2926     a.put("JsPromise!(");
2927     tree.children[0].generateDImports(a, context);
2928     a.put(")");
2929     break;
2930   case "WebIDL.PartialInterfaceRest":
2931     tree.children[1].generateDImports(a, context);
2932     break;
2933   default:
2934     tree.children.each!(c => generateDImports(c, a, context));
2935   }
2936 }
2937 
2938 string orNone(string s) {
2939   if (s.length == 0)
2940     return "none";
2941   return s;
2942 }
2943 
2944 auto createOptionalOverloads(FunctionNode func) {
2945   static bool notOptional(ref Argument arg) {
2946     return arg.argRest.matches[0] != "optional";
2947   }
2948   return chain([func],func.args.retro.until!(notOptional).enumerate.map!((t){
2949         return new FunctionNode(func.name, func.args[0..$-(t.index+1)], func.result, func.type, func.manglePostfix~t.index.to!string, func.baseType, func.customName);
2950       }));
2951 }
2952 
2953 IR toIr(ref Module module_) {
2954   auto app = appender!(Node[]);
2955   module_.iterate!(toIr)(app, Context(module_.semantics));
2956   return new IR([new ModuleNode(module_, app.data)], module_.semantics);
2957 }
2958 IR toIr(ref Semantics semantics) {
2959   auto app = appender!(ModuleNode[]);
2960   foreach(module_; semantics.modules) {
2961     auto mApp = appender!(Node[]);
2962     module_.iterate!(toIr)(mApp, Context(semantics));
2963     app.put(new ModuleNode(module_, mApp.data));
2964   }
2965   return new IR(app.data, semantics);
2966 }
2967 
2968 void toIr(Appender)(ParseTree tree, ref Appender a, Context context) {
2969   switch (tree.name) {
2970   case "WebIDL.Namespace":
2971     // TODO: get matching partials    
2972     auto app = appender!(Node[]);
2973     tree.children[1..$].each!(c => toIr(c, app, context));
2974     a.put(new StructNode(tree.children[0].matches[0], ParseTree.init, app.data, Yes.isStatic));
2975     break;
2976   case "WebIDL.InterfaceRest":
2977     ParseTree baseType;
2978     if (tree.children[1].children.length > 0 && tree.children[1].children[0].matches.length != 0)
2979       baseType = tree.children[1].children[0];
2980     auto app = appender!(Node[]);
2981     tree.children[2..$].each!(c => toIr(c, app, context));
2982     a.put(new StructNode(tree.children[0].matches[0], baseType, app.data));
2983     if (context.extendedAttributeList != ParseTree.init) {
2984       auto constructors = context.extendedAttributeList.children.filter!(attr => attr.matches[0] == "Constructor").array();
2985       auto exposeds = context.extendedAttributeList.children.filter!(attr => attr.matches[0] == "Exposed").map!(e => e.children[0].children[1].children).joiner();
2986       bool overloads = constructors.length > 1;
2987       foreach(constructor; constructors) {
2988         Argument[] args;
2989         string manglePostfix;
2990         if (constructor.children[0].children.length > 1) {
2991           auto argList = constructor.children[0].children[1];
2992           args = zip(argList.extractArguments,argList.extractTypes,argList.extractDefaults,argList.extractArgumentRests).map!(a=>Argument(a[0],a[1],a[2],a[3])).array();
2993         }
2994         if (overloads) {
2995           manglePostfix = "_" ~ args.map!(arg => arg.type.mangleTypeJs(context.semantics)).joiner("_").text;
2996         }
2997         auto name = tree.children[0].matches[0];
2998         auto result = context.semantics.getType(tree.children[0].matches[0]);
2999         foreach(exposed; exposeds) {
3000           auto baseTypeName = exposed.matches[0];
3001           a.put(new ExposedConstructorNode(name, args, result, baseTypeName, manglePostfix));
3002         }
3003       }
3004     }
3005     break;
3006   case "WebIDL.IncludesStatement":
3007     context.isIncludes = true;
3008     context.includes = tree;
3009     context.typeName = tree.children[1].matches[0];
3010     //writeln("Adding includes node ", context.typeName, " : ", tree.children[1].matches[0]);
3011     ParseTree mixinRest = context.getType(context.typeName);
3012     auto partials = context.getMatchingPartials(context.typeName);
3013     auto app = appender!(Node[]);
3014     mixinRest.children[2].toIr(app, context);
3015     partials.each!((c){
3016         if (c.children[0].children[0].children[0].name == "WebIDL.MixinRest")
3017           toIr(c.children[0].children[0].children[0].children[1], app, context);
3018         else
3019           toIr(c.children[0], app, context);
3020       });
3021     //writeln(app.data);
3022     a.put(new StructIncludesNode(tree.children[0].matches[0], tree.children[1].matches[0], app.data));
3023     break;
3024   case "WebIDL.Iterable":
3025     // a.putLn("// TODO: add iterable");
3026     break;
3027   case "WebIDL.MixinRest":
3028     context.typeName = tree.children[0].matches[0];
3029     auto partials = context.getMatchingPartials(context.typeName);
3030     auto app = appender!(Node[]);
3031     tree.children[1].toIr(app, context);
3032     partials.each!(c => toIr(c.children[0].children[0].children[0].children[1], app, context));
3033     a.put(new MixinNode(tree.children[0].matches[0], app.data));
3034     break;
3035   case "WebIDL.Const":
3036     a.put(new ConstNode(tree.children[0].generateDType(context),
3037                         tree.children[1].generateDType(context),
3038                         tree.children[2].generateDType(context)));
3039     break;
3040   case "WebIDL.InterfaceMember":
3041     context.extendedAttributeList = tree.children[0];
3042     tree.children[1].toIr(a, context);
3043     break;
3044   case "WebIDL.ReadOnlyMember":
3045     context.readonly = true;
3046     tree.children[0].toIr(a, context);
3047     break;
3048   case "WebIDL.MixinMember":
3049     if (tree.children[0].name == "WebIDL.ReadOnly") {
3050       if (tree.children[0].matches[0] == "readonly") {
3051         context.readonly = true;
3052       }
3053       tree.children[1].toIr(a, context);
3054     } else
3055       tree.children[0].toIr(a, context);
3056     break;
3057   case "WebIDL.AttributeRest":
3058     if (context.isIncludes) {
3059       auto name = tree.children[1].matches[0];
3060       auto baseName = context.includes.children[0].matches[0];
3061       auto attrType = tree.children[0];
3062       auto attrArg = Argument(name, attrType);
3063 
3064       if (!context.readonly)
3065         a.put(new FunctionNode( name, [attrArg], ParseTree.init, FunctionType.Attribute | FunctionType.Includes, "Set", baseName, ""));
3066       a.put(new FunctionNode( name, [], attrType, FunctionType.Attribute | FunctionType.Includes, "Get", baseName, ""));
3067       break;
3068     }
3069     if (context.isPartial) {
3070       auto name = tree.children[1].matches[0];
3071       auto baseName = context.partial.children[0].children[0].matches[0];
3072       auto attrType = tree.children[0];
3073       auto attrArg = Argument(name, attrType);
3074 
3075       if (!context.readonly)
3076         a.put(new FunctionNode( name, [attrArg], ParseTree.init, FunctionType.Attribute | FunctionType.Partial, "Set", baseName, ""));
3077       a.put(new FunctionNode( name, [], attrType, FunctionType.Attribute | FunctionType.Partial, "Get", baseName, ""));
3078       break;
3079     }
3080     auto name = tree.children[1].matches[0];
3081     auto attrType = tree.children[0];
3082     if (!context.readonly)
3083       a.put(new FunctionNode( name, [Argument(name, attrType)], ParseTree.init, FunctionType.Attribute, "Set", "", ""));
3084     a.put(new FunctionNode( name, [], attrType, FunctionType.Attribute, "Get", "", ""));
3085     break;
3086   case "WebIDL.ExtendedAttributeList":
3087     context.extendedAttributeList = tree;
3088     break;
3089   case "WebIDL.SpecialOperation":
3090     if (tree.children[1].children[1].children[0].matches[0] != "") {
3091       // context.customName = tree.children[0].matches[0];
3092       tree.children[1].toIr(a, context);
3093       switch(tree.matches[0]) {
3094       case "getter":
3095         // (cast(FunctionNode)a.data[$-1]).type |= FunctionType.Getter;
3096         (cast(FunctionNode)a.data[$-1]).manglePostfix = tree.children[0].matches[0];
3097         break;
3098       case "setter":
3099         // (cast(FunctionNode)a.data[$-1]).type |= FunctionType.Getter;
3100         (cast(FunctionNode)a.data[$-1]).manglePostfix = tree.children[0].matches[0];
3101         break;
3102       default: break;
3103       }
3104       break;
3105     }
3106     auto rest = tree.children[1].children[1];
3107     auto args = zip(rest.children[1].extractArguments,rest.children[1].extractTypes, rest.children[1].extractArgumentRests).map!(a=>Argument(a[0],a[1],ParseTree.init,a[2])).array();
3108     auto result = tree.children[1].children[0];
3109     switch(tree.matches[0]) {
3110     case "getter":
3111       assert(args.length == 1);
3112       a.put(new FunctionNode( "getter", args, result, FunctionType.OpIndex | FunctionType.Getter, "", "", ""));
3113       args = args.dup();
3114       args[0].templated = true;
3115       a.put(new FunctionNode( "getter", args, result, FunctionType.OpDispatch | FunctionType.Getter, "", "" ,""));
3116       break;
3117     case "setter":
3118       assert(args.length == 2);
3119       a.put(new FunctionNode( "setter", args, ParseTree.init, FunctionType.OpIndexAssign | FunctionType.Setter, "", "", ""));
3120       args = args.dup();
3121       args[0].templated = true;
3122       a.put(new FunctionNode( "setter", args, ParseTree.init, FunctionType.OpDispatch | FunctionType.Setter, "", "", ""));
3123       break;
3124     case "deleter":
3125       a.put(new FunctionNode( "remove", args, ParseTree.init, FunctionType.Function | FunctionType.Deleter, "", "", "deleter"));
3126       break;
3127     case "legacycaller":
3128       assert(args.length == 1);
3129       a.put(new FunctionNode( "getter", args, result, FunctionType.OpIndex | FunctionType.Getter, "", "", ""));
3130       args = args.dup();
3131       args[0].templated = true;
3132       a.put(new FunctionNode( "getter", args, result, FunctionType.OpDispatch | FunctionType.Getter, "", "" ,""));
3133       
3134       // todo: Find out what this is. getter for now
3135       break;
3136     default: assert(0);
3137     }
3138     break;
3139   case "WebIDL.RegularOperation":
3140     auto rest = tree.children[1];
3141     auto args = zip(rest.children[1].extractArguments,rest.children[1].extractTypes,rest.children[1].extractDefaults,rest.children[1].extractArgumentRests).map!(a=>Argument(a[0],a[1],a[2],a[3])).array();
3142     auto result = tree.children[0];
3143     if (context.isIncludes) {
3144       auto name = tree.children[1].matches[0];
3145       auto baseName = context.includes.children[0].matches[0];
3146       createOptionalOverloads(new FunctionNode( name, args, result, FunctionType.Function | FunctionType.Includes, "", baseName, context.customName)).copy(a);
3147       break;
3148     }
3149     if (context.isPartial) {
3150       auto name = rest.children[0].matches[0];
3151       auto baseName = context.partial.children[0].children[0].matches[0];
3152       createOptionalOverloads(new FunctionNode( name, args, result, FunctionType.Function | FunctionType.Partial, "", baseName, context.customName)).copy(a);
3153       break;
3154     }
3155     auto name = rest.children[0].matches[0];
3156     createOptionalOverloads(new FunctionNode( name, args, result, FunctionType.Function, "", "", context.customName)).copy(a);
3157     break;
3158   case "WebIDL.Enum":
3159     a.put(new EnumNode(tree.children[0].matches[0], tree.children[1].children.map!(c => c.matches[0][1..$-1].orNone.friendlyName).joiner(",\n  ").text));
3160     break;
3161   case "WebIDL.Dictionary":
3162     ParseTree baseType;
3163     if (tree.children[1].children.length > 0 && tree.children[1].children[0].matches.length != 0)
3164       baseType = tree.children[1].children[0];
3165     auto app = appender!(Node[]);
3166     Argument[] args;
3167     app.put(new FunctionNode("create", args, ParseTree.init, FunctionType.DictionaryConstructor, "", "", ""));
3168     tree.children[2].toIr(app, context);
3169     a.put(new StructNode(tree.children[0].matches[0], baseType, app.data));
3170     break;
3171   case "WebIDL.DictionaryMember":
3172     context.extendedAttributeList = tree.children[0];
3173     tree.children[1].toIr(a, context);
3174     break;
3175   case "WebIDL.MaplikeRest":
3176     a.put(new MaplikeNode(tree.children[0], tree.children[1]));
3177     break;
3178   case "WebIDL.DictionaryMemberRest":
3179     auto name = tree.children[1].matches[0];
3180     auto paramType = tree.children[0];
3181     a.put(new FunctionNode(name, [Argument(name, paramType)], ParseTree.init, FunctionType.Attribute, "Set", "", ""));
3182     a.put(new FunctionNode(name, [], paramType, FunctionType.Attribute, "Get", "", ""));
3183     break;
3184   case "WebIDL.Typedef":
3185     a.put(new TypedefNode(tree.children[1].matches[0], tree.children[0].generateDType(context), tree.children[0]));
3186     break;
3187   case "WebIDL.Partial":
3188     context.partial = tree;
3189     if (tree.children[0].children[0].name == "WebIDL.PartialInterfaceOrPartialMixin") {
3190       context.typeName = tree.children[0].children[0].children[0].children[0].matches[0];
3191       auto baseType = context.getType(context.typeName);
3192       if (baseType.name == "WebIDL.MixinRest")
3193         return;
3194     } else if (tree.children[0].children[0].name == "WebIDL.PartialDictionary") {
3195       context.typeName = tree.children[0].children[0].children[0].matches[0];
3196     } else if (tree.children[0].children[0].name == "WebIDL.Namespace") {
3197       context.typeName = tree.children[0].children[0].children[0].matches[0];
3198     }
3199     tree.children[0].toIr(a, context);
3200     break;
3201   case "WebIDL.PartialInterfaceRest":
3202     tree.children[1].toIr(a, context);
3203     break;
3204   case "WebIDL.CallbackRest":
3205     auto argList = tree.children[2];
3206     auto args = zip(argList.extractArguments,argList.extractTypes,argList.extractDefaults,argList.extractArgumentRests).map!(a=>Argument(a[0],a[1],a[2],a[3])).array();
3207 
3208     a.put(new CallbackNode(tree.children[0].matches[0], tree.children[1], args));
3209     break;
3210   default:
3211     tree.children.each!(c => toIr(c, a, context));
3212     return;
3213   }
3214 }
3215 
3216 auto collectFunctions(IR ir, string[] filter) {
3217   import std.algorithm : canFind;
3218   auto app = appender!(FunctionNode[]);
3219   void recurse(Node node, string parentName) {
3220     if (auto fun = cast(FunctionNode)node) {
3221       string name = mangleName(parentName.length > 0 ? parentName : fun.baseType, fun.name, fun.manglePostfix);
3222       if (filter.length == 0 || filter.canFind(name)) {
3223         app.put(fun);
3224       }
3225     } else if (auto structNode = cast(StructNode)node) {
3226       foreach(child; structNode.children)
3227         recurse(child, structNode.name);
3228     } else if (auto includesNode = cast(StructIncludesNode)node) {
3229       //writeln("collecting includes node ", includesNode.name);
3230       foreach(child; includesNode.children)
3231         recurse(child, includesNode.name);
3232     } else if (auto moduleNode = cast(ModuleNode)node) {
3233       foreach(child; moduleNode.children)
3234         recurse(child, "");
3235     }
3236   }
3237   ir.nodes.each!(node => recurse(node, ""));
3238   return app.data;
3239 }
3240 auto collectCallbacks(alias pred)(IR ir) {
3241   import std.algorithm : canFind;
3242   auto app = appender!(CallbackNode[]);
3243   void recurse(Node node) {
3244     if (auto cb = cast(CallbackNode)node) {
3245       if (pred(cb)) {
3246         app.put(cb);
3247       }
3248     } else if (auto moduleNode = cast(ModuleNode)node) {
3249       foreach(child; moduleNode.children)
3250         recurse(child);
3251     }
3252   }
3253   ir.nodes.each!(node => recurse(node));
3254   return app.data;
3255 }
3256 auto collectUsedCallbackNames(IR ir, FunctionNode[] funcs) {
3257   return funcs.map!(f => f.args).joiner.map!(a => a.type).filter!(t => ir.semantics.isCallback(t)).map!((c){
3258       if (ir.semantics.isTypedef(c))
3259         return ir.semantics.getAliasedType(c.getTypeName());
3260       return c;
3261     }).map!(t => t.getTypeName()).array().sort.uniq;
3262 }
3263 void generateDecodedTypes(IR ir, ref Appender!(ParseTree[]) a, string[] filter) {
3264   auto semantics = ir.semantics;
3265   auto funcs = collectFunctions(ir, filter);
3266   auto callbackNames = collectUsedCallbackNames(ir, funcs);
3267   auto cbs = collectCallbacks!(c => callbackNames.canFind(c.name))(ir);
3268   foreach(fun; funcs) {
3269     foreach(arg; fun.args) {
3270       if (semantics.isNullableTypedef(arg.type)) {
3271         a.put(arg.type.stripNullable);
3272       } else if (semantics.isUnion(arg.type) || semantics.isEnum(arg.type)) {
3273         a.put(arg.type);
3274       }
3275     }
3276   }
3277   foreach(cb; cbs) {
3278     if (semantics.isUnion(cb.result) || semantics.isEnum(cb.result) || semantics.isAny(cb.result))
3279       a.put(cb.result);
3280   }
3281 }
3282 void generateEncodedTypes(IR ir, ref Appender!(ParseTree[]) a, string[] filter) {
3283   auto semantics = ir.semantics;
3284   auto funcs = collectFunctions(ir, filter);
3285   foreach(fun; funcs) {
3286     if (fun.result != ParseTree.init && fun.result.matches[0] != "void") {
3287       if (semantics.isStringType(fun.result) || semantics.isUnion(fun.result) || semantics.isNullable(fun.result) || semantics.isEnum(fun.result)) {
3288         a.put(fun.result);
3289       }
3290     }
3291     foreach(arg; fun.args) {
3292       if (semantics.isCallback(arg.type.matches[0])){
3293         auto argList = semantics.getArgumentList(arg.type);
3294         auto types = extractTypes(argList);
3295         foreach(type; types) {
3296           if (semantics.isStringType(type) || semantics.isUnion(type)
3297               || semantics.isNullable(type) || semantics.isEnum(type))
3298             a.put(type);
3299         }
3300       }
3301     }
3302   }
3303 }
3304 
3305 auto stripNullable(const ParseTree tree) {
3306   ParseTree clone = tree.dup();
3307   if (clone.children.length == 0)
3308     return clone;
3309   switch (clone.name) {
3310   case "WebIDL.ExtendedAttributeList": return clone;
3311   case "WebIDL.CallbackRest": return clone;
3312   case "WebIDL.Dictionary": return clone;
3313   case "WebIDL.CallbackOrInterfaceOrMixin": return clone;
3314   case "WebIDL.PartialDefinition": return clone;
3315   default:
3316   }
3317   if (clone.children[$-1].name == "WebIDL.Null") {
3318     clone.children = clone.children.dup;
3319     clone.children[$-1].matches = [""];
3320     clone.matches[$-1] = "";
3321     return clone;
3322   } else if (clone.matches[$-1] == "?") {
3323     clone.matches = clone.matches.dup[0..$-1];
3324   }
3325   clone.children = clone.children.map!(c => c.stripNullable).array;
3326   return clone;
3327 }
3328 
3329 void generateJsDecoder(Decoder)(Decoder decoder, ref IndentedStringAppender a, ref Semantics semantics, bool isVar) {
3330   a.put("libwasm_decode_");
3331   a.put(decoder.mangled);
3332   if (isVar) {
3333     a.put(" = ");
3334   } else {
3335     a.put(": ");
3336   }
3337   if (semantics.isPrimitive(decoder.tree)) {
3338     switch (decoder.mangled) {
3339     case "uint":
3340     case "bool":
3341       a.put("getUInt");
3342       return;
3343     case "double":
3344       a.put("getDouble");
3345       return;
3346     default:
3347     }
3348   }
3349   if (decoder.mangled == "Handle" || decoder.mangled == "object" || decoder.mangled == "sequence" || decoder.mangled == "object" || decoder.mangled == "record") {
3350     a.put("decode_handle");
3351     return;
3352   }
3353   a.putLn("(ptr)=>{");
3354   a.indent();
3355   // enum
3356   // optional!T
3357   // sumType!Ts
3358   // typedef to T
3359   if (semantics.isNullableTypedef(decoder.tree)) {
3360     string typeName = decoder.tree.getTypeName();
3361     auto aliasedType = semantics.getAliasedType(typeName);
3362     uint structSize = semantics.getSizeOf(aliasedType);
3363     a.putLn(["if (getBool(ptr+", structSize.to!string, ")) {"]);
3364     a.indent();
3365     auto typedefMangled = aliasedType.mangleTypeJs(semantics);
3366     a.putLn(["return libwasm_decode_",typedefMangled,"(ptr);"]);
3367     a.undent();
3368     a.putLn("}");
3369   } else if (semantics.isTypedef(decoder.tree)) {
3370     string typeName = decoder.tree.getTypeName();
3371     auto aliasedType = semantics.getAliasedType(typeName);
3372     auto typedefMangled = aliasedType.mangleTypeJs(semantics);
3373     a.putLn(["return libwasm_decode_",typedefMangled,"(ptr);"]);
3374   } else if (semantics.isNullable(decoder.tree)) {
3375     auto baseType = decoder.tree.stripNullable;
3376     uint structSize = semantics.getSizeOf(baseType);
3377     a.putLn(["if (getBool(ptr+", structSize.to!string, ")) {"]);
3378     a.indent();
3379     auto typedefMangled = baseType.mangleTypeJs(semantics);
3380     a.putLn(["return libwasm_decode_",typedefMangled,"(ptr);"]);
3381     a.undent();
3382     a.putLn("}");
3383   } else if (semantics.isEnum(decoder.tree)) {
3384     string typeName = decoder.tree.getTypeName();
3385     auto aliasedType = (typeName in semantics.types).tree;
3386     a.putLn(["const vals = [",aliasedType.children[1].children.map!(c => c.matches[0]).joiner(", ").text,"];"]);
3387     a.putLn("return vals[ptr];");
3388   } else if (semantics.isUnion(decoder.tree)) {
3389     void outputChild(Child)(Child c, ref Semantics semantics) {
3390       a.putLn(["if (getUInt(ptr) == ", c.index.to!string, ") {"]);
3391       a.indent();
3392       a.putLn(["return libwasm_decode_",c.value.mangleTypeJs(semantics),"(ptr+4);"]);
3393       a.undent();
3394       a.put("}");
3395     }
3396     auto children = semantics.getUnionChildren(decoder.tree).enumerate;
3397     if (children.length > 0) {
3398       children[0..$-1].each!((c){
3399         outputChild(c, semantics);
3400         a.put(" else ");
3401       });
3402       outputChild(semantics.getUnionChildren(decoder.tree).enumerate[$-1], semantics);
3403     }
3404     a.putLn("");
3405   } else if (semantics.isSequence(decoder.tree)) {
3406     a.putLn("// sequence");
3407     assert(false, "not implemented");
3408   } else if (semantics.isPrimitive(decoder.tree)) {
3409     switch (decoder.mangled) {
3410     case "ulong":
3411       a.putLn("return getUInt(ptr) + (getUInt(ptr+4) << 32);");
3412       break;
3413     default:
3414       a.putLn("// primitive");
3415       a.putLn(["// ", decoder.tree.name]);
3416       a.putLn(decoder.tree.toString);
3417       break;
3418     }
3419     assert(false, "not implemented");
3420   } else {
3421     a.putLn(["// ", decoder.tree.name]);
3422     a.putLn("// other");
3423     assert(false, "not implemented");
3424   }
3425   // where T can be any of the above
3426   // and Ts two or more of the set including the above and the following:
3427   // - any primitive (double, bool, int; unsigned/signed; etc.)
3428   // - a JsHandle
3429   a.undent();
3430   a.put("}");
3431 }
3432 void generateJsEncoder(Encoder)(Encoder encoder, ref IndentedStringAppender a, ref Semantics semantics, bool isVar) {
3433   a.put("libwasm_encode_");
3434   a.put(encoder.mangled);
3435   if (isVar) {
3436     a.put(" = ");
3437   } else {
3438     a.put(": ");
3439   }
3440   if (semantics.isPrimitive(encoder.tree)) {
3441     switch (encoder.mangled) {
3442     case "uint":
3443     case "bool":
3444       a.put("setUInt");
3445       return;
3446     case "double":
3447       a.put("setDouble");
3448       return;
3449     default:
3450     }
3451   }
3452   if (encoder.mangled == "Handle" || encoder.mangled == "object" || encoder.mangled == "sequence" || encoder.mangled == "object" || encoder.mangled == "record") {
3453     a.put("encode_handle");
3454     return;
3455   }
3456   a.putLn("(ptr, val)=>{");
3457   a.indent();
3458   // enum
3459   // optional!T
3460   // sumType!Ts
3461   // typedef to T
3462   if (semantics.isNullableTypedef(encoder.tree)) {
3463     string typeName = encoder.tree.getTypeName();
3464     auto aliasedType = semantics.getAliasedType(typeName);
3465     uint structSize = semantics.getSizeOf(aliasedType);
3466     a.putLn(["if (setBool(ptr+", structSize.to!string, ", isDefined(val))) {"]);
3467     a.indent();
3468     auto typedefMangled = aliasedType.mangleTypeJs(semantics);
3469     a.putLn(["libwasm_encode_",typedefMangled,"(ptr, val);"]);
3470     a.undent();
3471     a.putLn("}");
3472   } else if (semantics.isTypedef(encoder.tree)) {
3473     string typeName = encoder.tree.getTypeName();
3474     auto aliasedType = semantics.getAliasedType(typeName);
3475     auto typedefMangled = aliasedType.mangleTypeJs(semantics);
3476     a.putLn(["libwasm_encode_",typedefMangled,"(ptr, val);"]);
3477   } else if (semantics.isNullable(encoder.tree)) {
3478     auto baseType = encoder.tree.stripNullable;
3479     uint structSize = semantics.getSizeOf(baseType);
3480     a.putLn(["if (setBool(ptr+", structSize.to!string, ", isDefined(val))) {"]);
3481     a.indent();
3482     auto typedefMangled = baseType.mangleTypeJs(semantics);
3483     a.putLn(["libwasm_encode_",typedefMangled,"(ptr, val);"]);
3484     a.undent();
3485     a.putLn("}");
3486   } else if (semantics.isEnum(encoder.tree)) {
3487     string typeName = encoder.tree.getTypeName();
3488     auto aliasedType = (typeName in semantics.types).tree;
3489     a.putLn(["const vals = [",aliasedType.children[1].children.map!(c => c.matches[0]).joiner(", ").text,"];"]);
3490     a.putLn("setInt(ptr, vals.indexOf(val))");
3491   } else if (semantics.isUnion(encoder.tree)) {
3492     void outputChild(Child)(Child c, ref Semantics semantics) {
3493         a.putLn(["if (val instanceof ",c.value.getTypeName,") {"]);
3494         a.indent();
3495         a.putLn(["setUInt(ptr, ",c.index.to!string ,");"]);
3496         a.putLn(["libwasm_encode_",c.value.mangleTypeJs(semantics),"(ptr+4, val);"]);
3497         a.undent();
3498         a.put("}");
3499     }
3500     auto children = semantics.getUnionChildren(encoder.tree).enumerate;
3501     if (children.length > 0) {
3502       children[0..$-1].each!((c){
3503         outputChild(c, semantics);
3504         a.put(" else ");
3505       });
3506       outputChild(semantics.getUnionChildren(encoder.tree).enumerate[$-1], semantics);
3507     }
3508     a.putLn("");
3509   } else if (semantics.isSequence(encoder.tree)) {
3510     a.putLn("// sequence");
3511     assert(false, "not implemented");
3512   } else if (semantics.isPrimitive(encoder.tree)) {
3513     switch (encoder.mangled) {
3514     case "ulong":
3515       a.putLn("setUInt(ptr, val & 0xffffffff);");
3516       a.putLn("setUInt(ptr+4, val >> 32);");
3517       break;
3518     default:
3519       a.putLn("// primitive");
3520       a.putLn(["// ", encoder.tree.name]);
3521       a.putLn(encoder.tree.toString);
3522       break;
3523     }
3524     assert(false, "not implemented");
3525   } else {
3526     a.putLn(["// ", encoder.tree.name]);
3527     a.putLn("// other");
3528     assert(false, "not implemented");
3529   }
3530   // where T can be any of the above
3531   // and Ts two or more of the set including the above and the following:
3532   // - any primitive (double, bool, int; unsigned/signed; etc.)
3533   // - a JsHandle
3534   a.undent();
3535   a.put("}");
3536 }
3537 
3538 
3539 void iterate(alias fun, Appender, Args...)(ref Module module_, ref Appender app, Args args) {
3540   foreach (key; module_.types.keys.array.sort) {
3541     auto type = module_.types[key];
3542     static if (Args.length > 0)
3543       args[0].extendedAttributeList = type.attributes;
3544     fun(type.tree, app, args);
3545   }
3546   foreach (namespace; module_.namespaces.dup.schwartzSort!(i => i.tree.name)) { 
3547     fun(namespace.tree, app, args);
3548   }
3549   foreach (partialType; module_.partials.dup.schwartzSort!(i => i.tree.name)) {
3550     static if (Args.length > 0)
3551       args[0].extendedAttributeList = partialType.attributes;
3552     fun(partialType.tree, app, args);
3553   }
3554   foreach (mixinType; module_.mixins.dup.schwartzSort!(i => i.tree.name)) {
3555     fun(mixinType.tree, app, args);
3556   }
3557 }
3558 
3559 ParseTree[] getUnionChildren(ref Semantics semantics, ParseTree tree) {
3560   if (tree.name == "WebIDL.ReturnType")
3561     return semantics.getUnionChildren(tree.children[0].children[0]);
3562   if (tree.name == "WebIDL.TypeWithExtendedAttributes")
3563     return semantics.getUnionChildren(tree.children[1].children[0]);
3564   if (tree.name == "WebIDL.Type") {
3565     assert(tree.children[0].name == "WebIDL.UnionType");
3566     return semantics.getUnionChildren(tree.children[0]);
3567   }
3568   assert(tree.name == "WebIDL.UnionType");
3569 
3570   // check each child's first child, if that is a UnionType (or via Typedef), we need to concat types
3571   auto children = appender!(ParseTree[]);
3572   foreach (child; tree.children) {
3573     auto member = child.children[0];
3574     if (member.name == "WebIDL.UnionType") {
3575       semantics.getUnionChildren(member).copy(children);
3576     } else if (semantics.isTypedef(child)) {
3577       string typeName = child.getTypeName();
3578       auto aliasedType = semantics.getAliasedType(typeName);
3579       if (semantics.isUnion(aliasedType)) {
3580         semantics.getUnionChildren(aliasedType).copy(children);
3581       } else
3582         children.put(child);
3583     } else
3584       children.put(child);
3585   }
3586   return children.data;
3587 }
3588 struct TypeEncoder {
3589   string mangled;
3590   ParseTree tree;
3591   bool external;
3592 }
3593 struct TypeDecoder {
3594   string mangled;
3595   ParseTree tree;
3596 }
3597 TypeDecoder[] generateDecodedTypes(IR ir, string[] filter) {
3598   auto semantics = ir.semantics;
3599   auto app = appender!(ParseTree[]);
3600   ir.generateDecodedTypes(app, filter);
3601   auto decodedTypes = app.data.map!(t => TypeDecoder(t.mangleTypeJs(semantics),t)).array.sort!((a,b){return a.mangled < b.mangled;}).uniq!((a, b){return a.mangled == b.mangled;}).array;
3602 
3603   auto decoders = appender!(TypeDecoder[]);
3604   decodedTypes.copy(decoders);
3605 
3606   ulong start = 0, end = decoders.data.length;
3607   while (start != end) {
3608     foreach(decoder; decoders.data[start..end].dup) {
3609       if (semantics.isNullableTypedef(decoder.tree)) {
3610         string typeName = decoder.tree.getTypeName();
3611         auto aliasedType = semantics.getAliasedType(typeName);
3612         auto typedefMangled = aliasedType.mangleTypeJs(semantics);
3613         decoders.put(TypeDecoder(typedefMangled, aliasedType));
3614       } else if (semantics.isTypedef(decoder.tree)) {
3615         string typeName = decoder.tree.getTypeName();
3616         auto aliasedType = semantics.getAliasedType(typeName);
3617         auto typedefMangled = aliasedType.mangleTypeJs(semantics);
3618         decoders.put(TypeDecoder(typedefMangled, aliasedType));
3619       } else if (semantics.isNullable(decoder.tree)) {
3620         ParseTree clone = decoder.tree;
3621         auto baseType = clone.stripNullable;
3622         auto typedefMangled = baseType.mangleTypeJs(semantics);
3623         decoders.put(TypeDecoder(typedefMangled, baseType));
3624       } else if (semantics.isUnion(decoder.tree)) {
3625         foreach (child; semantics.getUnionChildren(decoder.tree)) {
3626           auto typedefMangled = child.mangleTypeJs(semantics);
3627           decoders.put(TypeDecoder(typedefMangled, child));
3628         }
3629       }
3630     }
3631     start = end;
3632     end = decoders.data.length;
3633   }
3634   return decoders.data;
3635 }
3636 
3637 TypeEncoder[] generateEncodedTypes(IR ir, string[] filter) {
3638   auto semantics = ir.semantics;
3639   auto app = appender!(ParseTree[]);
3640   ir.generateEncodedTypes(app, filter);
3641   auto encodedTypes = app.data.map!(t => TypeEncoder(t.mangleTypeJs(semantics),t,true)).array.sort!((a,b){return a.mangled < b.mangled;}).uniq!((a, b){return a.mangled == b.mangled;});
3642 
3643   auto encoders = appender!(TypeEncoder[]);
3644   encodedTypes.copy(encoders);
3645 
3646   ulong start = 0, end = encoders.data.length;
3647   while (start != end) {
3648     foreach(encoder; encoders.data[start..end].dup) {
3649       if (semantics.isNullableTypedef(encoder.tree)) {
3650         string typeName = encoder.tree.getTypeName();
3651         auto aliasedType = semantics.getAliasedType(typeName);
3652         auto typedefMangled = aliasedType.mangleTypeJs(semantics);
3653         encoders.put(TypeEncoder(typedefMangled, aliasedType, false));
3654       } else if (semantics.isTypedef(encoder.tree)) {
3655         string typeName = encoder.tree.getTypeName();
3656         auto aliasedType = semantics.getAliasedType(typeName);
3657         auto typedefMangled = aliasedType.mangleTypeJs(semantics);
3658         encoders.put(TypeEncoder(typedefMangled, aliasedType, false));
3659       } else if (semantics.isNullable(encoder.tree)) {
3660         ParseTree clone = encoder.tree;
3661         auto baseType = clone.stripNullable;
3662         auto typedefMangled = baseType.mangleTypeJs(semantics);
3663         encoders.put(TypeEncoder(typedefMangled, baseType, false));
3664       } else if (semantics.isUnion(encoder.tree)) {
3665         foreach (child; semantics.getUnionChildren(encoder.tree)) {
3666           auto typedefMangled = child.mangleTypeJs(semantics);
3667           encoders.put(TypeEncoder(typedefMangled, child, false));
3668         }
3669       }
3670     }
3671     start = end;
3672     end = encoders.data.length;
3673   }
3674   return encoders.data;
3675 }
3676 
3677 auto getImports(IR ir, Module module_) {
3678   import std.format : format;
3679   import std.typecons : tuple, Tuple;
3680   alias Item = Tuple!(Type,"type",string,"name");
3681   auto app = appender!(Item[]);
3682   auto semantics = ir.semantics;
3683   void extractTypes(alias sink)(Semantics semantics, ParseTree tree, Appender!(Item[]) app) {
3684     if (tree.name == "WebIDL.Typedef") {
3685       sink(tree.children[1].matches[0], semantics, app);
3686     } else if (tree.name == "WebIDL.NonAnyType" && tree.children[0].name == "WebIDL.Identifier") {
3687       sink(tree.matches[0], semantics, app);
3688     } else if (tree.name == "WebIDL.Inheritance") {
3689       if (tree.children.length > 0) {
3690         sink(tree.children[0].matches[0], semantics, app);
3691       }
3692     }
3693     else
3694       tree.children.each!(c => extractTypes!(sink)(semantics, c, app));
3695   }
3696   static void sink(string name, Semantics semantics, Appender!(Item[]) app) {
3697     if (name == "void")
3698       return;
3699     if (auto p = name in semantics.types) {
3700       if (semantics.isTypedef(name)) {
3701         auto baseType = semantics.getAliasedType(name);
3702         auto str = generateDType(baseType, Context(semantics));
3703         if (str.length < name.length) {
3704           if (auto q = str in semantics.types) {
3705             app.put(tuple!("type", "name")(*q, str));
3706             return;
3707           }
3708         }
3709       }
3710       app.put(tuple!("type","name")(*p, name));
3711     } else {
3712       //writeln("Cannot find ", name);
3713     }
3714   }
3715   void recurse(Node node) {
3716     if (cast(FunctionNode)node) {
3717       extractTypes!sink(semantics, (cast(FunctionNode)node).result, app);
3718       (cast(FunctionNode)node).args.each!(arg => extractTypes!sink(semantics, arg.type, app));
3719     } else if (cast(TypedefNode)node) {
3720       extractTypes!sink(semantics, (cast(TypedefNode)node).rhs, app);
3721     } else if (cast(CallbackNode)node) {
3722       extractTypes!sink(semantics, (cast(CallbackNode)node).result, app);
3723       (cast(CallbackNode)node).args.each!(arg => extractTypes!sink(semantics, arg.type, app));
3724     } else if (cast(StructNode)node) {
3725       auto baseType = (cast(StructNode)node).baseType;
3726       if (baseType != ParseTree.init && baseType.matches[0].length > 0)
3727         sink(baseType.matches[0], semantics, app);
3728       (cast(StructNode)node).children.each!(node => recurse(node));
3729     } else if (cast(StructIncludesNode)node) {
3730       auto name = (cast(StructIncludesNode)node).name;
3731       //writeln("getImports StructIncludesNode/sink ", name, " includes ", (cast(StructIncludesNode)node).baseType );
3732       sink(name, semantics, app);
3733       (cast(StructIncludesNode)node).children.each!(node => recurse(node));
3734     } else if (cast(ModuleNode)node)
3735       (cast (ModuleNode)node).children.each!(node => recurse(node));
3736     else if (cast(MixinNode)node) {
3737       (cast(MixinNode)node).children.each!(node => recurse(node));
3738     } else if (auto cons = cast(ExposedConstructorNode)node) {
3739       sink(cons.name, semantics, app);
3740     }
3741   }
3742   ir.nodes.filter!(n => n.module_ is module_).each!((node){
3743       node.children.each!(c => recurse(c));
3744     });
3745   return app.data.filter!(t => t.type.module_ !is module_).map!(t => t.type.module_.name).map!(n => format("import libwasm.bindings.%s;",n)).array.sort.uniq.array;
3746   // return app.data.schwartzSort!(a => a.name).uniq!((a,b){return a.name == b.name;}).filter!(t => t.type.module_ !is module_).map!(t => format("import libwasm.bindings.%s : %s;", t.type.module_.name,t.name)).array;
3747 }
3748 
3749 class IR {
3750   ModuleNode[] nodes;
3751   StructNode[string] structs;
3752   Semantics semantics;
3753   this(ModuleNode[] nodes, Semantics semantics) {
3754     this.nodes = nodes;
3755     this.semantics = semantics;
3756     nodes.each!(mod => mod.children.map!(n => cast(StructNode)n).filter!(n => n !is null).each!((n){
3757           structs[n.name] = n;
3758         }));
3759     this.resolvePartialsAndIncludes();
3760     this.mangleJsOverloads(semantics);
3761     this.moveExposedConstructors();
3762     this.collectMethods();
3763   }
3764 }
3765 
3766 auto collectMethods(IR ir) {
3767   ir.structs.values.each!((s){
3768       foreach(child; s.children) {
3769         if (auto constructor = cast(ExposedConstructorNode)child) {
3770           s.functions ~= constructor.name;
3771         } else if (auto includes = cast(StructIncludesNode)child) {
3772           //writeln("Collect methods on ", includes.baseType, " : ", includes.name, ", children: ", includes.children.length);
3773           foreach(includesChild; includes.children) {
3774             if (auto func = cast(FunctionNode)includesChild) {
3775               //writeln(func.name);
3776               s.functions ~= func.name;
3777             }
3778           }
3779         } else if (auto func = cast(FunctionNode)child) {
3780           s.functions ~= func.name;
3781         }
3782       }
3783     });
3784 }
3785 template skipType(T) {
3786   auto skipType(Range)(auto ref Range range) {
3787     return range.filter!(i => (cast(T)i) is null);
3788   }
3789 }
3790 template mapType(T) {
3791   auto mapType(Range)(auto ref Range range) {
3792     return range.map!(n => cast(T)n).filter!(n => n !is null);
3793   }
3794 }
3795 auto resolvePartialsAndIncludes(IR ir) {
3796   ir.nodes.each!(mod => mod.children.skipType!(ExposedConstructorNode).mapType!(FunctionNode).filter!(n => n.baseType.length > 0).each!((n){
3797         if (auto p = n.baseType in ir.structs)
3798           p.children ~= n;
3799         else
3800           writeln("Error: Type ", n.baseType, " is unknown: ");
3801       }));
3802   ir.nodes.each!(mod => mod.children.mapType!(StructIncludesNode).each!((n){
3803         if (auto p = n.baseType in ir.structs) {
3804           p.children ~= n;
3805         }
3806         else
3807           writeln("Error: Type ", n.baseType, " is unknown");
3808       }));
3809 }
3810 auto moveExposedConstructors(IR ir) {
3811   foreach(mod; ir.nodes) {
3812     mod.children.mapType!(ExposedConstructorNode).each!((constructor){
3813       if (auto p = constructor.baseType in ir.structs) {
3814         p.children ~= constructor;
3815       } else
3816         writeln("Error: cannot find type ", constructor.baseType," to exposed constructor ",constructor.name, " on.");
3817       });
3818   }
3819   foreach(mod; ir.nodes) {
3820     mod.children = std.algorithm.remove!(n => cast(ExposedConstructorNode)n)(mod.children);
3821   }
3822 }
3823 auto mangleJsOverloads(IR ir, Semantics semantics) {
3824   void handleSet(Node[] nodes) {
3825     auto funcs = nodes.mapType!(FunctionNode).array;
3826     nodes.mapType!(StructIncludesNode).each!(i => handleSet(i.children));
3827     auto overloadGroups = funcs.schwartzSort!((a){
3828         if (a.customName.length > 0)
3829           return a.customName ~ a.manglePostfix;
3830         return a.name ~ a.manglePostfix;
3831       }).groupBy.map!(g => g.array).filter!(g => g.length > 1);
3832     foreach(group; overloadGroups) {
3833       foreach(fun; group) {
3834         fun.manglePostfix ~= "_" ~ fun.args.map!(arg => arg.type.mangleTypeJs(semantics)).joiner("_").text;
3835       }
3836     }
3837   }
3838   import std.algorithm : schwartzSort;
3839   foreach(item; ir.structs.byValue) {
3840     handleSet(item.children);
3841   }
3842   foreach(item; ir.nodes.map!(n => n.children).joiner) {
3843     auto mixinNode = cast(MixinNode)item;
3844     if (mixinNode)
3845       handleSet(mixinNode.children);
3846   }
3847 }
3848 string generateDBindings(IR ir, Module module_) {
3849   auto app = IndentedStringAppender();
3850   auto context = Context(module_.semantics);
3851   ir.nodes.filter!(mod => mod.module_ is module_).each!(n => n.toDBinding(module_.semantics, &app));
3852   return app.data;
3853 }
3854 
3855 string generateDImports(IR ir, Module module_) {
3856   auto app = IndentedStringAppender();
3857   ir.nodes.filter!(mod => mod.module_ is module_).each!(n => n.toDImport(module_.semantics, &app));
3858   if (app.data.length > 0)
3859     return app.data[0 .. $ - 1]; // remove last newline
3860   return app.data;
3861 }
3862 
3863 string generateSingleJsBinding(IR ir, string[] filtered = []) {
3864   import std.algorithm : map, filter, joiner, each, sort, uniq, cmp;
3865   import std.array : array;
3866   import std.conv : text;
3867   import std.typecons : tuple;
3868   auto semantics = ir.semantics;
3869   auto app = IndentedStringAppender();
3870   app.putLn("// File is autogenerated with `dub libwasm:webidl -- --bindgen`");
3871   app.putLn("import {libwasm as spa, encoders as encoder, decoders as decoder} from '../modules/libwasm.js';");
3872   app.putLn("let libwasm = spa;");
3873   app.putLn("let memory = {};");
3874   app.putLn("const objects = libwasm.objects;");
3875   app.putLn("const addObject = libwasm.addObject;");
3876   app.putLn("const setupMemory = () => {");
3877   app.putLn("    let buffer = libwasm.memory.buffer;");
3878   app.putLn("    if (memory.buffer == buffer)");
3879   app.putLn("        return;");
3880   app.putLn("    memory.buffer = buffer;");
3881   app.putLn("    memory.heapi32s = new Int32Array(buffer)");
3882   app.putLn("    memory.heapi32u = new Uint32Array(buffer)");
3883   app.putLn("    memory.heapi16s = new Int16Array(buffer)");
3884   app.putLn("    memory.heapi16u = new Uint16Array(buffer)");
3885   app.putLn("    memory.heapi8s = new Int8Array(buffer)");
3886   app.putLn("    memory.heapi8u = new Uint8Array(buffer)");
3887   app.putLn("    memory.heapf32 = new Float32Array(buffer)");
3888   app.putLn("    memory.heapf64 = new Float64Array(buffer)");
3889   app.putLn("}");
3890 
3891   auto decodedTypes = ir.generateDecodedTypes(filtered).sort!((a,b){return a.mangled < b.mangled;}).uniq!((a, b){return a.mangled == b.mangled;});
3892   auto encodedTypes = ir.generateEncodedTypes(filtered).sort!((a,b){return a.mangled < b.mangled;}).uniq!((a, b){return a.mangled == b.mangled;});
3893   app.putLn("const setBool = (ptr, val) => (memory.heapi32u[ptr/4] = +val),");
3894   app.putLn("      setInt = (ptr, val) => (memory.heapi32s[ptr/4] = val),");
3895   app.putLn("      setUInt = (ptr, val) => (memory.heapi32u[ptr/4] = val),");
3896   app.putLn("      setShort = (ptr, val) => (memory.heapi16s[ptr/2] = val),");
3897   app.putLn("      setUShort = (ptr, val) => (memory.heapi16u[ptr/2] = val),");
3898   app.putLn("      setByte = (ptr, val) => (memory.heapi8s[ptr] = val),");
3899   app.putLn("      setUByte = (ptr, val) => (memory.heapi8u[ptr] = val),");
3900   app.putLn("      setFloat = (ptr, val) => (memory.heapf32[ptr/4] = val),");
3901   app.putLn("      setDouble = (ptr, val) => (memory.heapf64[ptr/8] = val),");
3902   app.putLn("      getBool = (ptr) => memory.heapi32u[ptr/4],");
3903   app.putLn("      getInt = (ptr) => memory.heapi32s[ptr/4],");
3904   app.putLn("      getUInt = (ptr) => memory.heapi32u[ptr/4],");
3905   app.putLn("      getShort = (ptr) => memory.heapi16s[ptr/2],");
3906   app.putLn("      getUShort = (ptr) => memory.heapi16u[ptr/2],");
3907   app.putLn("      getByte = (ptr) => memory.heapi8s[ptr],");
3908   app.putLn("      getUByte = (ptr) => memory.heapi8u[ptr],");
3909   app.putLn("      getFloat = (ptr) => memory.heapf32[ptr/4],");
3910   app.putLn("      getDouble = (ptr) => memory.heapf64[ptr/8],");
3911   app.putLn("      isDefined = (val) => (val != undefined && val != null),");
3912   app.putLn("      encode_handle = (ptr, val) => { setUInt(ptr, addObject(val)); },");
3913   app.putLn("      decode_handle = (ptr) => { return objects[getUInt(ptr)]; },");
3914   app.putLn("      libwasm_encode_string = encoder.string,");
3915   app.putLn("      libwasm_decode_string = decoder.string,");
3916   app.put("      libwasm_indirect_function_get = (ptr)=>libwasm.instance.exports.__indirect_function_table.get(ptr)");
3917   app.indent();
3918   foreach(encoder; encodedTypes.filter!(e => e.mangled != "string")) {
3919     app.putLn(",");
3920     encoder.generateJsEncoder(app, semantics, true);
3921   }
3922   foreach(decoder; decodedTypes.filter!(e => e.mangled != "string")) {
3923     app.putLn(",");
3924     decoder.generateJsDecoder(app, semantics, true);
3925   }
3926   app.putLn(";");
3927   app.undent();
3928   app.putLn("export let jsExports = {");
3929   app.indent();
3930   app.putLn("env: {");
3931   app.indent();
3932 
3933   auto pos = app.data.length;
3934   ir.nodes.each!(n => n.toJsExport(semantics, filtered, &app));
3935   ir.generateJsGlobalBindings(filtered, app);
3936 
3937   app.undent();
3938   app.putLn("}");
3939   app.undent();
3940   app.put("}");
3941   return app.data;
3942 }
3943 
3944 class Type {
3945   ParseTree tree;
3946   ParseTree attributes;
3947   Module module_;
3948   this(ParseTree t, ParseTree a, Module m) {
3949     tree = t;
3950     attributes = a;
3951     module_ = m;
3952   }
3953 }
3954 
3955 class Module {
3956   string name;
3957   Type[string] types;
3958   Type[] partials;
3959   Type[] mixins;
3960   Type[] namespaces;
3961   Semantics semantics;
3962   this(string n, Semantics semantics) {
3963     this.name = n;
3964     this.semantics = semantics;
3965   }
3966 }
3967 
3968 class Semantics {
3969   Type[string] types;
3970   Type[] partials;
3971   Type[] mixins;
3972   Type[] namespaces;
3973   Module[string] modules;
3974   Module analyse(string module_, ParseTree tree) {
3975     import std.range : chunks;
3976     assert(tree.name == "WebIDL");
3977     auto m = new Module(module_, this);
3978     foreach(chunk; tree.children[0].children.chunks(2)) {
3979       assert(chunk.length == 2, "Expected 2 chunks for " ~ chunk[0].name);
3980       analyse(m, chunk[1], chunk[0]);
3981     }
3982     modules[module_] = m;
3983     foreach (n; m.namespaces)
3984       namespaces ~= n;
3985     foreach(p; m.partials)
3986       partials ~= p;
3987     foreach(mix; m.mixins)
3988       mixins ~= mix;
3989     foreach(k,v; m.types)
3990       types[k] = v;
3991     return m;
3992   }
3993   void dumpTypes() {
3994     import std.format;
3995     writefln("%(%s\n%)",types.keys.map!((key){return format("%s.%s", types[key].module_.name, key);}).array.sort);
3996   }
3997   private void analyse(Module module_, ParseTree tree, ParseTree attributes) {
3998     switch (tree.name) {
3999     case "WebIDL.IncludesStatement":
4000       //writeln("Analysing include statement");
4001       module_.mixins ~= new Type(tree, ParseTree.init, module_);
4002       break;
4003       
4004     case "WebIDL.Declaration":
4005       break;
4006     case "WebIDL.Dictionary":
4007     case "WebIDL.InterfaceRest":
4008     case "WebIDL.Enum":
4009     case "WebIDL.CallbackRest":
4010     case "WebIDL.MixinRest":
4011       string name = tree.children[0].matches[0];
4012     if (auto p = name in types) {
4013       writefln("Warning: duplicated entry for %s", name);
4014       writefln("A in %s: %s",(*p).module_.name,(*p).tree.input[(*p).tree.begin .. (*p).tree.end]);
4015       writefln("B in %s: %s",module_.name,tree.input[tree.begin .. tree.end]);
4016     }
4017     module_.types[name] = new Type(tree, attributes, module_);
4018     break;
4019     case "WebIDL.Partial":
4020       module_.partials ~= new Type(tree, attributes, module_);
4021       break;
4022     case "WebIDL.Typedef":
4023       string name = tree.children[1].matches[0];
4024       module_.types[name] = new Type(tree, attributes, module_);
4025       break;
4026     case "WebIDL.Namespace":
4027       module_.namespaces ~= new Type(tree, attributes, module_);
4028       break;
4029     default:
4030       tree.children.each!(c => analyse(module_, c, attributes));
4031     }
4032   }
4033 }
4034 string generateDType(ParseTree tree, Context context) {
4035   auto app = IndentedStringAppender();
4036   tree.generateDType(app, context);
4037   return app.data;
4038 }
4039 void generateDType(Appender)(ParseTree tree, ref Appender a, Context context) {
4040   switch (tree.name) {
4041   case "WebIDL.Declaration":
4042   case "WebIDL.InterfaceRest":
4043   case "WebIDL.IncludesStatement":
4044   case "WebIDL.Iterable":
4045   case "WebIDL.MixinRest":
4046   case "WebIDL.Const":
4047   case "WebIDL.InterfaceMember":
4048   case "WebIDL.ReadOnlyMember":
4049   case "WebIDL.MixinMember":
4050   case "WebIDL.AttributeRest":
4051   case "WebIDL.ExtendedAttributeList":
4052   case "WebIDL.SpecialOperation":
4053   case "WebIDL.RegularOperation":
4054   case "WebIDL.ArgumentList":
4055   case "WebIDL.ArgumentName":
4056   case "WebIDL.ArgumentRest":
4057   case "WebIDL.Enum":
4058   case "WebIDL.Dictionary":
4059   case "WebIDL.DictionaryMember":
4060   case "WebIDL.SetlikeRest":
4061   case "WebIDL.MaplikeRest":
4062   case "WebIDL.DictionaryMemberRest":
4063   case "WebIDL.Typedef":
4064   case "WebIDL.Partial":
4065   case "WebIDL.PartialInterfaceRest":
4066   case "WebIDL.CallbackRest":
4067     break;
4068   case "WebIDL.SequenceType":
4069     if (tree.children[$-1].matches[0] == "?")
4070       a.put("Optional!(Sequence!(");
4071     else
4072       a.put("Sequence!(");
4073     tree.children[0].generateDType(a, context);
4074     a.put(")");
4075     if (tree.children[$-1].matches[0] == "?")
4076       a.put(")");
4077     break;
4078   case "WebIDL.TypeWithExtendedAttributes":
4079     context.extendedAttributeList = tree.children[0];
4080     tree.children[1].generateDType(a, context);
4081     break;
4082   case "WebIDL.StringType":
4083     a.put("string");
4084     break;
4085   case "WebIDL.SingleType":
4086     if (tree.matches[0] == "any")
4087       a.put("Any");
4088     else
4089       tree.children[0].generateDType(a, context);
4090     break;
4091   case "WebIDL.NonAnyType":
4092     bool optional = !context.skipOptional && (context.optional || tree.children[$-1].name == "WebIDL.Null" && tree.children[$-1].matches[0] == "?");
4093     if (optional) {
4094       a.put("Optional!(");
4095     }
4096     switch (tree.matches[0]) {
4097     case "object":
4098       a.put("JsObject");
4099       break;
4100     case "symbol":
4101       a.put("Symbol");
4102       break;
4103     case "Error":
4104       a.put("Error");
4105       break;
4106     case "FrozenArray":
4107       a.put("FrozenArray!(");
4108       tree.children[$-2].generateDType(a, context);
4109       a.put(")");
4110       break;
4111     default:
4112       tree.children.each!(c => c.generateDType(a, context));
4113     }
4114     if (optional) {
4115       a.put(")");
4116     }
4117     break;
4118   case "WebIDL.PrimitiveType":
4119     if (tree.children.length == 0) {
4120       switch (tree.matches[0]) {
4121       case "byte": a.put("byte"); break;
4122       case "octet": a.put("ubyte"); break;
4123       case "boolean": a.put("bool"); break;
4124       default: a.put(tree.matches[0]); break;
4125       }
4126     } else
4127       tree.children[0].generateDType(a, context);
4128     break;
4129   case "WebIDL.UnrestrictedFloatType":
4130     // TODO: handle unrestricted
4131     tree.children[0].generateDType(a, context);
4132     break;
4133   case "WebIDL.UnsignedIntegerType":
4134     if (tree.matches[0] == "unsigned")
4135       a.put("u");
4136     tree.children[0].generateDType(a, context);
4137     break;
4138   case "WebIDL.IntegerType":
4139     if (tree.matches[0] == "long") {
4140       if (tree.matches.length > 1)
4141         a.put(is32Bit ? "int" : "long");
4142       else
4143         a.put("int");
4144     } else
4145       a.put(tree.matches[0]);
4146     break;
4147   case "WebIDL.FloatType":
4148   case "WebIDL.Identifier":
4149     string typeName = tree.matches[0];
4150     if (context.isTypedef(typeName)) {
4151       auto aliasedType = context.getAliasedType(typeName);
4152       IndentedStringAppender app;
4153       aliasedType.generateDType(app, context);
4154       if (app.data.length < tree.matches[0].length)
4155         return a.put(app.data);
4156     }
4157     if (context.locals.canFind(tree.matches[0]))
4158       a.put(".");
4159     a.put(tree.matches[0]);
4160     break;
4161   case "WebIDL.ReturnType":
4162     if (tree.children.length > 0)
4163       tree.children[0].generateDType(a, context);
4164     else
4165       a.put(tree.matches[0]);
4166     break;
4167   case "WebIDL.Default":
4168     if (tree.children.length == 0)
4169       return;
4170     a.put("/* = ");
4171     tree.children[0].generateDType(a, context);
4172     a.put(" */");
4173     break;
4174   case "WebIDL.DefaultValue":
4175     if (tree.children.length == 0) {
4176       a.put("[]");
4177       return;
4178     }
4179     tree.children[0].generateDType(a, context);
4180     break;
4181   case "WebIDL.ConstValue":
4182     if (tree.children.length == 0) {
4183       a.put(tree.matches);
4184       break;
4185     }
4186     tree.children[0].generateDType(a, context);
4187     break;
4188   case "WebIDL.BooleanLiteral":
4189   case "WebIDL.Integer":
4190     a.put(tree.matches);
4191     break;
4192   case "WebIDL.Float":
4193     a.put(tree.matches[0]);
4194     break;
4195   case "WebIDL.FloatLiteral":
4196     if (tree.children.length == 0) {
4197       switch (tree.matches[0]) {
4198       case "-Infinity": a.put("-float.infinity"); break;
4199       case "Infinity": a.put("float.infinity"); break;
4200       case "NaN": a.put("float.nan"); break;
4201       default: assert(false);
4202       }
4203       break;
4204     }
4205     tree.children[0].generateDType(a, context);
4206     break;
4207   case "WebIDL.String":
4208     a.put(tree.matches);
4209     break;
4210   case "WebIDL.RecordType":
4211     a.put("Record!(");
4212     tree.children.putWithDelimiter!(generateDType)(", ", a, context);
4213     a.put(")");
4214     break;
4215   case "WebIDL.PromiseType":
4216     a.put("JsPromise!(");
4217     tree.children[0].generateDType(a, context);
4218     a.put(")");
4219     break;
4220   case "WebIDL.Type":
4221     context.optional = false;
4222     if (tree.children[0].name == "WebIDL.UnionType") {
4223       bool optional = !context.skipOptional && tree.children[1].matches[0] == "?";
4224       context.optional = optional;
4225       if (optional) {
4226         a.put("Optional!(");
4227       }
4228       a.put("SumType!(");
4229       tree.children[0].children.putWithDelimiter!(generateDType)(", ", a, context);
4230       if (optional)
4231         a.put(")");
4232       a.put(")");
4233       break;
4234     } else
4235       tree.children[0].generateDType(a, context);
4236     break;
4237   case "WebIDL.UnionMemberType":
4238     context.optional = false;
4239     if (tree.children[0].name == "WebIDL.UnionType") {
4240       bool optional = !context.skipOptional && tree.matches[$-1] == "?";
4241       context.optional = optional;
4242       if (optional) {
4243         a.put("Optional!(");
4244       }
4245       a.put("SumType!(");
4246       tree.children[0].children.putWithDelimiter!(generateDType)(", ", a, context);
4247       if (optional)
4248         a.put(")");
4249       a.put(")");
4250       break;
4251     } else
4252       tree.children.each!(c => c.generateDType(a, context));
4253     break;
4254   default:
4255     tree.children.each!(c => generateDType(c, a, context));
4256   }
4257 }