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 }