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 mixin(registerMethods); 21 22 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 }; 23 24 struct Argument { 25 string name; 26 ParseTree type; 27 ParseTree default_; 28 ParseTree argRest; 29 bool templated = false; 30 } 31 32 struct JsExportFunction { 33 string parentTypeName; 34 string name; 35 Argument[] args; 36 ParseTree result; 37 FunctionType type; 38 string manglePostfix; 39 } 40 41 struct DBindingFunction { 42 string parentTypeName; 43 string name; 44 Argument[] args; 45 ParseTree result; 46 FunctionType type; 47 string manglePostfix; 48 string baseType; 49 string customName; 50 string handle; 51 } 52 53 struct DImportFunction { 54 string parentTypeName; 55 string name; 56 Argument[] args; 57 ParseTree result; 58 FunctionType type; 59 string manglePostfix; 60 } 61 62 interface Node { 63 void toString(scope void delegate(const(char)[]) sink); 64 } 65 66 void indentToString(scope void delegate(const(char)[]) sink, Node[] children) { 67 foreach(child; children) { 68 bool begin = true; 69 child.toString((const(char)[] line){ 70 if (!line) 71 return; 72 if (begin) 73 sink(" "); 74 sink(line); 75 begin = line[$-1] == '\n'; 76 }); 77 if (!begin) 78 sink("\n"); 79 } 80 } 81 class ModuleNode : Node { 82 Module module_; 83 Node[] children; 84 this(Module module_, Node[] children) { 85 this.module_ = module_; 86 this.children = children; 87 } 88 void toString(scope void delegate(const(char)[]) sink) { 89 sink("Module "); 90 sink(module_.name); 91 sink("\n"); 92 sink.indentToString(children); 93 } 94 } 95 class ConstNode : Node { 96 string type; 97 string name; 98 string value; 99 this(string type, string name, string value) { 100 this.type = type; 101 this.name = name; 102 this.value = value; 103 } 104 void toString(scope void delegate(const(char)[]) sink) { 105 sink(name); 106 } 107 } 108 109 class StructNode : Node { 110 string name; 111 ParseTree baseType; 112 Node[] children; 113 string[] functions; 114 Flag!"isStatic" isStatic; 115 this(string name, ParseTree baseType , Node[] children, string[] functions, Flag!"isStatic" isStatic = No.isStatic) { 116 this(name, baseType, children, isStatic); 117 this.functions = functions; 118 } 119 this(string name, ParseTree baseType , Node[] children, Flag!"isStatic" isStatic = No.isStatic) { 120 this.name = name; 121 this.baseType = baseType; 122 this.children = children; 123 this.isStatic = isStatic; 124 } 125 void toString(scope void delegate(const(char)[]) sink) { 126 sink("Struct "); 127 sink(name); 128 sink("\n"); 129 sink.indentToString(children); 130 } 131 string getHandleSymbol() { 132 if (baseType != ParseTree.init) 133 return "_parent"; 134 return "handle"; 135 } 136 } 137 138 void toDBinding(virtual!Node node, Semantics semantics, IndentedStringAppender* a); 139 void toDBinding(virtual!Node node, StructNode parent, Semantics semantics, IndentedStringAppender* a); 140 void toJsExport(virtual!Node node, Semantics semantics, string[] filter, IndentedStringAppender* a); 141 void toJsExport(virtual!Node node, StructNode parent, Semantics semantics, string[] filter, IndentedStringAppender* a); 142 void toDImport(virtual!Node node, Semantics semantics, IndentedStringAppender* a); 143 void toDImport(virtual!Node node, StructNode parent, Semantics semantics, IndentedStringAppender* a); 144 145 @method void _toDBinding(Node node, Semantics semantics, IndentedStringAppender* a) {} 146 @method void _toDBinding(ModuleNode node, Semantics semantics, IndentedStringAppender* a) { 147 node.children.each!(c => toDBinding(c, semantics, a)); 148 } 149 @method void _toJsExport(Node node, Semantics semantics, string[] filter, IndentedStringAppender* a) {} 150 @method void _toJsExport(ModuleNode node, Semantics semantics, string[] filter, IndentedStringAppender* a) { 151 node.children.each!(c => toJsExport(c, semantics, filter, a)); 152 } 153 @method void _toJsExport(StructNode node, Semantics semantics, string[] filter, IndentedStringAppender* a) { 154 import std.algorithm : canFind; 155 bool[string] names; 156 foreach(child; node.children) { 157 auto fun = cast(FunctionNode)child; 158 if (fun) { 159 string name = mangleJsName(node, fun); 160 if (auto p = name in names) 161 continue; 162 if (filter.length > 0 && !filter.canFind(name)) 163 continue; 164 names[name] = true; 165 } 166 toJsExport(child, node, semantics, filter, a); 167 } 168 } 169 @method void _toDImport(Node node, Semantics semantics, IndentedStringAppender* a) {} 170 @method void _toDImport(ModuleNode node, Semantics semantics, IndentedStringAppender* a) { 171 node.children.each!(c => toDImport(c, semantics, a)); 172 } 173 @method void _toDImport(StructNode node, Semantics semantics, IndentedStringAppender* a) { 174 bool[string] names; 175 foreach(child; node.children) { 176 auto fun = cast(FunctionNode)child; 177 if (fun) { 178 string name = mangleJsName(node, fun); 179 if (auto p = name in names) 180 continue; 181 names[name] = true; 182 } 183 toDImport(child, node, semantics, a); 184 } 185 } 186 187 @method void _toDBinding(StructNode node, Semantics semantics, IndentedStringAppender* a) { 188 a.putLn(["struct ", node.name.friendlyName, " {"]); 189 a.indent(); 190 a.putLn("nothrow:"); 191 if (node.isStatic == Yes.isStatic) { 192 a.putLn("static:"); 193 } else if (node.baseType != ParseTree.init) { 194 auto p = node.baseType.matches[0] in semantics.types; 195 if (p is null) { 196 writeln("Error: Cannot find definition of type ", node.baseType.matches[0], "."); 197 } else { 198 a.put(["spasm.bindings.", p.module_.name, "."]); 199 } 200 a.put(node.baseType.matches[0].friendlyName); 201 a.putLn(" _parent;"); 202 a.putLn("alias _parent this;"); 203 a.putLn("this(Handle h) {"); 204 a.indent(); 205 a.putLn(["_parent = .", node.baseType.matches[0].friendlyName,"(h);"]); 206 a.undent(); 207 a.putLn("}"); 208 } else { 209 a.putLn("JsHandle handle;"); 210 a.putLn("alias handle this;"); 211 a.putLn("this(Handle h) {"); 212 a.indent(); 213 a.putLn("this.handle = JsHandle(h);"); 214 a.undent(); 215 a.putLn("}"); 216 } 217 node.children.each!(c => toDBinding(c, node, semantics, a)); 218 a.undent(); 219 a.putLn("}"); 220 } 221 @method void _toDImport(MixinNode node, Semantics semantics, IndentedStringAppender* a) { 222 auto dummyParent = new StructNode(node.name, ParseTree.init, node.children); 223 bool[string] names; 224 foreach(child; node.children) { 225 auto fun = cast(FunctionNode)child; 226 if (fun) { 227 string name = mangleJsName(dummyParent, fun); 228 if (auto p = name in names) 229 continue; 230 names[name] = true; 231 } 232 toDImport(child, dummyParent, semantics, a); 233 } 234 } 235 236 @method void _toDBinding(Node node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 237 // default od nothing 238 } 239 @method void _toJsExport(Node node, StructNode parent, Semantics semantics, string[] filter, IndentedStringAppender* a) { 240 // default od nothing 241 } 242 @method void _toDImport(Node node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 243 // default od nothing 244 } 245 @method void _toDBinding(ConstNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 246 a.putLn(["enum ", node.type, " ", node.name, " = ", node.value, ";"]); 247 } 248 249 class StructIncludesNode : Node { 250 string name; 251 string baseType; 252 Node[] children; 253 this(string baseType, string name, Node[] children) { 254 this.name = name; 255 this.baseType = baseType; 256 this.children = children; 257 } 258 void toString(scope void delegate(const(char)[]) sink) { 259 sink("StructIncludes "); 260 sink(name); 261 sink("\n"); 262 sink.indentToString(children); 263 } 264 } 265 266 class MixinNode : Node { 267 string name; 268 Node[] children; 269 this(string name, Node[] children) { 270 this.name = name; 271 this.children = children; 272 } 273 void toString(scope void delegate(const(char)[]) sink) { 274 sink("MixinNode "); 275 sink(name); 276 sink("\n"); 277 sink.indentToString(children); 278 } 279 } 280 @method void _toDBinding(StructIncludesNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 281 auto dummyParent = new StructNode(node.name, parent.baseType, node.children, parent.functions); 282 node.children.each!(c => toDBinding(c, dummyParent, semantics, a)); 283 } 284 @method void _toJsExport(MixinNode node, Semantics semantics, string[] filter, IndentedStringAppender* a) { 285 import std.algorithm : canFind; 286 auto dummyParent = new StructNode(node.name, ParseTree.init, node.children); 287 bool[string] names; 288 foreach(child; node.children) { 289 auto fun = cast(FunctionNode)child; 290 if (fun) { 291 string name = mangleJsName(dummyParent, fun); 292 if (auto p = name in names) 293 continue; 294 if (filter.length > 0 && !filter.canFind(name)) 295 continue; 296 names[name] = true; 297 } 298 toJsExport(child, dummyParent, semantics, filter, a); 299 } 300 } 301 302 @method void _toDImport(StructIncludesNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 303 // auto dummyParent = new StructNode(node.name, ParseTree.init, node.children); 304 // node.children.each!(c => toDImport(c, dummyParent, semantics, a)); 305 } 306 307 class FunctionNode : Node { 308 string name; 309 Argument[] args; 310 ParseTree result; 311 FunctionType type; 312 string manglePostfix; 313 string baseType; 314 string customName; 315 this(string name, Argument[] args, ParseTree result, FunctionType type, string manglePostfix, string baseType, string customName) { 316 this.name = name; 317 this.args = args; 318 this.result = result; 319 this.type = type; 320 this.manglePostfix = manglePostfix; 321 this.baseType = baseType; 322 this.customName = customName; 323 } 324 void toString(scope void delegate(const(char)[]) sink) { 325 sink("Function "); 326 if (customName.length > 0) 327 sink(customName); 328 else 329 sink(name); 330 if (manglePostfix.length) { 331 sink("_"); 332 sink(manglePostfix); 333 } 334 sink("("); 335 sink(args.map!(a => a.name).joiner(", ").text()); 336 sink(")"); 337 } 338 } 339 340 @method void _toDBinding(FunctionNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 341 auto tmp = DBindingFunction(parent.name, node.name, node.args, node.result, node.type, node.manglePostfix, node.baseType, node.customName, parent.getHandleSymbol()); 342 if (parent.isStatic == Yes.isStatic) 343 tmp.type |= FunctionType.Static; 344 semantics.dump(tmp, a, parent.functions); 345 // TODO: use parent.functions to avoid local shadowing 346 } 347 @method void _toJsExport(FunctionNode node, StructNode parent, Semantics semantics, string[] filter, IndentedStringAppender* a) { 348 if (node.type & (FunctionType.OpDispatch) || node.type & (FunctionType.DictionaryConstructor)) 349 return; 350 auto tmp = JsExportFunction(parent.name, node.customName != "" ? node.customName : node.name, node.args, node.result, node.type, node.manglePostfix); 351 if (parent.isStatic == Yes.isStatic) 352 tmp.type |= FunctionType.Static; 353 auto context = Context(semantics); 354 context.dump(tmp, a); 355 } 356 @method void _toDImport(FunctionNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 357 if (node.type & (FunctionType.OpDispatch) || node.type & (FunctionType.DictionaryConstructor)) 358 return; 359 auto tmp = DImportFunction(parent.name, node.customName != "" ? node.customName : node.name, node.args, node.result, node.type, node.manglePostfix); 360 if (parent.isStatic == Yes.isStatic) 361 tmp.type |= FunctionType.Static; 362 semantics.dump(tmp, a); 363 } 364 365 class ExposedConstructorNode : FunctionNode { 366 this(string name, Argument[] args, ParseTree result, string baseType, string manglePostfix) { 367 super(name, args, result, FunctionType.ExposedConstructor, manglePostfix, baseType, ""); 368 } 369 override void toString(scope void delegate(const(char)[]) sink) { 370 sink("ExposedConstructor "); 371 sink(name); 372 if (manglePostfix) { 373 sink("_"); 374 sink(manglePostfix); 375 } 376 sink("("); 377 sink(args.map!(a => a.name).joiner(", ").text()); 378 sink(")"); 379 } 380 } 381 @method void _toDBinding(ExposedConstructorNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 382 if (parent.name != node.baseType) 383 return; 384 auto tmp = DBindingFunction(parent.name, node.name, node.args, node.result, FunctionType.ExposedConstructor, node.manglePostfix, "", "", parent.getHandleSymbol()); 385 semantics.dump(tmp, a, parent.functions); 386 } 387 @method void _toJsExport(ExposedConstructorNode node, StructNode parent, Semantics semantics, string[] filter, IndentedStringAppender* a) { 388 if (parent.name != node.baseType) 389 return; 390 string mangledName = mangleName(parent.name, node.name, node.manglePostfix); 391 if (filter.length > 0 && !filter.canFind(mangledName)) 392 return; 393 auto tmp = JsExportFunction(parent.name, node.name, node.args, node.result, FunctionType.ExposedConstructor, node.manglePostfix); 394 auto context = Context(semantics); 395 context.dump(tmp, a); 396 } 397 @method void _toDImport(ExposedConstructorNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 398 if (parent.name != node.baseType) 399 return; 400 auto tmp = DImportFunction(parent.name, node.name, node.args, node.result, FunctionType.ExposedConstructor, node.manglePostfix); 401 semantics.dump(tmp, a); 402 } 403 404 class TypedefNode : Node { 405 string name; 406 string def; 407 ParseTree rhs; 408 this(string n, string d, ParseTree rhs) { 409 name = n; 410 def = d; 411 this.rhs = rhs; 412 } 413 void toString(scope void delegate(const(char)[]) sink) { 414 sink("Typedef "); 415 sink(name); 416 sink(" = "); 417 sink(def); 418 } 419 } 420 @method void _toDBinding(TypedefNode node, Semantics semantics, IndentedStringAppender* a) { 421 a.putLn(["alias ", node.name, " = ", node.def, ";"]); 422 } 423 424 class EnumNode : Node { 425 string name; 426 string content; 427 this(string n, string c) { 428 name = n; 429 content = c; 430 } 431 void toString(scope void delegate(const(char)[]) sink) { 432 sink("Enum "); 433 sink(name); 434 } 435 } 436 437 @method void _toDBinding(EnumNode node, Semantics semantics, IndentedStringAppender* a) { 438 a.putLn(["enum ", node.name, " {"]); 439 a.indent(); 440 a.putLn(node.content); 441 a.undent(); 442 a.putLn("}"); 443 } 444 445 class MaplikeNode : Node { 446 ParseTree keyType; 447 ParseTree valueType; 448 this(ParseTree keyType, ParseTree valueType) { 449 this.keyType = keyType; 450 this.valueType = valueType; 451 } 452 void toString(scope void delegate(const(char)[]) sink) { 453 sink("Maplike"); 454 } 455 } 456 457 @method void _toDBinding(MaplikeNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 458 auto context = Context(semantics); 459 string keyType = node.keyType.generateDType(context); 460 string valueQualifiers = (semantics.isPrimitive(node.valueType) || semantics.isNullable(node.valueType)) ? "" : "scope ref "; 461 string valueType = node.valueType.generateDType(context); 462 string mangleKeyType = node.keyType.generateDImports(Context(semantics)); 463 string mangleValueType = node.valueType.generateDImports(Context(semantics)); 464 string manglePrefix = "Maplike_" ~ mangleKeyType ~ "_" ~ mangleValueType ~ "_"; 465 a.putLn("uint size() {"); 466 a.putLn([" return ", manglePrefix, "size(this.handle);"]); 467 a.putLn("}"); 468 a.putLn("void clear() {"); 469 a.putLn([" ", manglePrefix, "clear(this.handle);"]); 470 a.putLn("}"); 471 a.putLn(["void delete_(",keyType," key) {"]); 472 a.putLn([" ", manglePrefix, "delete(this.handle, key);"]); 473 a.putLn("}"); 474 a.putLn(["Iterator!(ArrayPair!(",keyType,", ",valueType,")) entries() {"]); 475 a.putLn([" return Iterator!(ArrayPair!(",keyType,", ",valueType,"))(", manglePrefix, "entries(this.handle));"]); 476 a.putLn("}"); 477 a.putLn(["extern(C) void forEach(void delegate(",keyType,", Handle, Handle) callback) {"]); 478 a.putLn([" ", manglePrefix, "forEach(this.handle, callback);"]); 479 a.putLn("}"); 480 a.putLn(["",valueType," get(",keyType," key) {"]); 481 a.putLn([" return ",valueType,"(", manglePrefix, "get(this.handle, key));"]); 482 a.putLn("}"); 483 a.putLn(["bool has(",keyType," key) {"]); 484 a.putLn([" return ", manglePrefix, "has(this.handle, key);"]); 485 a.putLn("}"); 486 a.putLn(["Iterator!(",keyType,") keys() {"]); 487 a.putLn([" return Iterator!(",keyType,")(", manglePrefix, "keys(this.handle));"]); 488 a.putLn("}"); 489 a.putLn(["void set(",keyType," key, ", valueQualifiers, valueType," value) {"]); 490 a.putLn([" ", manglePrefix, "set(this.handle, key, value.handle);"]); 491 a.putLn("}"); 492 a.putLn(["Iterator!(",valueType,") values() {"]); 493 a.putLn([" return Iterator!(",valueType,")(", manglePrefix, "values(this.handle));"]); 494 a.putLn("}"); 495 } 496 @method void _toDImport(MaplikeNode node, StructNode parent, Semantics semantics, IndentedStringAppender* a) { 497 auto context = Context(semantics); 498 string keyType = node.keyType.generateDType(context); 499 string valueType = node.valueType.generateDType(context); 500 string mangleKeyType = node.keyType.generateDImports(Context(semantics)); 501 string mangleValueType = node.valueType.generateDImports(Context(semantics)); 502 string manglePrefix = "Maplike_" ~ mangleKeyType ~ "_" ~ mangleValueType ~ "_"; 503 a.putLn(["extern (C) uint ", manglePrefix, "size(Handle);"]); 504 a.putLn(["extern (C) void ", manglePrefix, "clear(Handle);"]); 505 a.putLn(["extern (C) void ", manglePrefix, "delete(Handle, ", mangleKeyType," key);"]); 506 a.putLn(["extern (C) Handle ", manglePrefix, "entries(Handle);"]); 507 a.putLn(["extern (C) void ", manglePrefix, "forEach(Handle, void delegate(", keyType,", Handle, Handle));"]); 508 a.putLn(["extern (C) ", valueType, " ", manglePrefix, "get(Handle, ", mangleKeyType,");"]); 509 a.putLn(["extern (C) bool ", manglePrefix, "has(Handle, ", mangleKeyType,");"]); 510 a.putLn(["extern (C) Handle ", manglePrefix, "keys(Handle);"]); 511 a.putLn(["extern (C) void ", manglePrefix, "set(Handle, ", mangleKeyType, " key, ", mangleValueType, " value);"]); 512 a.putLn(["extern (C) Handle ", manglePrefix, "values(Handle);"]); 513 } 514 515 class CallbackNode : Node { 516 string name; 517 ParseTree result; 518 Argument[] args; 519 this(string name, ParseTree result, Argument[] args) { 520 this.name = name; 521 this.result = result; 522 this.args = args; 523 } 524 void toString(scope void delegate(const(char)[]) sink) { 525 sink("Callback "); 526 sink(name); 527 } 528 } 529 530 @method void _toDBinding(CallbackNode node, Semantics semantics, IndentedStringAppender* a) { 531 a.put(["alias ", node.name, " = "]); 532 if (node.result.matches[0] == "void") { 533 a.put("void"); 534 } else 535 node.result.generateDType(a, Context(semantics)); 536 auto types = node.args.map!(arg => arg.type).map!(type => type.generateDType(Context(semantics))).joiner(", ").text; 537 a.putLn([" delegate(", types, ");"]); 538 } 539 540 void dumpJsArgument(Appender)(ref Semantics semantics, Argument arg, ref Appender a) { 541 if (semantics.isNullable(arg.type)) { 542 a.put(arg.name.friendlyJsName); 543 a.put("Defined ? "); 544 } 545 if (semantics.isTypedef(arg.type) && semantics.isCallback(arg.type)) { 546 auto name = getTypeName(arg.type); 547 auto aliased = semantics.getAliasedType(name); 548 auto argument = Argument(arg.name, aliased.stripNullable); 549 semantics.dumpJsArgument(argument, a); 550 } else if (semantics.isUnion(arg.type) || semantics.isEnum(arg.type)) { 551 a.put("spasm_decode_"); 552 arg.type.mangleTypeJsImpl(a, semantics, MangleTypeJsContext(true)); 553 a.put("("); 554 a.put(arg.name.friendlyJsName); 555 a.put(")"); 556 } else if (semantics.isStringType(arg.type)) { 557 a.put(["spasm_decode_string(",arg.name.friendlyJsName,"Len, ",arg.name.friendlyJsName,"Ptr)"]); 558 } else if (semantics.isCallback(arg.type)){ 559 auto signature = getTypeName(arg.type) in semantics.types; 560 auto argList = semantics.getArgumentList(signature.tree); 561 auto arguments = extractArguments(argList); 562 auto types = extractTypes(argList); 563 string base = arg.name.friendlyJsName; 564 a.put([ "(", arguments.joiner(", ").text, ")=>{"]); 565 size_t offset = 0; 566 zip(arguments, types).enumerate.each!((t) { 567 auto index = t.index; 568 auto arg = t.value[0]; 569 auto type = t.value[1]; 570 if (semantics.isStringType(type) || semantics.isUnion(type) 571 || semantics.isNullable(type) || semantics.isEnum(type)) { 572 a.put(["spasm_encode_"]); 573 if (type.name == "WebIDL.TypeWithExtendedAttributes") 574 type.children[1].mangleTypeJs(a, semantics); 575 else 576 type.mangleTypeJs(a, semantics); 577 a.put(["(", offset.to!string, ", ", arg, ");"]); 578 offset += semantics.getSizeOf(type); 579 } else if (!semantics.isPrimitive(type)) { 580 a.put(["encode_handle(", offset.to!string, ", ", arg, ");"]); 581 offset += semantics.getSizeOf(type); 582 } 583 }); 584 auto resultPtr = offset; 585 bool needsClose = false; 586 auto result = signature.tree.children[1]; 587 bool hasResult = result.matches[0] != "void"; 588 bool resultIsPassedViaFirstArgument = hasResult && (semantics.isUnion(result) || semantics.isEnum(result) || semantics.isNullable(result) || semantics.isAny(result)); 589 if (hasResult && !resultIsPassedViaFirstArgument) 590 a.put("return "); 591 592 a.put(["spasm_indirect_function_get(", base, "Ptr)("]); 593 if (resultIsPassedViaFirstArgument) 594 a.put([resultPtr.to!string, ", "]); 595 a.put([base, "Ctx"]); 596 offset = 0; 597 zip(arguments, types).enumerate.each!((t) { 598 a.put(", "); 599 auto index = t.index; 600 auto arg = t.value[0]; 601 auto type = t.value[1]; 602 if (semantics.isStringType(type) || semantics.isUnion(type) 603 || semantics.isNullable(type) || semantics.isEnum(type) 604 || !semantics.isPrimitive(type)) { 605 a.put(offset.to!string); 606 offset += semantics.getSizeOf(type); 607 } else 608 a.put(arg); 609 }); 610 a.put(")"); 611 if (resultIsPassedViaFirstArgument) { 612 a.put("; return "); 613 if (semantics.isUnion(result) || semantics.isEnum(result) || semantics.isNullable(result)) { 614 a.put("spasm_decode_"); 615 result.mangleTypeJsImpl(a, semantics, MangleTypeJsContext(true)); 616 a.put(["(", resultPtr.to!string, ")"]); 617 } else if (semantics.isAny(result)) { 618 a.put(["spasm_decode_Handle(", resultPtr.to!string, ")"]); 619 } 620 } 621 a.put("}"); 622 } else if (semantics.isPrimitive(arg.type)) { 623 a.put(arg.name.friendlyJsName); 624 } else { 625 a.put(["objects[",arg.name.friendlyJsName,"]"]); 626 } 627 if (semantics.isNullable(arg.type)) { 628 a.put(" : undefined"); 629 } 630 } 631 632 void dumpJsArguments(Appender)(ref Semantics semantics, Argument[] args, ref Appender a) { 633 if (args.length == 0) 634 return; 635 foreach(arg; args[0..$-1]) { 636 semantics.dumpJsArgument(arg, a); 637 a.put(", "); 638 } 639 semantics.dumpJsArgument(args[$-1], a); 640 } 641 642 void dump(Appender)(ref Context context, JsExportFunction item, ref Appender a) { 643 auto semantics = context.semantics; 644 a.put(mangleName(item.parentTypeName,item.name,item.manglePostfix)); 645 a.put(": ("); 646 bool rawResult = item.result != ParseTree.init && semantics.isRawResultType(item.result); 647 if (rawResult) 648 a.put("rawResult"); 649 if (!(item.type & FunctionType.Static)) { 650 if (rawResult) 651 a.put(", "); 652 a.put("ctx"); 653 } 654 if ((rawResult || (!(item.type & FunctionType.Static))) && item.args.length > 0) 655 a.put(", "); 656 item.args.enumerate.each!((e){ 657 auto arg = e.value; 658 if (e.index > 0) 659 a.put(", "); 660 a.put(arg.name.friendlyJsName); 661 if (semantics.isNullable(arg.type)) 662 a.put(["Defined, ", arg.name.friendlyJsName]); 663 if (semantics.isCallback(arg.type)) 664 a.put(["Ctx, ", arg.name.friendlyJsName, "Ptr"]); 665 else if (semantics.isStringType(arg.type)) 666 a.put(["Len, ", arg.name.friendlyJsName, "Ptr"]); 667 }); 668 a.putLn(") => {"); 669 a.indent(); 670 a.putLn("setupMemory();"); 671 bool returns = item.result != ParseTree.init && item.result.matches[0] != "void"; 672 bool needsClose = false; 673 if (returns) { 674 if (!rawResult) 675 a.put("return "); 676 if (semantics.isStringType(item.result) || semantics.isUnion(item.result) || semantics.isNullable(item.result) || semantics.isEnum(item.result)) { 677 a.put("spasm_encode_"); 678 if (item.result.name == "WebIDL.TypeWithExtendedAttributes") 679 item.result.children[1].mangleTypeJs(a, semantics); 680 else 681 item.result.mangleTypeJs(a, semantics); 682 needsClose = true; 683 if (rawResult) 684 a.put("(rawResult, "); 685 else 686 a.put("("); 687 } else if (!semantics.isPrimitive(item.result)) { 688 a.put(["addObject("]); 689 needsClose = true; 690 } 691 } 692 if (item.type & FunctionType.Deleter) 693 a.put("delete "); 694 if (item.type & FunctionType.Static) 695 a.put(item.parentTypeName); 696 else { 697 if (item.type & FunctionType.ExposedConstructor) 698 a.put("new "); 699 a.put("objects[ctx]"); 700 } 701 if (item.type & (FunctionType.Getter | FunctionType.Setter | FunctionType.Deleter)) { 702 a.put("["); 703 semantics.dumpJsArgument(item.args[0], a); 704 a.put("]"); 705 if (item.type & FunctionType.Setter) { 706 a.put(" = "); 707 semantics.dumpJsArgument(item.args[1], a); 708 } 709 } else { 710 a.put("."); 711 a.put(item.name); 712 if (item.type & FunctionType.Attribute) { 713 if (!returns) { 714 a.put(" = "); 715 semantics.dumpJsArgument(item.args[0], a); 716 } 717 } else { 718 a.put("("); 719 semantics.dumpJsArguments(item.args, a); 720 a.put(")"); 721 } 722 } 723 if (needsClose) 724 a.put(")"); 725 a.putLn(";"); 726 a.undent(); 727 a.putLn("},"); 728 } 729 730 void dump(Appender)(ref Semantics semantics, DImportFunction item, ref Appender a) { 731 auto context = Context(semantics); 732 a.put("extern (C) "); 733 if (item.result == ParseTree.init || item.result.matches[0] == "void") 734 a.put("void"); 735 else { 736 if (!semantics.isPrimitive(item.result) && !semantics.isUnion(item.result) && !semantics.isNullable(item.result)) { 737 a.put("Handle"); 738 } else { 739 item.result.generateDType(*a, context); 740 } 741 } 742 a.put(" "); 743 a.put(mangleName(item.parentTypeName,item.name,item.manglePostfix)); 744 a.put("("); 745 if (!(item.type & FunctionType.Static)) { 746 a.put("Handle"); 747 if (item.args.length > 0) 748 a.put(", "); 749 } 750 if (item.args.length > 0) { 751 item.args.map!(arg => arg.type).array.putWithDelimiter!(generateDImports)(", ", *a, context); 752 } 753 a.putLn(");"); 754 } 755 756 auto getTemplatedTypeName(size_t idx) { 757 import std.conv : to; 758 return "T"~idx.to!string; 759 } 760 auto getSymbolInfo(string symbol) { 761 struct SymbolInfo { 762 string module_; 763 string name; 764 } 765 import std.algorithm : splitter; 766 import std.demangle; 767 import std.range : drop; 768 import std.typecons : tuple; 769 auto parts = demangle("_D"~symbol[1..$]~"v")[5..$].splitter(".").drop(2).array(); 770 return SymbolInfo(parts[0], parts[1]); 771 } 772 string generateJsGlobalBindings(IR ir, string[] jsExportFilters, ref IndentedStringAppender app) { 773 auto generatePromiseThenBindings(IR ir, string symbol, string mangled, ref IndentedStringAppender app) { 774 auto getDecoder(string mangled) { 775 if (mangled == "Aya") { 776 return "decode_string"; 777 } else if (mangled == "handle" || mangled[0] == 'S') { 778 return "decode_handle"; 779 } else if (mangled == "v") 780 return "void"; 781 else if (mangled[0] == 'E') { 782 auto info = getSymbolInfo(mangled); 783 return "spasm_decode_" ~ info.name; 784 } else { 785 if (mangled.startsWith("S8optional")) { 786 throw new Error("Promise!T.then is not yet implemented for Optional!T."); 787 } 788 auto info = getSymbolInfo(mangled); 789 if (ir.semantics.isTypedef(info.name)) { 790 throw new Error("Promise!T.then is not yet implemented for Typedefs."); 791 } 792 if (mangled.canFind("sumtype")) { 793 throw new Error("Promise!T.then is not yet implemented for SumType!Ts."); 794 } 795 } 796 return ""; 797 } 798 auto getEncoder(string mangled) { 799 if (mangled == "Aya") { 800 return "encode_string"; 801 } else if (mangled == "handle" || mangled[0] == 'S') { 802 return "encode_handle"; 803 } else if (mangled == "v") 804 return "void"; 805 else if (mangled[0] == 'E') { 806 auto info = getSymbolInfo(mangled); 807 return "spasm_encode_" ~ info.name; 808 } else { 809 if (mangled.startsWith("S8optional")) { 810 throw new Error("Promise!T.then is not yet implemented for Optional!T."); 811 } 812 auto info = getSymbolInfo(mangled); 813 if (ir.semantics.isTypedef(info.name)) { 814 throw new Error("Promise!T.then is not yet implemented for Typedefs."); 815 } 816 if (mangled.canFind("sumtype")) { 817 throw new Error("Promise!T.then is not yet implemented for SumType!Ts."); 818 } 819 } 820 return ""; 821 } 822 import std.ascii : isDigit; 823 auto len = mangled.until!(a => !a.isDigit).to!int; 824 auto prefixLen = mangled.countUntil!(a => !a.isDigit); 825 len += prefixLen+1; 826 auto argEncoder = getEncoder(mangled[prefixLen+1..len]); 827 auto resultDecoder = getDecoder(mangled[len..$]); 828 bool returns = resultDecoder != "void" && resultDecoder != ""; 829 app.putLn(["promise_then_", mangled,": (handle, ctx, ptr) => {"]); 830 app.indent(); 831 app.putLn("return addObject(objects[handle].then((r)=>{"); 832 app.indent(); 833 if (argEncoder != "" && argEncoder != "void") 834 app.putLn([argEncoder, "(0,r);"]); 835 app.put("spasm_indirect_function_get(ptr)("); 836 if (returns) { 837 app.put("512, "); 838 } 839 if (argEncoder == "void") { 840 app.putLn("ctx);"); 841 } else if (argEncoder != "") { 842 app.putLn("ctx, 0);"); 843 } else { 844 app.putLn("ctx, r);"); 845 } 846 if (returns) { 847 app.putLn(["return ", resultDecoder, "(512);"]); 848 } 849 app.undent(); 850 app.putLn("}));"); 851 app.undent(); 852 app.putLn("},"); 853 } 854 auto mappings = ["promise_then_": &generatePromiseThenBindings]; 855 foreach(filter; jsExportFilters) { 856 foreach(key, func; mappings) { 857 if (filter.startsWith(key)) 858 func(ir, filter, filter[commonPrefix(filter,key).length .. $], app); 859 } 860 } 861 return app.data; 862 } 863 void dump(Appender)(ref Semantics semantics, DBindingFunction item, ref Appender a, string[] locals) { 864 // if (item.result != ParseTree.init) { 865 // item.result.generateDType(a, Context(semantics)); 866 // a.put(" "); 867 // } else 868 // a.put("void "); 869 if (item.type & FunctionType.DictionaryConstructor) { 870 a.putLn("static auto create() {"); 871 a.indent(); 872 a.putLn(["return ", item.parentTypeName, "(spasm_add__object());"]); 873 a.undent(); 874 a.putLn("}"); 875 return; 876 } 877 bool returns = item.result != ParseTree.init && item.result.matches[0] != "void"; 878 if (returns) a.put("auto "); else a.put("void "); 879 void putFuncName() { 880 switch (item.type & (FunctionType.OpIndex | FunctionType.OpDispatch | FunctionType.OpIndexAssign)) { 881 case FunctionType.OpIndex: 882 a.put("opIndex"); 883 break; 884 case FunctionType.OpDispatch: 885 a.put("opDispatch"); 886 break; 887 case FunctionType.OpIndexAssign: 888 a.put("opIndexAssign"); 889 break; 890 default: 891 a.put(item.name.friendlyName); 892 break; 893 } 894 } 895 putFuncName(); 896 auto templArgs = item.args.filter!(a => a.templated).array(); 897 auto runArgs = item.args.filter!(a => !a.templated).array(); 898 auto anyOrOptArgs = item.args.enumerate.filter!(a => semantics.isAny(a.value.type) || semantics.isNullable(a.value.type)).array(); 899 auto anys = anyOrOptArgs.filter!(a => semantics.isAny(a.value.type)).array(); 900 auto optArgs = anyOrOptArgs.filter!(a => semantics.isNullable(a.value.type)).array(); 901 if (templArgs.length > 0) { 902 assert(anys.length == 0); 903 a.put("("); 904 semantics.dumpDParameters(templArgs, a, locals); 905 a.put(")"); 906 } else if (anyOrOptArgs.length > 0) { 907 a.put("("); 908 foreach(anyOrOpt; anyOrOptArgs[0..$-1]) { 909 a.put(getTemplatedTypeName(anyOrOpt.index)); 910 a.put(", "); 911 } 912 a.put(getTemplatedTypeName(anyOrOptArgs[$-1].index)); 913 a.put(")"); 914 } else { 915 a.put("()"); 916 } 917 a.put("("); 918 if (item.type & FunctionType.OpIndexAssign) { 919 assert(runArgs.length > 1); 920 semantics.dumpDParameter(runArgs[$-1], a, locals, 0); 921 a.put(", "); 922 semantics.dumpDParameters(runArgs[0..$-1], a, locals); 923 } else 924 semantics.dumpDParameters(runArgs, a, locals); 925 a.put(") "); 926 if (optArgs.length > 0) { 927 a.put("if ("); 928 foreach(idx, opt; optArgs) { 929 a.put("isTOrPointer!("); 930 a.put(getTemplatedTypeName(opt.index)); 931 a.put(", "); 932 opt.value.type.stripNullable.generateDType(a, Context(semantics).withLocals(locals)); 933 a.put(")"); 934 if (idx+1 < optArgs.length) 935 a.put(" && "); 936 } 937 a.put(") "); 938 } 939 a.putLn("{"); 940 a.indent(); 941 foreach(any; anys) { 942 a.putLn(["Handle _handle_", any.value.name, " = getOrCreateHandle(", any.value.name.friendlyName, ");"]); 943 } 944 bool needDropHandle = anys.length != 0; 945 if (returns) { 946 if (needDropHandle) 947 a.put("auto result = "); 948 else 949 a.put("return "); 950 if (!semantics.isPrimitive(item.result) && !semantics.isUnion(item.result) && !semantics.isNullable(item.result)) { 951 if (item.type == FunctionType.ExposedConstructor) 952 a.put([".",item.name]); 953 else 954 item.result.generateDType(a, Context(semantics).withLocals(locals)); 955 a.put("("); 956 } 957 } 958 a.put(mangleName(item.parentTypeName, item.customName.length > 0 ? item.customName : item.name,item.manglePostfix)); 959 a.put("("); 960 if (!(item.type & FunctionType.Static)) { 961 a.put(["this.", item.handle]); 962 if (item.args.length > 0) 963 a.put(", "); 964 } 965 semantics.dumpDJsArguments(item.args, a); 966 if (returns) { 967 if (!semantics.isPrimitive(item.result) && !semantics.isUnion(item.result) && !semantics.isNullable(item.result)) { 968 a.put(")"); 969 } 970 } 971 a.putLn(");"); 972 foreach(any; anys) { 973 a.putLn(["dropHandle!(T", any.index.to!string, ")(_handle_", any.value.name, ");"]); 974 } 975 if (returns && needDropHandle) 976 a.putLn("return result;"); 977 a.undent(); 978 a.putLn("}"); 979 } 980 void dumpDParameters(Appender)(ref Semantics semantics, Argument[] args, ref Appender a, string[] locals) { 981 if (args.length == 0) 982 return; 983 foreach(i, arg; args[0..$-1]) { 984 semantics.dumpDParameter(arg, a, locals, i); 985 a.put(", "); 986 } 987 semantics.dumpDParameter(args[$-1], a, locals, args.length-1); 988 } 989 void dumpDParameter(Appender)(ref Semantics semantics, Argument arg, ref Appender a, string[] locals, size_t idx) { 990 if (semantics.isAny(arg.type)) { 991 a.put("scope auto ref "); 992 a.put(getTemplatedTypeName(idx)); 993 } else { 994 if (semantics.isNullable(arg.type)) { 995 a.put("scope auto ref Optional!("); 996 a.put(getTemplatedTypeName(idx)); 997 a.put(")"); 998 } else { 999 if (!semantics.isPrimitive(arg.type)) 1000 a.put("scope ref "); 1001 arg.type.generateDType(a, Context(semantics).withLocals(locals)); 1002 } 1003 } 1004 a.put(" "); 1005 a.putCamelCase(arg.name.friendlyName); 1006 if (arg.default_.matches.length > 1) { 1007 a.put(" "); 1008 if (arg.default_.children[0].matches[0] == "null") { 1009 if (semantics.isNullable(arg.type)) { 1010 a.put("/* = no!("); 1011 arg.type.generateDType(a, Context(semantics).withSkipOptional.withLocals(locals).setDParameter); 1012 a.put(") */"); 1013 return; 1014 } 1015 } 1016 arg.default_.generateDType(a, Context(semantics).withLocals(locals).setDParameter); 1017 } 1018 } 1019 1020 void dumpDJsArguments(Appender)(ref Semantics semantics, Argument[] args, ref Appender a) { 1021 if (args.length == 0) 1022 return; 1023 foreach(arg; args[0..$-1]) { 1024 semantics.dumpDJsArgument(arg, a); 1025 a.put(", "); 1026 } 1027 semantics.dumpDJsArgument(args[$-1], a); 1028 } 1029 1030 void dumpDJsArgument(Appender)(ref Semantics semantics, Argument arg, ref Appender a) { 1031 if (semantics.isAny(arg.type)) { 1032 a.put("_handle_"); 1033 a.put(arg.name.friendlyName); 1034 return; 1035 } 1036 bool optional = semantics.isNullable(arg.type); 1037 if (optional) 1038 a.put("!"); 1039 a.put(arg.name.friendlyName); 1040 if (optional) { 1041 if (!semantics.isUnion(arg.type)) { 1042 a.put([".empty, ", arg.name.friendlyName]); 1043 a.put(".front"); 1044 } else { 1045 a.put([".empty, *", arg.name.friendlyName]); 1046 a.put(".frontRef"); 1047 } 1048 } 1049 if (!semantics.isPrimitive(arg.type) && !semantics.isUnion(arg.type)) { 1050 auto s = arg.type.matches[0] in semantics.types; 1051 if (s !is null) { 1052 if (s.tree.children[1].matches.length > 1) 1053 a.put("._parent"); 1054 else 1055 a.put(".handle"); 1056 } else 1057 a.put(".handle"); 1058 } 1059 } 1060 1061 bool isSequence(Semantics semantics, ParseTree tree) { 1062 if (tree.name == "WebIDL.ReturnType") { 1063 if (tree.matches[0] == "void") 1064 return false; 1065 return semantics.isSequence(tree.children[0]); 1066 } 1067 if (tree.name == "WebIDL.Type") { 1068 if (tree.children[0].name == "WebIDL.SingleType") 1069 return false; 1070 return tree.children[0].children[0].children[0].name == "WebIDL.SequenceType"; 1071 } 1072 if (tree.name == "WebIDL.UnionMemberType") 1073 return tree.children[1].children[0].name == "WebIDL.SequenceType"; 1074 if (tree.name == "WebIDL.NonAnyType") 1075 return tree.children[0].name == "WebIDL.SequenceType"; 1076 return false; 1077 } 1078 1079 bool isAny(ref Semantics semantics, ParseTree tree) { 1080 return tree.matches[0] == "any"; 1081 } 1082 bool isDKeyword(string s) { 1083 import std.algorithm : canFind; 1084 return dKeywords.canFind(s); 1085 } 1086 bool isJsKeyword(string s) { 1087 import std.algorithm : canFind; 1088 return jsKeywords.canFind(s); 1089 } 1090 1091 string friendlyJsName(string s) { 1092 import std.ascii; 1093 import std.conv : text; 1094 import std.utf : byChar; 1095 if (s.length == 0) 1096 return s; 1097 if (s.isJsKeyword) 1098 return s~"_"; 1099 return s; 1100 } 1101 1102 string friendlyName(string s) { 1103 import std.ascii; 1104 import std.conv : text; 1105 import std.utf : byChar; 1106 if (s.length == 0) 1107 return s; 1108 if (s.isDKeyword) 1109 return s~"_"; 1110 string clean = s.byChar.map!(c => c.isAlphaNum ? c : '_').text; 1111 if (!clean[0].isAlpha && clean[0] != '_') 1112 return '_'~clean; 1113 return clean; 1114 } 1115 1116 struct IndentedStringAppender { 1117 import std.array : Appender; 1118 import std.algorithm : each; 1119 bool beginLine = true; 1120 Appender!string appender; 1121 int i = 0; 1122 void put(char c) { 1123 putIndent(); 1124 appender.put(c); 1125 } 1126 void put(string s) { 1127 putIndent(); 1128 appender.put(s); 1129 } 1130 void put(string[] ss) { 1131 putIndent(); 1132 ss.each!(s => appender.put(s)); 1133 } 1134 void putLn(char c) { 1135 put(c); 1136 appender.put("\n"); 1137 beginLine = true; 1138 } 1139 void putLn(string s) { 1140 put(s); 1141 appender.put("\n"); 1142 beginLine = true; 1143 } 1144 void putLn(string[] ss) { 1145 put(ss); 1146 appender.put("\n"); 1147 beginLine = true; 1148 } 1149 void putIndent() { 1150 if (!beginLine) 1151 return; 1152 beginLine = false; 1153 import std.range : repeat; 1154 import std.algorithm : copy; 1155 ' '.repeat(i*2).copy(appender); 1156 } 1157 void indent() { 1158 i++; 1159 } 1160 void undent() { 1161 import std.algorithm : max; 1162 i = max(0, i-1); 1163 } 1164 auto data() { 1165 return appender.data; 1166 } 1167 } 1168 1169 struct Context { 1170 Semantics semantics; 1171 ParseTree extendedAttributeList; 1172 ParseTree partial; 1173 ParseTree includes; 1174 bool readonly = false; 1175 bool primitiveType = false; 1176 bool sumType = false; 1177 bool optional = false; 1178 bool returnType = false; 1179 bool isIncludes = false; 1180 bool skipOptional = false; 1181 bool dParameter = false; 1182 string typeName; 1183 string customName; 1184 string[] locals; 1185 } 1186 1187 auto withLocals(Context c, string[] locals) { 1188 c.locals = locals; 1189 return c; 1190 } 1191 1192 auto setDParameter(Context c) { 1193 c.dParameter = true; 1194 return c; 1195 } 1196 1197 auto withSkipOptional(Context c) { 1198 c.skipOptional = true; 1199 return c; 1200 } 1201 1202 bool isEnum(ref Context context, ParseTree tree) { 1203 return context.semantics.isEnum(tree); 1204 } 1205 bool isEnum(ref Semantics semantics, ParseTree tree) { 1206 if (tree.name == "WebIDL.TypeWithExtendedAttributes" || tree.name == "WebIDL.UnionMemberType") 1207 return semantics.isEnum(tree.children[1]); 1208 return semantics.isEnum(tree.matches[0]); 1209 } 1210 1211 bool isEnum(ref Semantics semantics, string typeName) { 1212 if (auto p = typeName in semantics.types) { 1213 return p.tree.name == "WebIDL.Enum"; 1214 } 1215 return false; 1216 } 1217 bool isNullableTypedef(ref Semantics semantics, ParseTree tree) { 1218 if (tree.name == "WebIDL.ReturnType") { 1219 if (tree.matches[0] == "void") 1220 return false; 1221 return semantics.isNullableTypedef(tree.children[0]); 1222 } 1223 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 1224 return semantics.isNullableTypedef(tree.children[1]); 1225 assert(tree.name == "WebIDL.Type" || tree.name == "WebIDL.UnionMemberType"); 1226 if (tree.name == "WebIDL.UnionMemberType" && tree.children[0].name == "WebIDL.UnionType") 1227 return false; 1228 string typeName = tree.getTypeName(); 1229 if (!semantics.isTypedef(typeName)) 1230 return false; 1231 if (tree.matches[$-1] == "?") 1232 return true; 1233 return false; 1234 } 1235 1236 bool isTypedef(ref Context context, ParseTree tree) { 1237 return context.semantics.isTypedef(tree); 1238 } 1239 1240 bool isTypedef(ref Semantics semantics, ParseTree tree) { 1241 string typeName = tree.getTypeName(); 1242 return semantics.isTypedef(typeName); 1243 } 1244 1245 bool isTypedef(ref Context context, string typeName) { 1246 return context.semantics.isTypedef(typeName); 1247 } 1248 bool isTypedef(ref Semantics semantics, string typeName) { 1249 if (auto p = typeName in semantics.types) { 1250 return p.tree.name == "WebIDL.Typedef"; 1251 } 1252 return false; 1253 } 1254 bool isCallback(ref Context context, string typeName) { 1255 return context.semantics.isCallback(typeName); 1256 } 1257 bool isCallback(ref Semantics semantics, ParseTree tree) { 1258 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 1259 return semantics.isCallback(tree.children[1].matches[0]); 1260 return semantics.isCallback(tree.children[0].matches[0]); 1261 } 1262 bool isCallback(ref Semantics semantics, string typeName) { 1263 if (semantics.isTypedef(typeName)) { 1264 auto aliased = semantics.getAliasedType(typeName); 1265 return semantics.isCallback(aliased); 1266 } 1267 if (auto p = typeName in semantics.types) { 1268 return p.tree.name == "WebIDL.CallbackRest"; 1269 } 1270 return false; 1271 } 1272 1273 bool isPartial(ref Context context) { 1274 return context.partial.matches.length > 0; 1275 } 1276 1277 void putCamelCase(Appender)(ref Appender a, string s) { 1278 import std.algorithm : until; 1279 import std.uni : isUpper, isLower, asLowerCase; 1280 import std.conv : text; 1281 if (s.length == 0) 1282 return; 1283 if (s[0].isLower) { 1284 a.put(s); 1285 return; 1286 } 1287 import std.string : toLower; 1288 auto head = s.until!(isLower).asLowerCase.text; 1289 if (head.length == 1) { 1290 a.put(head); 1291 a.put(s[head.length .. $]); 1292 return; 1293 } 1294 auto tail = s[head.length-1 .. $]; 1295 a.put(head[0..$-1]); 1296 a.put(tail); 1297 } 1298 1299 string toCamelCase(string s) { 1300 auto app = appender!string; 1301 app.putCamelCase(s); 1302 return app.data; 1303 } 1304 1305 string mangleJsName(StructNode node, FunctionNode fun) { 1306 return mangleName(node.name, fun.customName != "" ? fun.customName : fun.name, fun.manglePostfix); 1307 } 1308 1309 string mangleName(string typeName, string name, string appendix = "") { 1310 import std.ascii : toLower, toUpper; 1311 import std.array : appender; 1312 auto app = appender!string; 1313 app.put(typeName); 1314 app.put("_"); 1315 app.put(name); 1316 if (appendix.length > 0) { 1317 app.put("_"); 1318 app.put(appendix); 1319 } 1320 return app.data; 1321 } 1322 1323 bool isNullable(ref Semantics semantics, ParseTree tree) { 1324 if (tree.name == "WebIDL.ReturnType") { 1325 if (tree.matches[0] == "void") 1326 return false; 1327 return semantics.isNullable(tree.children[0]); 1328 } 1329 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 1330 return semantics.isNullable(tree.children[1]); 1331 if (tree.name == "WebIDL.UnionMemberType") 1332 return tree.matches[$-1] == "?"; 1333 if (tree.name == "WebIDL.InterfaceRest") 1334 return false; 1335 assert(tree.name == "WebIDL.Type"); 1336 if (tree.matches[$-1] == "?") 1337 return true; 1338 string typeName = tree.getTypeName(); 1339 if (semantics.isTypedef(typeName)) { 1340 return semantics.isNullable(semantics.getAliasedType(typeName)); 1341 } 1342 return false; 1343 } 1344 1345 bool isRawResultType(ref Semantics semantics, ParseTree tree) { 1346 if (tree.name == "WebIDL.ReturnType") { 1347 if (tree.matches[0] == "void") 1348 return false; 1349 return semantics.isRawResultType(tree.children[0]); 1350 } 1351 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 1352 return semantics.isRawResultType(tree.children[1]); 1353 if (tree.name == "WebIDL.InterfaceRest") 1354 return false; 1355 assert(tree.name == "WebIDL.Type"); 1356 return semantics.isNullable(tree) || 1357 semantics.isStringType(tree) || semantics.isUnion(tree); 1358 } 1359 1360 bool isStringType(ref Semantics semantics, ParseTree tree) { 1361 if (tree.name == "WebIDL.ReturnType") { 1362 if (tree.matches[0] == "void") 1363 return false; 1364 return semantics.isStringType(tree.children[0]); 1365 } 1366 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 1367 return semantics.isStringType(tree.children[1]); 1368 if (tree.name == "WebIDL.InterfaceRest") 1369 return false; 1370 assert(tree.name == "WebIDL.Type"); 1371 string typeName = tree.getTypeName(); 1372 if (semantics.isTypedef(typeName)) { 1373 return semantics.isStringType(semantics.getAliasedType(typeName)); 1374 } 1375 if (tree.children[0].name != "WebIDL.SingleType") 1376 return false; 1377 if (tree.children[0].matches[0] == "any") 1378 return false; 1379 if (tree.children[0].children[0].children[0].name == "WebIDL.StringType") 1380 return true; 1381 return false; 1382 } 1383 bool isUnion(ref Context context, ParseTree tree) { 1384 return context.semantics.isUnion(tree); 1385 } 1386 1387 bool isUnion(ref Semantics semantics, ParseTree tree) { 1388 if (tree.name == "WebIDL.ReturnType") { 1389 if (tree.matches[0] == "void") 1390 return false; 1391 return semantics.isUnion(tree.children[0]); 1392 } 1393 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 1394 return semantics.isUnion(tree.children[1]); 1395 if (tree.name == "WebIDL.InterfaceRest") 1396 return false; 1397 assert(tree.name == "WebIDL.Type" || tree.name == "WebIDL.UnionMemberType"); 1398 if (tree.children[0].name == "WebIDL.UnionType") 1399 return true; 1400 string typeName = tree.getTypeName(); 1401 if (semantics.isTypedef(typeName)) { 1402 return semantics.isUnion(semantics.getAliasedType(typeName)); 1403 } 1404 return false; 1405 } 1406 auto getAliasedType(ref Context context, string typeName) { 1407 assert(typeName in context.semantics.types); 1408 assert(context.semantics.types[typeName].tree.name == "WebIDL.Typedef"); 1409 return context.semantics.types[typeName].tree.children[0]; 1410 } 1411 1412 auto getAliasedType(ref Semantics semantics, string typeName) { 1413 assert(typeName in semantics.types); 1414 assert(semantics.types[typeName].tree.name == "WebIDL.Typedef"); 1415 return semantics.types[typeName].tree.children[0]; 1416 } 1417 1418 bool isPrimitive(Context context, ParseTree tree) { 1419 return context.semantics.isPrimitive(tree); 1420 } 1421 1422 bool isPrimitive(ref Semantics semantics, ParseTree tree) { 1423 if (tree.name == "WebIDL.ReturnType") { 1424 if (tree.matches[0] == "void") 1425 return false; 1426 return semantics.isPrimitive(tree.children[0]); 1427 } 1428 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 1429 return semantics.isPrimitive(tree.children[1]); 1430 string typeName = tree.getTypeName(); 1431 if (tree.name == "WebIDL.UnionMemberType") { 1432 if (tree.children[1].name != "WebIDL.NonAnyType") 1433 return false; 1434 return tree.children[1].children[0].name == "WebIDL.PrimitiveType"; 1435 } 1436 if (tree.name == "WebIDL.InterfaceRest") 1437 return false; 1438 assert(tree.name == "WebIDL.Type"); 1439 if (semantics.isEnum(typeName) || semantics.isCallback(typeName)) 1440 return true; 1441 if (semantics.isTypedef(typeName)) { 1442 return semantics.isPrimitive(semantics.getAliasedType(typeName)); 1443 } 1444 if (tree.children[0].name != "WebIDL.SingleType") 1445 return false; 1446 if (tree.children[0].matches[0] == "any") 1447 return false; 1448 if (semantics.isStringType(tree)) 1449 return true; 1450 if (tree.children[0].children[0].name != "WebIDL.NonAnyType") 1451 return false; 1452 return tree.children[0].children[0].children[0].name == "WebIDL.PrimitiveType"; 1453 } 1454 1455 string getTypeName(ParseTree tree) { 1456 if (tree.name == "WebIDL.ReturnType") { 1457 assert(tree.matches[0] != "void"); 1458 return tree.children[0].getTypeName(); 1459 } 1460 if (tree.name == "WebIDL.InterfaceRest") { 1461 return tree.matches[0]; 1462 } 1463 assert(tree.name == "WebIDL.TypeWithExtendedAttributes" || tree.name == "WebIDL.Type" || tree.name == "WebIDL.UnionMemberType"); 1464 if (tree.name == "WebIDL.UnionMemberType") { 1465 assert(tree.children[1].name == "WebIDL.NonAnyType"); 1466 return tree.children[1].matches[0]; 1467 } 1468 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 1469 return tree.children[1].matches[0]; 1470 return tree.matches[0]; 1471 } 1472 1473 auto isEmpty(string s) { 1474 return s == ""; 1475 } 1476 auto isEmpty(string[] matches) { 1477 import std.algorithm : all; 1478 return matches.length == 0 || matches.all!(m => m.isEmpty); 1479 } 1480 1481 template putWithDelimiter(alias Generator) 1482 { 1483 void putWithDelimiter(Appender, Ts...)(ParseTree[] children, string delimiter,ref Appender a, Ts args) { 1484 import std.algorithm : each, filter; 1485 import std.array : array; 1486 auto nonEmpty = children.filter!(c => !c.matches.isEmpty).array; 1487 // need to filter first 1488 if (nonEmpty.length > 0) { 1489 nonEmpty[0..$-1].each!((c){if (c.matches.isEmpty) return; Generator(c, a, args);a.put(delimiter);}); 1490 Generator(nonEmpty[$-1], a, args); 1491 } 1492 } 1493 } 1494 1495 auto extractArgument(ParseTree tree) { 1496 assert(tree.name == "WebIDL.Argument"); 1497 auto argRest = tree.children[$-1]; 1498 if (argRest.matches[0] == "optional") { 1499 return argRest.children[1].matches[0]; 1500 } else { 1501 return argRest.children[2].matches[0]; 1502 } 1503 } 1504 auto extractDefault(ParseTree tree) { 1505 assert(tree.name == "WebIDL.Argument"); 1506 auto argRest = tree.children[$-1]; 1507 string typeName; 1508 if (argRest.matches[0] == "optional") { 1509 return argRest.children[2]; 1510 } else { 1511 return ParseTree.init; 1512 } 1513 } 1514 auto extractType(ParseTree tree) { 1515 assert(tree.name == "WebIDL.Argument"); 1516 auto argRest = tree.children[$-1]; 1517 string typeName; 1518 if (argRest.matches[0] == "optional") { 1519 return argRest.children[0].children[1]; 1520 } else { 1521 return argRest.children[0]; 1522 } 1523 } 1524 1525 string extractTypeName(ParseTree tree) { 1526 if (tree.name == "WebIDL.Argument") { 1527 auto argRest = tree.children[$-1]; 1528 if (argRest.matches[0] == "optional") { 1529 return extractTypeName(argRest.children[0].children[1]); 1530 } 1531 return extractTypeName(argRest.children[0]); 1532 } 1533 assert(tree.name == "WebIDL.Type"); 1534 string typeName = tree.matches[0]; 1535 if (typeName == "any") 1536 return "Any"; 1537 if (typeName == "DOMString") 1538 return "string"; 1539 if (typeName == "boolean") 1540 return "bool"; 1541 return typeName; 1542 } 1543 auto extractArguments(ParseTree tree) { 1544 import std.algorithm : map; 1545 assert(tree.name == "WebIDL.ArgumentList"); 1546 return tree.children.map!(c => c.extractArgument); 1547 } 1548 auto extractArgumentRests(ParseTree tree) { 1549 assert(tree.name == "WebIDL.ArgumentList"); 1550 return tree.children.map!(c => c.children[1]); 1551 } 1552 auto extractDefaults(ParseTree tree) { 1553 import std.algorithm : map; 1554 assert(tree.name == "WebIDL.ArgumentList"); 1555 return tree.children.map!(c => c.extractDefault); 1556 } 1557 auto extractTypes(ParseTree tree) { 1558 import std.algorithm : map; 1559 assert(tree.name == "WebIDL.ArgumentList"); 1560 return tree.children.map!(c => c.extractType); 1561 } 1562 auto extractTypeNames(ParseTree tree) { 1563 import std.algorithm : map; 1564 assert(tree.name == "WebIDL.ArgumentList"); 1565 return tree.children.map!(c => c.extractTypeName); 1566 } 1567 ParseTree getArgumentList(ref Semantics semantics, ParseTree tree) { 1568 auto p = tree.matches[0] in semantics.types; 1569 assert(p != null); 1570 assert(p.tree.name == "WebIDL.CallbackRest"); 1571 return p.tree.children[2]; 1572 } 1573 auto getType(ref Semantics semantics, ParseTree tree) { 1574 return semantics.getType(tree.matches[0]); 1575 } 1576 auto getType(ref Semantics semantics, string name) { 1577 auto p = name in semantics.types; 1578 if (p is null) { 1579 writeln("Failed to find "~name); 1580 return ParseTree.init; 1581 } 1582 return p.tree; 1583 } 1584 auto getType(ref Context context, string name) { 1585 return context.semantics.getType(name); 1586 } 1587 auto getMatchingPartials(ref Semantics semantics, string name) { 1588 auto isInterface = (Type p) => p.tree.children[0].children[0].name == "WebIDL.PartialInterfaceOrPartialMixin"; 1589 auto matches = (Type p) => p.tree.children[0].children[0].children[0].children[0].matches[0] == name; 1590 return semantics.partials.filter!(p => isInterface(p) && matches(p)).map!(t => t.tree).array(); 1591 } 1592 1593 auto getMatchingPartials(ref Context context, string name) { 1594 return context.semantics.getMatchingPartials(name); 1595 } 1596 1597 uint getSizeOf(ref Semantics semantics, ParseTree tree) { 1598 switch(tree.name) { 1599 case "WebIDL.IntegerType": 1600 if (tree.matches[0] == "long") { 1601 if (tree.matches.length > 1 && !is32Bit) 1602 return 8; 1603 return 4; 1604 } 1605 return 2; 1606 case "WebIDL.StringType": 1607 return 8; 1608 case "WebIDL.FloatType": 1609 if (tree.matches[0] == "float") 1610 return 4; 1611 return 8; 1612 case "WebIDL.PrimitiveType": 1613 if (tree.children.length == 0) { 1614 if (tree.matches[0] == "boolean") 1615 return 4; 1616 return 1; 1617 } else 1618 goto default; 1619 case "WebIDL.SingleType": 1620 if (tree.matches[0] == "any") 1621 return 4; 1622 goto default; 1623 case "WebIDL.Identifier": 1624 case "WebIDL.SequenceType": 1625 case "WebIDL.Enum": 1626 case "WebIDL.RecordType": 1627 case "WebIDL.PromiseType": 1628 case "WebIDL.BufferRelatedType": 1629 return 4; 1630 case "WebIDL.UnionType": 1631 return 4 + tree.children.map!(c => semantics.getSizeOf(c)).maxElement; 1632 case "WebIDL.NonAnyType": 1633 if (tree.matches[0] == "object" || tree.matches[0] == "symbol" || tree.matches[0] == "Error" || tree.matches[0] == "FrozenArray") { 1634 return 4 + semantics.getSizeOf(tree.children[$-1]); 1635 } 1636 goto default; 1637 case "WebIDL.Null": 1638 return tree.matches[0] == "?" ? 4 : 0; 1639 default: 1640 return tree.children.map!(c => semantics.getSizeOf(c)).sum; 1641 } 1642 } 1643 string mangleTypeJs(ParseTree tree, ref Semantics semantics) { 1644 auto app = appender!string; 1645 tree.mangleTypeJs(app, semantics); 1646 return app.data; 1647 } 1648 string mangleTypeJs(ParseTree tree, ref Semantics semantics, MangleTypeJsContext context) { 1649 auto app = appender!string; 1650 tree.mangleTypeJsImpl(app, semantics, context); 1651 return app.data; 1652 } 1653 1654 struct MangleTypeJsContext { 1655 bool skipOptional; 1656 bool inUnion = false; 1657 } 1658 void mangleTypeJs(Appender)(ParseTree tree, ref Appender a, ref Semantics semantics) { 1659 MangleTypeJsContext context; 1660 tree.mangleTypeJsImpl(a, semantics, context); 1661 } 1662 void mangleTypeJsImpl(Appender)(ParseTree tree, ref Appender a, ref Semantics semantics, MangleTypeJsContext context) { 1663 switch (tree.name) { 1664 case "WebIDL.CallbackRest": 1665 a.put("callback_"); 1666 tree.children[1].mangleTypeJsImpl(a, semantics, context); 1667 if (tree.children[2].children.length > 0) { 1668 a.put("_"); 1669 tree.children[2].mangleTypeJsImpl(a, semantics, context); 1670 } 1671 break; 1672 case "WebIDL.ArgumentList": 1673 tree.children.putWithDelimiter!(mangleTypeJsImpl)("_", a, semantics, context); 1674 break; 1675 case "WebIDL.Argument": 1676 auto type = tree.extractType; 1677 if (!semantics.isPrimitive(type) && !semantics.isUnion(type)) { 1678 a.put("Handle"); 1679 break; 1680 } 1681 tree.children[1].mangleTypeJsImpl(a, semantics, context); 1682 break; 1683 case "WebIDL.UnsignedIntegerType": 1684 if (tree.matches[0] == "unsigned") 1685 a.put("u"); 1686 tree.children[0].mangleTypeJsImpl(a, semantics, context); 1687 break; 1688 case "WebIDL.IntegerType": 1689 if (tree.matches[0] == "long") { 1690 if (tree.matches.length > 1) 1691 a.put(is32Bit ? "int" : "long"); 1692 else 1693 a.put("int"); 1694 } else 1695 a.put(tree.matches[0]); 1696 break; 1697 case "WebIDL.RecordType": 1698 a.put("record"); 1699 // tree.children.putWithDelimiter!(mangleTypeJsImpl)("_", a, semantics, context); 1700 break; 1701 case "WebIDL.SequenceType": 1702 if (!context.skipOptional && tree.matches[$-1] == "?") 1703 a.put("optional_"); 1704 a.put("sequence"); 1705 // tree.children[0].mangleTypeJsImpl(a, semantics, context); 1706 break; 1707 case "WebIDL.SingleType": 1708 if (tree.matches[0] == "any") { 1709 a.put("Handle"); 1710 } else if (tree.matches[0] == "void") { 1711 a.put("void"); 1712 } else { 1713 tree.children[0].mangleTypeJsImpl(a, semantics, context); 1714 } 1715 break; 1716 case "WebIDL.Type": 1717 string typeName = getTypeName(tree); 1718 if (semantics.isTypedef(typeName)) { 1719 if (tree.matches[$-1] == "?") { 1720 if (context.skipOptional) 1721 context.skipOptional = false; 1722 else 1723 a.put("optional_"); 1724 } 1725 auto aliasMangled = semantics.getAliasedType(typeName).mangleTypeJs(semantics, context); 1726 if (aliasMangled.length < typeName.length) 1727 a.put(aliasMangled); 1728 else 1729 a.put(typeName); 1730 return; 1731 } 1732 if (tree.children.length == 2 && tree.children[$-1].matches[0] == "?") { 1733 if (context.skipOptional) 1734 context.skipOptional = false; 1735 else 1736 a.put("optional_"); 1737 } 1738 tree.children[0].mangleTypeJsImpl(a, semantics, context); 1739 break; 1740 case "WebIDL.UnionType": 1741 a.put("union"); 1742 a.put(tree.children.length.to!string); 1743 a.put("_"); 1744 context.inUnion = true; 1745 tree.children.putWithDelimiter!(mangleTypeJsImpl)("_", a, semantics, context); 1746 break; 1747 case "WebIDL.UnionMemberType": 1748 if (tree.children[1].name == "WebIDL.NonAnyType") 1749 tree.children[1].mangleTypeJsImpl(a, semantics, context); 1750 else { 1751 tree.children[0].mangleTypeJsImpl(a, semantics, context); 1752 if (tree.children[$-1].matches[0] == "?") 1753 a.put("_Null"); 1754 } 1755 break; 1756 case "WebIDL.NonAnyType": 1757 if (tree.children.length > 1 && tree.children[$-1].matches[0] == "?") { 1758 if (context.skipOptional) 1759 context.skipOptional = false; 1760 else 1761 a.put("optional_"); 1762 } 1763 if (tree.children[0].name == "WebIDL.Null") 1764 a.put(tree.matches[0]); // Maybe return here? 1765 if (tree.matches[0] == "FrozenArray") 1766 assert(false); 1767 tree.children[0].mangleTypeJsImpl(a, semantics, context); 1768 break; 1769 case "WebIDL.FloatType": 1770 a.put(tree.matches[0]); 1771 break; 1772 case "WebIDL.TypeWithExtendedAttributes": 1773 tree.children[1].mangleTypeJsImpl(a, semantics, context); 1774 break; 1775 case "WebIDL.Identifier": 1776 auto typeName = tree.matches[0]; 1777 if (!context.inUnion && !semantics.isEnum(tree)) { 1778 a.put("Handle"); 1779 return; 1780 } 1781 // TODO: could be nullable typedef 1782 if (semantics.isTypedef(typeName)) { 1783 auto aliasMangled = semantics.getAliasedType(typeName).mangleTypeJs(semantics, context); 1784 if (aliasMangled.length < tree.matches[0].length) { 1785 a.put(aliasMangled); 1786 return; 1787 } 1788 } 1789 a.put(tree.matches[0]); 1790 break; 1791 case "WebIDL.StringType": 1792 a.put("string"); 1793 break; 1794 case "WebIDL.ArgumentRest": 1795 tree.children[0].mangleTypeJsImpl(a, semantics, context); 1796 break; 1797 case "WebIDL.PrimitiveType": 1798 if (tree.children.length == 1) { 1799 tree.children[0].mangleTypeJsImpl(a, semantics, context); 1800 } else { 1801 switch (tree.matches[0]) { 1802 case "byte": a.put("byte"); break; 1803 case "octet": a.put("ubyte"); break; 1804 case "boolean": a.put("bool"); break; 1805 default: a.put(tree.matches[0]); break; 1806 } 1807 } 1808 break; 1809 default: 1810 tree.children.each!(c => c.mangleTypeJsImpl(a, semantics, context)); 1811 } 1812 } 1813 1814 string generateDImports(ParseTree tree, Context context) { 1815 auto app = IndentedStringAppender(); 1816 tree.generateDImports(app, context); 1817 return app.data; 1818 } 1819 void generateDImports(Appender)(ParseTree tree, ref Appender a, Context context) { 1820 import std.algorithm : each, joiner, map; 1821 import std.range : chain; 1822 import std.conv : text; 1823 switch (tree.name) { 1824 case "WebIDL.InterfaceRest": 1825 case "WebIDL.InterfaceMembers": 1826 case "WebIDL.InterfaceMember": 1827 case "WebIDL.ReadOnlyMember": 1828 case "WebIDL.AttributeRest": 1829 break; 1830 case "WebIDL.TypeWithExtendedAttributes": 1831 tree.children[1].generateDImports(a, context); 1832 break; 1833 case "WebIDL.NonAnyType": 1834 bool optional = !context.skipOptional && (context.optional || tree.children[$-1].name == "WebIDL.Null" && tree.children[$-1].matches[0] == "?"); 1835 if (optional) { 1836 if (context.returnType) 1837 a.put("Optional!("); 1838 else 1839 a.put("bool, "); 1840 } 1841 if (!(optional && context.returnType) && !context.primitiveType && !context.sumType && !(context.returnType && tree.children[0].name == "WebIDL.SequenceType")) 1842 a.put("Handle"); 1843 else 1844 tree.children.each!(c => c.generateDType(a, context)); 1845 if (optional && context.returnType) 1846 a.put(")"); 1847 break; 1848 case "WebIDL.SingleType": 1849 if (tree.matches[0] == "any") { 1850 a.put("Handle"); 1851 } else 1852 tree.children[0].generateDImports(a, context); 1853 break; 1854 case "WebIDL.PrimitiveType": 1855 if (tree.children.length == 0) { 1856 switch (tree.matches[0]) { 1857 case "byte": a.put("byte"); break; 1858 case "octet": a.put("ubyte"); break; 1859 case "boolean": a.put("bool"); break; 1860 default: a.put(tree.matches[0]); break; 1861 } 1862 } else 1863 tree.children[0].generateDImports(a, context); 1864 break; 1865 case "WebIDL.StringType": 1866 a.put("string"); 1867 break; 1868 case "WebIDL.ArgumentName": 1869 case "WebIDL.AttributeName": 1870 break; 1871 case "WebIDL.UnrestrictedFloatType": 1872 // TODO: handle unrestricted 1873 tree.children[0].generateDImports(a, context); 1874 break; 1875 case "WebIDL.UnsignedIntegerType": 1876 if (tree.matches[0] == "unsigned") 1877 a.put("u"); 1878 tree.children[0].generateDType(a, context); 1879 break; 1880 case "WebIDL.IntegerType": 1881 if (tree.matches[0] == "long") { 1882 if (tree.matches.length > 1) 1883 a.put(is32Bit ? "int" : "long"); 1884 else 1885 a.put("int"); 1886 } else 1887 a.put(tree.matches[0]); 1888 break; 1889 case "WebIDL.ExtendedAttributeList": 1890 break; 1891 case "WebIDL.FloatType": 1892 case "WebIDL.Identifier": 1893 a.put(tree.matches[0]); 1894 break; 1895 case "WebIDL.SpecialOperation": 1896 case "WebIDL.RegularOperation": 1897 break; 1898 case "WebIDL.Type": 1899 context.optional = false; 1900 if (context.isUnion(tree)) { 1901 bool optional = tree.matches[$-1] == "?"; 1902 context.optional = optional; 1903 if (optional) { 1904 if (context.returnType) 1905 a.put("Optional!("); 1906 else 1907 a.put("bool, "); 1908 } 1909 a.put("scope ref "); 1910 if (!context.isTypedef(tree)) { 1911 a.put("SumType!("); 1912 } 1913 context.sumType = true; 1914 tree.children[0].children.putWithDelimiter!(generateDImports)(", ", a, context.withSkipOptional); 1915 if (optional && context.returnType) 1916 a.put(")"); 1917 if (!context.isTypedef(tree)) 1918 a.put(")"); 1919 break; 1920 } else if (context.isTypedef(tree) && context.semantics.isNullable(tree)) { 1921 auto typeName = tree.getTypeName(); 1922 auto aliasedType = context.semantics.getAliasedType(typeName); 1923 aliasedType.generateDImports(a, context); 1924 } else { 1925 context.primitiveType = context.isPrimitive(tree); 1926 tree.children[0].generateDImports(a, context); 1927 } 1928 break; 1929 case "WebIDL.UnionMemberType": 1930 context.optional = false; 1931 if (!context.isTypedef(tree) && context.isUnion(tree)) { 1932 bool optional = tree.matches[$-1] == "?"; 1933 context.optional = optional; 1934 if (optional) { 1935 if (context.returnType) 1936 a.put("Optional!("); 1937 else 1938 a.put("bool, "); 1939 } 1940 a.put("SumType!("); 1941 tree.children[0].children.putWithDelimiter!(generateDImports)(", ", a, context); 1942 if (optional && context.returnType) 1943 a.put(")"); 1944 a.put(")"); 1945 break; 1946 } else 1947 tree.children.each!(c => c.generateDImports(a, context)); 1948 break; 1949 case "WebIDL.IncludesStatement": 1950 break; 1951 case "WebIDL.ReturnType": 1952 context.returnType = true; 1953 if (tree.children.length > 0) { 1954 if (context.isPrimitive(tree.children[0]) || context.isUnion(tree.children[0]) || tree.matches[$-1] == "?") 1955 tree.children[0].generateDImports(a, context); 1956 else if (tree.matches[0] != "void") 1957 a.put("Handle"); 1958 else 1959 a.put("void"); 1960 } 1961 else 1962 a.put(tree.matches[0]); 1963 break; 1964 case "WebIDL.OperationRest": 1965 case "WebIDL.Enum": 1966 case "WebIDL.Dictionary": 1967 case "WebIDL.DictionaryMember": 1968 case "WebIDL.Iterable": 1969 case "WebIDL.Typedef": 1970 case "WebIDL.SetlikeRest": 1971 case "WebIDL.MaplikeRest": 1972 case "WebIDL.CallbackRest": 1973 case "WebIDL.MixinRest": 1974 break; 1975 case "WebIDL.SequenceType": 1976 a.put("Handle"); 1977 break; 1978 case "WebIDL.MixinMember": 1979 case "WebIDL.Const": 1980 case "WebIDL.Partial": 1981 break; 1982 case "WebIDL.PromiseType": 1983 a.put("Promise!("); 1984 tree.children[0].generateDImports(a, context); 1985 a.put(")"); 1986 break; 1987 case "WebIDL.PartialInterfaceRest": 1988 tree.children[1].generateDImports(a, context); 1989 break; 1990 default: 1991 tree.children.each!(c => generateDImports(c, a, context)); 1992 } 1993 } 1994 1995 string orNone(string s) { 1996 if (s.length == 0) 1997 return "none"; 1998 return s; 1999 } 2000 2001 auto createOptionalOverloads(FunctionNode func) { 2002 static bool notOptional(ref Argument arg) { 2003 return arg.argRest.matches[0] != "optional"; 2004 } 2005 return chain([func],func.args.retro.until!(notOptional).enumerate.map!((t){ 2006 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); 2007 })); 2008 } 2009 2010 IR toIr(ref Module module_) { 2011 auto app = appender!(Node[]); 2012 module_.iterate!(toIr)(app, Context(module_.semantics)); 2013 return new IR([new ModuleNode(module_, app.data)], module_.semantics); 2014 } 2015 IR toIr(ref Semantics semantics) { 2016 auto app = appender!(ModuleNode[]); 2017 foreach(module_; semantics.modules) { 2018 auto mApp = appender!(Node[]); 2019 module_.iterate!(toIr)(mApp, Context(semantics)); 2020 app.put(new ModuleNode(module_, mApp.data)); 2021 } 2022 return new IR(app.data, semantics); 2023 } 2024 2025 void toIr(Appender)(ParseTree tree, ref Appender a, Context context) { 2026 switch (tree.name) { 2027 case "WebIDL.Namespace": 2028 // TODO: get matching partials 2029 auto app = appender!(Node[]); 2030 tree.children[1..$].each!(c => toIr(c, app, context)); 2031 a.put(new StructNode(tree.children[0].matches[0], ParseTree.init, app.data, Yes.isStatic)); 2032 break; 2033 case "WebIDL.InterfaceRest": 2034 ParseTree baseType; 2035 if (tree.children[1].children.length > 0 && tree.children[1].children[0].matches.length != 0) 2036 baseType = tree.children[1].children[0]; 2037 auto app = appender!(Node[]); 2038 tree.children[2..$].each!(c => toIr(c, app, context)); 2039 a.put(new StructNode(tree.children[0].matches[0], baseType, app.data)); 2040 if (context.extendedAttributeList != ParseTree.init) { 2041 auto constructors = context.extendedAttributeList.children.filter!(attr => attr.matches[0] == "Constructor").array(); 2042 auto exposeds = context.extendedAttributeList.children.filter!(attr => attr.matches[0] == "Exposed").map!(e => e.children[0].children[1].children).joiner(); 2043 bool overloads = constructors.length > 1; 2044 foreach(constructor; constructors) { 2045 Argument[] args; 2046 string manglePostfix; 2047 if (constructor.children[0].children.length > 1) { 2048 auto argList = constructor.children[0].children[1]; 2049 args = zip(argList.extractArguments,argList.extractTypes,argList.extractDefaults,argList.extractArgumentRests).map!(a=>Argument(a[0],a[1],a[2],a[3])).array(); 2050 } 2051 if (overloads) { 2052 manglePostfix = "_" ~ args.map!(arg => arg.type.mangleTypeJs(context.semantics)).joiner("_").text; 2053 } 2054 auto name = tree.children[0].matches[0]; 2055 auto result = context.semantics.getType(tree.children[0].matches[0]); 2056 foreach(exposed; exposeds) { 2057 auto baseTypeName = exposed.matches[0]; 2058 a.put(new ExposedConstructorNode(name, args, result, baseTypeName, manglePostfix)); 2059 } 2060 } 2061 } 2062 break; 2063 case "WebIDL.IncludesStatement": 2064 context.isIncludes = true; 2065 context.includes = tree; 2066 context.typeName = tree.children[1].matches[0]; 2067 ParseTree mixinRest = context.getType(context.typeName); 2068 auto partials = context.getMatchingPartials(context.typeName); 2069 auto app = appender!(Node[]); 2070 mixinRest.children[1].toIr(app, context); 2071 partials.each!((c){ 2072 if (c.children[0].children[0].children[0].name == "WebIDL.MixinRest") 2073 toIr(c.children[0].children[0].children[0].children[1], app, context); 2074 else 2075 toIr(c.children[0], app, context); 2076 }); 2077 a.put(new StructIncludesNode(tree.children[0].matches[0], tree.children[1].matches[0], app.data)); 2078 break; 2079 case "WebIDL.Iterable": 2080 // a.putLn("// TODO: add iterable"); 2081 break; 2082 case "WebIDL.MixinRest": 2083 context.typeName = tree.children[0].matches[0]; 2084 auto partials = context.getMatchingPartials(context.typeName); 2085 auto app = appender!(Node[]); 2086 tree.children[1].toIr(app, context); 2087 partials.each!(c => toIr(c.children[0].children[0].children[0].children[1], app, context)); 2088 a.put(new MixinNode(tree.children[0].matches[0], app.data)); 2089 break; 2090 case "WebIDL.Const": 2091 a.put(new ConstNode(tree.children[0].generateDType(context), 2092 tree.children[1].generateDType(context), 2093 tree.children[2].generateDType(context))); 2094 break; 2095 case "WebIDL.InterfaceMember": 2096 context.extendedAttributeList = tree.children[0]; 2097 tree.children[1].toIr(a, context); 2098 break; 2099 case "WebIDL.ReadOnlyMember": 2100 context.readonly = true; 2101 tree.children[0].toIr(a, context); 2102 break; 2103 case "WebIDL.MixinMember": 2104 if (tree.children[0].name == "WebIDL.ReadOnly") { 2105 if (tree.children[0].matches[0] == "readonly") { 2106 context.readonly = true; 2107 } 2108 tree.children[1].toIr(a, context); 2109 } else 2110 tree.children[0].toIr(a, context); 2111 break; 2112 case "WebIDL.AttributeRest": 2113 if (context.isIncludes) { 2114 auto name = tree.children[1].matches[0]; 2115 auto baseName = context.includes.children[0].matches[0]; 2116 auto attrType = tree.children[0]; 2117 auto attrArg = Argument(name, attrType); 2118 2119 if (!context.readonly) 2120 a.put(new FunctionNode( name, [attrArg], ParseTree.init, FunctionType.Attribute | FunctionType.Includes, "Set", baseName, "")); 2121 a.put(new FunctionNode( name, [], attrType, FunctionType.Attribute | FunctionType.Includes, "Get", baseName, "")); 2122 break; 2123 } 2124 if (context.isPartial) { 2125 auto name = tree.children[1].matches[0]; 2126 auto baseName = context.partial.children[0].children[0].matches[0]; 2127 auto attrType = tree.children[0]; 2128 auto attrArg = Argument(name, attrType); 2129 2130 if (!context.readonly) 2131 a.put(new FunctionNode( name, [attrArg], ParseTree.init, FunctionType.Attribute | FunctionType.Partial, "Set", baseName, "")); 2132 a.put(new FunctionNode( name, [], attrType, FunctionType.Attribute | FunctionType.Partial, "Get", baseName, "")); 2133 break; 2134 } 2135 auto name = tree.children[1].matches[0]; 2136 auto attrType = tree.children[0]; 2137 if (!context.readonly) 2138 a.put(new FunctionNode( name, [Argument(name, attrType)], ParseTree.init, FunctionType.Attribute, "Set", "", "")); 2139 a.put(new FunctionNode( name, [], attrType, FunctionType.Attribute, "Get", "", "")); 2140 break; 2141 case "WebIDL.ExtendedAttributeList": 2142 context.extendedAttributeList = tree; 2143 break; 2144 case "WebIDL.SpecialOperation": 2145 if (tree.children[1].children[1].children[0].matches[0] != "") { 2146 // context.customName = tree.children[0].matches[0]; 2147 tree.children[1].toIr(a, context); 2148 switch(tree.matches[0]) { 2149 case "getter": 2150 // (cast(FunctionNode)a.data[$-1]).type |= FunctionType.Getter; 2151 (cast(FunctionNode)a.data[$-1]).manglePostfix = tree.children[0].matches[0]; 2152 break; 2153 case "setter": 2154 // (cast(FunctionNode)a.data[$-1]).type |= FunctionType.Getter; 2155 (cast(FunctionNode)a.data[$-1]).manglePostfix = tree.children[0].matches[0]; 2156 break; 2157 default: break; 2158 } 2159 break; 2160 } 2161 auto rest = tree.children[1].children[1]; 2162 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(); 2163 auto result = tree.children[1].children[0]; 2164 switch(tree.matches[0]) { 2165 case "getter": 2166 assert(args.length == 1); 2167 a.put(new FunctionNode( "getter", args, result, FunctionType.OpIndex | FunctionType.Getter, "", "", "")); 2168 args = args.dup(); 2169 args[0].templated = true; 2170 a.put(new FunctionNode( "getter", args, result, FunctionType.OpDispatch | FunctionType.Getter, "", "" ,"")); 2171 break; 2172 case "setter": 2173 assert(args.length == 2); 2174 a.put(new FunctionNode( "setter", args, ParseTree.init, FunctionType.OpIndexAssign | FunctionType.Setter, "", "", "")); 2175 args = args.dup(); 2176 args[0].templated = true; 2177 a.put(new FunctionNode( "setter", args, ParseTree.init, FunctionType.OpDispatch | FunctionType.Setter, "", "", "")); 2178 break; 2179 case "deleter": 2180 a.put(new FunctionNode( "remove", args, ParseTree.init, FunctionType.Function | FunctionType.Deleter, "", "", "deleter")); 2181 break; 2182 default: assert(0); 2183 } 2184 break; 2185 case "WebIDL.RegularOperation": 2186 auto rest = tree.children[1]; 2187 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(); 2188 auto result = tree.children[0]; 2189 if (context.isIncludes) { 2190 auto name = tree.children[1].matches[0]; 2191 auto baseName = context.includes.children[0].matches[0]; 2192 createOptionalOverloads(new FunctionNode( name, args, result, FunctionType.Function | FunctionType.Includes, "", baseName, context.customName)).copy(a); 2193 break; 2194 } 2195 if (context.isPartial) { 2196 auto name = rest.children[0].matches[0]; 2197 auto baseName = context.partial.children[0].children[0].matches[0]; 2198 createOptionalOverloads(new FunctionNode( name, args, result, FunctionType.Function | FunctionType.Partial, "", baseName, context.customName)).copy(a); 2199 break; 2200 } 2201 auto name = rest.children[0].matches[0]; 2202 createOptionalOverloads(new FunctionNode( name, args, result, FunctionType.Function, "", "", context.customName)).copy(a); 2203 break; 2204 case "WebIDL.Enum": 2205 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)); 2206 break; 2207 case "WebIDL.Dictionary": 2208 ParseTree baseType; 2209 if (tree.children[1].children.length > 0 && tree.children[1].children[0].matches.length != 0) 2210 baseType = tree.children[1].children[0]; 2211 auto app = appender!(Node[]); 2212 Argument[] args; 2213 app.put(new FunctionNode("create", args, ParseTree.init, FunctionType.DictionaryConstructor, "", "", "")); 2214 tree.children[2].toIr(app, context); 2215 a.put(new StructNode(tree.children[0].matches[0], baseType, app.data)); 2216 break; 2217 case "WebIDL.DictionaryMember": 2218 context.extendedAttributeList = tree.children[0]; 2219 tree.children[1].toIr(a, context); 2220 break; 2221 case "WebIDL.MaplikeRest": 2222 a.put(new MaplikeNode(tree.children[0], tree.children[1])); 2223 break; 2224 case "WebIDL.DictionaryMemberRest": 2225 auto name = tree.children[1].matches[0]; 2226 auto paramType = tree.children[0]; 2227 a.put(new FunctionNode(name, [Argument(name, paramType)], ParseTree.init, FunctionType.Attribute, "Set", "", "")); 2228 a.put(new FunctionNode(name, [], paramType, FunctionType.Attribute, "Get", "", "")); 2229 break; 2230 case "WebIDL.Typedef": 2231 a.put(new TypedefNode(tree.children[1].matches[0], tree.children[0].generateDType(context), tree.children[0])); 2232 break; 2233 case "WebIDL.Partial": 2234 context.partial = tree; 2235 if (tree.children[0].children[0].name == "WebIDL.PartialInterfaceOrPartialMixin") { 2236 context.typeName = tree.children[0].children[0].children[0].children[0].matches[0]; 2237 auto baseType = context.getType(context.typeName); 2238 if (baseType.name == "WebIDL.MixinRest") 2239 return; 2240 } else if (tree.children[0].children[0].name == "WebIDL.PartialDictionary") 2241 context.typeName = tree.children[0].children[0].children[0].matches[0]; 2242 tree.children[0].toIr(a, context); 2243 break; 2244 case "WebIDL.PartialInterfaceRest": 2245 tree.children[1].toIr(a, context); 2246 break; 2247 case "WebIDL.CallbackRest": 2248 auto argList = tree.children[2]; 2249 auto args = zip(argList.extractArguments,argList.extractTypes,argList.extractDefaults,argList.extractArgumentRests).map!(a=>Argument(a[0],a[1],a[2],a[3])).array(); 2250 2251 a.put(new CallbackNode(tree.children[0].matches[0], tree.children[1], args)); 2252 break; 2253 default: 2254 tree.children.each!(c => toIr(c, a, context)); 2255 return; 2256 } 2257 } 2258 2259 auto collectFunctions(IR ir, string[] filter) { 2260 import std.algorithm : canFind; 2261 auto app = appender!(FunctionNode[]); 2262 void recurse(Node node, string parentName) { 2263 if (auto fun = cast(FunctionNode)node) { 2264 string name = mangleName(parentName.length > 0 ? parentName : fun.baseType, fun.name, fun.manglePostfix); 2265 if (filter.length == 0 || filter.canFind(name)) { 2266 app.put(fun); 2267 } 2268 } else if (auto structNode = cast(StructNode)node) { 2269 foreach(child; structNode.children) 2270 recurse(child, structNode.name); 2271 } else if (auto includesNode = cast(StructIncludesNode)node) { 2272 foreach(child; includesNode.children) 2273 recurse(child, includesNode.name); 2274 } else if (auto moduleNode = cast(ModuleNode)node) { 2275 foreach(child; moduleNode.children) 2276 recurse(child, ""); 2277 } 2278 } 2279 ir.nodes.each!(node => recurse(node, "")); 2280 return app.data; 2281 } 2282 auto collectCallbacks(alias pred)(IR ir) { 2283 import std.algorithm : canFind; 2284 auto app = appender!(CallbackNode[]); 2285 void recurse(Node node) { 2286 if (auto cb = cast(CallbackNode)node) { 2287 if (pred(cb)) { 2288 app.put(cb); 2289 } 2290 } else if (auto moduleNode = cast(ModuleNode)node) { 2291 foreach(child; moduleNode.children) 2292 recurse(child); 2293 } 2294 } 2295 ir.nodes.each!(node => recurse(node)); 2296 return app.data; 2297 } 2298 auto collectUsedCallbackNames(IR ir, FunctionNode[] funcs) { 2299 return funcs.map!(f => f.args).joiner.map!(a => a.type).filter!(t => ir.semantics.isCallback(t)).map!((c){ 2300 if (ir.semantics.isTypedef(c)) 2301 return ir.semantics.getAliasedType(c.getTypeName()); 2302 return c; 2303 }).map!(t => t.getTypeName()).array().sort.uniq; 2304 } 2305 void generateDecodedTypes(IR ir, ref Appender!(ParseTree[]) a, string[] filter) { 2306 auto semantics = ir.semantics; 2307 auto funcs = collectFunctions(ir, filter); 2308 auto callbackNames = collectUsedCallbackNames(ir, funcs); 2309 auto cbs = collectCallbacks!(c => callbackNames.canFind(c.name))(ir); 2310 foreach(fun; funcs) { 2311 foreach(arg; fun.args) { 2312 if (semantics.isNullableTypedef(arg.type)) { 2313 a.put(arg.type.stripNullable); 2314 } else if (semantics.isUnion(arg.type) || semantics.isEnum(arg.type)) { 2315 a.put(arg.type); 2316 } 2317 } 2318 } 2319 foreach(cb; cbs) { 2320 if (semantics.isUnion(cb.result) || semantics.isEnum(cb.result) || semantics.isAny(cb.result)) 2321 a.put(cb.result); 2322 } 2323 } 2324 void generateEncodedTypes(IR ir, ref Appender!(ParseTree[]) a, string[] filter) { 2325 auto semantics = ir.semantics; 2326 auto funcs = collectFunctions(ir, filter); 2327 foreach(fun; funcs) { 2328 if (fun.result != ParseTree.init && fun.result.matches[0] != "void") { 2329 if (semantics.isStringType(fun.result) || semantics.isUnion(fun.result) || semantics.isNullable(fun.result) || semantics.isEnum(fun.result)) { 2330 a.put(fun.result); 2331 } 2332 } 2333 foreach(arg; fun.args) { 2334 if (semantics.isCallback(arg.type.matches[0])){ 2335 auto argList = semantics.getArgumentList(arg.type); 2336 auto types = extractTypes(argList); 2337 foreach(type; types) { 2338 if (semantics.isStringType(type) || semantics.isUnion(type) 2339 || semantics.isNullable(type) || semantics.isEnum(type)) 2340 a.put(type); 2341 } 2342 } 2343 } 2344 } 2345 } 2346 2347 auto stripNullable(const ParseTree tree) { 2348 ParseTree clone = tree.dup(); 2349 if (clone.children.length == 0) 2350 return clone; 2351 switch (clone.name) { 2352 case "WebIDL.ExtendedAttributeList": return clone; 2353 case "WebIDL.CallbackRest": return clone; 2354 case "WebIDL.Dictionary": return clone; 2355 case "WebIDL.CallbackOrInterfaceOrMixin": return clone; 2356 case "WebIDL.PartialDefinition": return clone; 2357 default: 2358 } 2359 if (clone.children[$-1].name == "WebIDL.Null") { 2360 clone.children = clone.children.dup; 2361 clone.children[$-1].matches = [""]; 2362 clone.matches[$-1] = ""; 2363 return clone; 2364 } else if (clone.matches[$-1] == "?") { 2365 clone.matches = clone.matches.dup[0..$-1]; 2366 } 2367 clone.children = clone.children.map!(c => c.stripNullable).array; 2368 return clone; 2369 } 2370 2371 void generateJsDecoder(Decoder)(Decoder decoder, ref IndentedStringAppender a, ref Semantics semantics, bool isVar) { 2372 a.put("spasm_decode_"); 2373 a.put(decoder.mangled); 2374 if (isVar) { 2375 a.put(" = "); 2376 } else { 2377 a.put(": "); 2378 } 2379 if (semantics.isPrimitive(decoder.tree)) { 2380 switch (decoder.mangled) { 2381 case "uint": 2382 case "bool": 2383 a.put("getUInt"); 2384 return; 2385 case "double": 2386 a.put("getDouble"); 2387 return; 2388 default: 2389 } 2390 } 2391 if (decoder.mangled == "Handle" || decoder.mangled == "object" || decoder.mangled == "sequence" || decoder.mangled == "object" || decoder.mangled == "record") { 2392 a.put("decode_handle"); 2393 return; 2394 } 2395 a.putLn("(ptr)=>{"); 2396 a.indent(); 2397 // enum 2398 // optional!T 2399 // sumType!Ts 2400 // typedef to T 2401 if (semantics.isNullableTypedef(decoder.tree)) { 2402 string typeName = decoder.tree.getTypeName(); 2403 auto aliasedType = semantics.getAliasedType(typeName); 2404 uint structSize = semantics.getSizeOf(aliasedType); 2405 a.putLn(["if (getBool(ptr+", structSize.to!string, ")) {"]); 2406 a.indent(); 2407 auto typedefMangled = aliasedType.mangleTypeJs(semantics); 2408 a.putLn(["return spasm_decode_",typedefMangled,"(ptr);"]); 2409 a.undent(); 2410 a.putLn("}"); 2411 } else if (semantics.isTypedef(decoder.tree)) { 2412 string typeName = decoder.tree.getTypeName(); 2413 auto aliasedType = semantics.getAliasedType(typeName); 2414 auto typedefMangled = aliasedType.mangleTypeJs(semantics); 2415 a.putLn(["return spasm_decode_",typedefMangled,"(ptr);"]); 2416 } else if (semantics.isNullable(decoder.tree)) { 2417 auto baseType = decoder.tree.stripNullable; 2418 uint structSize = semantics.getSizeOf(baseType); 2419 a.putLn(["if (getBool(ptr+", structSize.to!string, ")) {"]); 2420 a.indent(); 2421 auto typedefMangled = baseType.mangleTypeJs(semantics); 2422 a.putLn(["return spasm_decode_",typedefMangled,"(ptr);"]); 2423 a.undent(); 2424 a.putLn("}"); 2425 } else if (semantics.isEnum(decoder.tree)) { 2426 string typeName = decoder.tree.getTypeName(); 2427 auto aliasedType = (typeName in semantics.types).tree; 2428 a.putLn(["const vals = [",aliasedType.children[1].children.map!(c => c.matches[0]).joiner(", ").text,"];"]); 2429 a.putLn("return vals[ptr];"); 2430 } else if (semantics.isUnion(decoder.tree)) { 2431 void outputChild(Child)(Child c, ref Semantics semantics) { 2432 a.putLn(["if (getUInt(ptr) == ", c.index.to!string, ") {"]); 2433 a.indent(); 2434 a.putLn(["return spasm_decode_",c.value.mangleTypeJs(semantics),"(ptr+4);"]); 2435 a.undent(); 2436 a.put("}"); 2437 } 2438 auto children = semantics.getUnionChildren(decoder.tree).enumerate; 2439 if (children.length > 0) { 2440 children[0..$-1].each!((c){ 2441 outputChild(c, semantics); 2442 a.put(" else "); 2443 }); 2444 outputChild(semantics.getUnionChildren(decoder.tree).enumerate[$-1], semantics); 2445 } 2446 a.putLn(""); 2447 } else if (semantics.isSequence(decoder.tree)) { 2448 a.putLn("// sequence"); 2449 assert(false, "not implemented"); 2450 } else if (semantics.isPrimitive(decoder.tree)) { 2451 switch (decoder.mangled) { 2452 case "ulong": 2453 a.putLn("return getUInt(ptr) + (getUInt(ptr+4) << 32);"); 2454 break; 2455 default: 2456 a.putLn("// primitive"); 2457 a.putLn(["// ", decoder.tree.name]); 2458 a.putLn(decoder.tree.toString); 2459 break; 2460 } 2461 assert(false, "not implemented"); 2462 } else { 2463 a.putLn(["// ", decoder.tree.name]); 2464 a.putLn("// other"); 2465 assert(false, "not implemented"); 2466 } 2467 // where T can be any of the above 2468 // and Ts two or more of the set including the above and the following: 2469 // - any primitive (double, bool, int; unsigned/signed; etc.) 2470 // - a JsHandle 2471 a.undent(); 2472 a.put("}"); 2473 } 2474 void generateJsEncoder(Encoder)(Encoder encoder, ref IndentedStringAppender a, ref Semantics semantics, bool isVar) { 2475 a.put("spasm_encode_"); 2476 a.put(encoder.mangled); 2477 if (isVar) { 2478 a.put(" = "); 2479 } else { 2480 a.put(": "); 2481 } 2482 if (semantics.isPrimitive(encoder.tree)) { 2483 switch (encoder.mangled) { 2484 case "uint": 2485 case "bool": 2486 a.put("setUInt"); 2487 return; 2488 case "double": 2489 a.put("setDouble"); 2490 return; 2491 default: 2492 } 2493 } 2494 if (encoder.mangled == "Handle" || encoder.mangled == "object" || encoder.mangled == "sequence" || encoder.mangled == "object" || encoder.mangled == "record") { 2495 a.put("encode_handle"); 2496 return; 2497 } 2498 a.putLn("(ptr, val)=>{"); 2499 a.indent(); 2500 // enum 2501 // optional!T 2502 // sumType!Ts 2503 // typedef to T 2504 if (semantics.isNullableTypedef(encoder.tree)) { 2505 string typeName = encoder.tree.getTypeName(); 2506 auto aliasedType = semantics.getAliasedType(typeName); 2507 uint structSize = semantics.getSizeOf(aliasedType); 2508 a.putLn(["if (setBool(ptr+", structSize.to!string, ", isDefined(val))) {"]); 2509 a.indent(); 2510 auto typedefMangled = aliasedType.mangleTypeJs(semantics); 2511 a.putLn(["spasm_encode_",typedefMangled,"(ptr, val);"]); 2512 a.undent(); 2513 a.putLn("}"); 2514 } else if (semantics.isTypedef(encoder.tree)) { 2515 string typeName = encoder.tree.getTypeName(); 2516 auto aliasedType = semantics.getAliasedType(typeName); 2517 auto typedefMangled = aliasedType.mangleTypeJs(semantics); 2518 a.putLn(["spasm_encode_",typedefMangled,"(ptr, val);"]); 2519 } else if (semantics.isNullable(encoder.tree)) { 2520 auto baseType = encoder.tree.stripNullable; 2521 uint structSize = semantics.getSizeOf(baseType); 2522 a.putLn(["if (setBool(ptr+", structSize.to!string, ", isDefined(val))) {"]); 2523 a.indent(); 2524 auto typedefMangled = baseType.mangleTypeJs(semantics); 2525 a.putLn(["spasm_encode_",typedefMangled,"(ptr, val);"]); 2526 a.undent(); 2527 a.putLn("}"); 2528 } else if (semantics.isEnum(encoder.tree)) { 2529 string typeName = encoder.tree.getTypeName(); 2530 auto aliasedType = (typeName in semantics.types).tree; 2531 a.putLn(["const vals = [",aliasedType.children[1].children.map!(c => c.matches[0]).joiner(", ").text,"];"]); 2532 a.putLn("setInt(ptr, vals.indexOf(val))"); 2533 } else if (semantics.isUnion(encoder.tree)) { 2534 void outputChild(Child)(Child c, ref Semantics semantics) { 2535 a.putLn(["if (val instanceof ",c.value.getTypeName,") {"]); 2536 a.indent(); 2537 a.putLn(["setUInt(ptr, ",c.index.to!string ,");"]); 2538 a.putLn(["spasm_encode_",c.value.mangleTypeJs(semantics),"(ptr+4, val);"]); 2539 a.undent(); 2540 a.put("}"); 2541 } 2542 auto children = semantics.getUnionChildren(encoder.tree).enumerate; 2543 if (children.length > 0) { 2544 children[0..$-1].each!((c){ 2545 outputChild(c, semantics); 2546 a.put(" else "); 2547 }); 2548 outputChild(semantics.getUnionChildren(encoder.tree).enumerate[$-1], semantics); 2549 } 2550 a.putLn(""); 2551 } else if (semantics.isSequence(encoder.tree)) { 2552 a.putLn("// sequence"); 2553 assert(false, "not implemented"); 2554 } else if (semantics.isPrimitive(encoder.tree)) { 2555 switch (encoder.mangled) { 2556 case "ulong": 2557 a.putLn("setUInt(ptr, val & 0xffffffff);"); 2558 a.putLn("setUInt(ptr+4, val >> 32);"); 2559 break; 2560 default: 2561 a.putLn("// primitive"); 2562 a.putLn(["// ", encoder.tree.name]); 2563 a.putLn(encoder.tree.toString); 2564 break; 2565 } 2566 assert(false, "not implemented"); 2567 } else { 2568 a.putLn(["// ", encoder.tree.name]); 2569 a.putLn("// other"); 2570 assert(false, "not implemented"); 2571 } 2572 // where T can be any of the above 2573 // and Ts two or more of the set including the above and the following: 2574 // - any primitive (double, bool, int; unsigned/signed; etc.) 2575 // - a JsHandle 2576 a.undent(); 2577 a.put("}"); 2578 } 2579 2580 2581 void iterate(alias fun, Appender, Args...)(ref Module module_, ref Appender app, Args args) { 2582 foreach (key; module_.types.keys.array.sort) { 2583 auto type = module_.types[key]; 2584 static if (Args.length > 0) 2585 args[0].extendedAttributeList = type.attributes; 2586 fun(type.tree, app, args); 2587 } 2588 foreach (namespace; module_.namespaces.dup.schwartzSort!(i => i.tree.name)) { 2589 fun(namespace.tree, app, args); 2590 } 2591 foreach (partialType; module_.partials.dup.schwartzSort!(i => i.tree.name)) { 2592 static if (Args.length > 0) 2593 args[0].extendedAttributeList = partialType.attributes; 2594 fun(partialType.tree, app, args); 2595 } 2596 foreach (mixinType; module_.mixins.dup.schwartzSort!(i => i.tree.name)) { 2597 fun(mixinType.tree, app, args); 2598 } 2599 } 2600 2601 ParseTree[] getUnionChildren(ref Semantics semantics, ParseTree tree) { 2602 if (tree.name == "WebIDL.ReturnType") 2603 return semantics.getUnionChildren(tree.children[0].children[0]); 2604 if (tree.name == "WebIDL.TypeWithExtendedAttributes") 2605 return semantics.getUnionChildren(tree.children[1].children[0]); 2606 if (tree.name == "WebIDL.Type") { 2607 assert(tree.children[0].name == "WebIDL.UnionType"); 2608 return semantics.getUnionChildren(tree.children[0]); 2609 } 2610 assert(tree.name == "WebIDL.UnionType"); 2611 2612 // check each child's first child, if that is a UnionType (or via Typedef), we need to concat types 2613 auto children = appender!(ParseTree[]); 2614 foreach (child; tree.children) { 2615 auto member = child.children[0]; 2616 if (member.name == "WebIDL.UnionType") { 2617 semantics.getUnionChildren(member).copy(children); 2618 } else if (semantics.isTypedef(child)) { 2619 string typeName = child.getTypeName(); 2620 auto aliasedType = semantics.getAliasedType(typeName); 2621 if (semantics.isUnion(aliasedType)) { 2622 semantics.getUnionChildren(aliasedType).copy(children); 2623 } else 2624 children.put(child); 2625 } else 2626 children.put(child); 2627 } 2628 return children.data; 2629 } 2630 struct TypeEncoder { 2631 string mangled; 2632 ParseTree tree; 2633 bool external; 2634 } 2635 struct TypeDecoder { 2636 string mangled; 2637 ParseTree tree; 2638 } 2639 TypeDecoder[] generateDecodedTypes(IR ir, string[] filter) { 2640 auto semantics = ir.semantics; 2641 auto app = appender!(ParseTree[]); 2642 ir.generateDecodedTypes(app, filter); 2643 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; 2644 2645 auto decoders = appender!(TypeDecoder[]); 2646 decodedTypes.copy(decoders); 2647 2648 ulong start = 0, end = decoders.data.length; 2649 while (start != end) { 2650 foreach(decoder; decoders.data[start..end].dup) { 2651 if (semantics.isNullableTypedef(decoder.tree)) { 2652 string typeName = decoder.tree.getTypeName(); 2653 auto aliasedType = semantics.getAliasedType(typeName); 2654 auto typedefMangled = aliasedType.mangleTypeJs(semantics); 2655 decoders.put(TypeDecoder(typedefMangled, aliasedType)); 2656 } else if (semantics.isTypedef(decoder.tree)) { 2657 string typeName = decoder.tree.getTypeName(); 2658 auto aliasedType = semantics.getAliasedType(typeName); 2659 auto typedefMangled = aliasedType.mangleTypeJs(semantics); 2660 decoders.put(TypeDecoder(typedefMangled, aliasedType)); 2661 } else if (semantics.isNullable(decoder.tree)) { 2662 ParseTree clone = decoder.tree; 2663 auto baseType = clone.stripNullable; 2664 auto typedefMangled = baseType.mangleTypeJs(semantics); 2665 decoders.put(TypeDecoder(typedefMangled, baseType)); 2666 } else if (semantics.isUnion(decoder.tree)) { 2667 foreach (child; semantics.getUnionChildren(decoder.tree)) { 2668 auto typedefMangled = child.mangleTypeJs(semantics); 2669 decoders.put(TypeDecoder(typedefMangled, child)); 2670 } 2671 } 2672 } 2673 start = end; 2674 end = decoders.data.length; 2675 } 2676 return decoders.data; 2677 } 2678 2679 TypeEncoder[] generateEncodedTypes(IR ir, string[] filter) { 2680 auto semantics = ir.semantics; 2681 auto app = appender!(ParseTree[]); 2682 ir.generateEncodedTypes(app, filter); 2683 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;}); 2684 2685 auto encoders = appender!(TypeEncoder[]); 2686 encodedTypes.copy(encoders); 2687 2688 ulong start = 0, end = encoders.data.length; 2689 while (start != end) { 2690 foreach(encoder; encoders.data[start..end].dup) { 2691 if (semantics.isNullableTypedef(encoder.tree)) { 2692 string typeName = encoder.tree.getTypeName(); 2693 auto aliasedType = semantics.getAliasedType(typeName); 2694 auto typedefMangled = aliasedType.mangleTypeJs(semantics); 2695 encoders.put(TypeEncoder(typedefMangled, aliasedType, false)); 2696 } else if (semantics.isTypedef(encoder.tree)) { 2697 string typeName = encoder.tree.getTypeName(); 2698 auto aliasedType = semantics.getAliasedType(typeName); 2699 auto typedefMangled = aliasedType.mangleTypeJs(semantics); 2700 encoders.put(TypeEncoder(typedefMangled, aliasedType, false)); 2701 } else if (semantics.isNullable(encoder.tree)) { 2702 ParseTree clone = encoder.tree; 2703 auto baseType = clone.stripNullable; 2704 auto typedefMangled = baseType.mangleTypeJs(semantics); 2705 encoders.put(TypeEncoder(typedefMangled, baseType, false)); 2706 } else if (semantics.isUnion(encoder.tree)) { 2707 foreach (child; semantics.getUnionChildren(encoder.tree)) { 2708 auto typedefMangled = child.mangleTypeJs(semantics); 2709 encoders.put(TypeEncoder(typedefMangled, child, false)); 2710 } 2711 } 2712 } 2713 start = end; 2714 end = encoders.data.length; 2715 } 2716 return encoders.data; 2717 } 2718 2719 auto getImports(IR ir, Module module_) { 2720 import std.format : format; 2721 import std.typecons : tuple, Tuple; 2722 alias Item = Tuple!(Type,"type",string,"name"); 2723 auto app = appender!(Item[]); 2724 auto semantics = ir.semantics; 2725 void extractTypes(alias sink)(Semantics semantics, ParseTree tree, Appender!(Item[]) app) { 2726 if (tree.name == "WebIDL.Typedef") { 2727 sink(tree.children[1].matches[0], semantics, app); 2728 } else if (tree.name == "WebIDL.NonAnyType" && tree.children[0].name == "WebIDL.Identifier") { 2729 sink(tree.matches[0], semantics, app); 2730 } else if (tree.name == "WebIDL.Inheritance") { 2731 if (tree.children.length > 0) { 2732 sink(tree.children[0].matches[0], semantics, app); 2733 } 2734 } 2735 else 2736 tree.children.each!(c => extractTypes!(sink)(semantics, c, app)); 2737 } 2738 static void sink(string name, Semantics semantics, Appender!(Item[]) app) { 2739 if (name == "void") 2740 return; 2741 if (auto p = name in semantics.types) { 2742 if (semantics.isTypedef(name)) { 2743 auto baseType = semantics.getAliasedType(name); 2744 auto str = generateDType(baseType, Context(semantics)); 2745 if (str.length < name.length) { 2746 if (auto q = str in semantics.types) { 2747 app.put(tuple!("type", "name")(*q, str)); 2748 return; 2749 } 2750 } 2751 } 2752 app.put(tuple!("type","name")(*p, name)); 2753 } else { 2754 writeln("Cannot find ", name); 2755 } 2756 } 2757 void recurse(Node node) { 2758 if (cast(FunctionNode)node) { 2759 extractTypes!sink(semantics, (cast(FunctionNode)node).result, app); 2760 (cast(FunctionNode)node).args.each!(arg => extractTypes!sink(semantics, arg.type, app)); 2761 } else if (cast(TypedefNode)node) { 2762 extractTypes!sink(semantics, (cast(TypedefNode)node).rhs, app); 2763 } else if (cast(CallbackNode)node) { 2764 extractTypes!sink(semantics, (cast(CallbackNode)node).result, app); 2765 (cast(CallbackNode)node).args.each!(arg => extractTypes!sink(semantics, arg.type, app)); 2766 } else if (cast(StructNode)node) { 2767 auto baseType = (cast(StructNode)node).baseType; 2768 if (baseType != ParseTree.init && baseType.matches[0].length > 0) 2769 sink(baseType.matches[0], semantics, app); 2770 (cast(StructNode)node).children.each!(node => recurse(node)); 2771 } else if (cast(StructIncludesNode)node) { 2772 auto name = (cast(StructIncludesNode)node).name; 2773 sink(name, semantics, app); 2774 (cast(StructIncludesNode)node).children.each!(node => recurse(node)); 2775 } else if (cast(ModuleNode)node) 2776 (cast (ModuleNode)node).children.each!(node => recurse(node)); 2777 else if (cast(MixinNode)node) { 2778 (cast(MixinNode)node).children.each!(node => recurse(node)); 2779 } else if (auto cons = cast(ExposedConstructorNode)node) { 2780 sink(cons.name, semantics, app); 2781 } 2782 } 2783 ir.nodes.filter!(n => n.module_ is module_).each!((node){ 2784 node.children.each!(c => recurse(c)); 2785 }); 2786 return app.data.filter!(t => t.type.module_ !is module_).map!(t => t.type.module_.name).map!(n => format("import spasm.bindings.%s;",n)).array.sort.uniq.array; 2787 // 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 spasm.bindings.%s : %s;", t.type.module_.name,t.name)).array; 2788 } 2789 2790 class IR { 2791 ModuleNode[] nodes; 2792 StructNode[string] structs; 2793 Semantics semantics; 2794 this(ModuleNode[] nodes, Semantics semantics) { 2795 this.nodes = nodes; 2796 this.semantics = semantics; 2797 nodes.each!(mod => mod.children.map!(n => cast(StructNode)n).filter!(n => n !is null).each!((n){ 2798 structs[n.name] = n; 2799 })); 2800 this.resolvePartialsAndIncludes(); 2801 this.mangleJsOverloads(semantics); 2802 this.moveExposedConstructors(); 2803 this.collectMethods(); 2804 } 2805 } 2806 2807 auto collectMethods(IR ir) { 2808 ir.structs.values.each!((s){ 2809 foreach(child; s.children) { 2810 if (auto constructor = cast(ExposedConstructorNode)child) { 2811 s.functions ~= constructor.name; 2812 } else if (auto includes = cast(StructIncludesNode)child) { 2813 foreach(includesChild; includes.children) { 2814 if (auto func = cast(FunctionNode)includesChild) { 2815 s.functions ~= func.name; 2816 } 2817 } 2818 } else if (auto func = cast(FunctionNode)child) { 2819 s.functions ~= func.name; 2820 } 2821 } 2822 }); 2823 } 2824 template skipType(T) { 2825 auto skipType(Range)(auto ref Range range) { 2826 return range.filter!(i => (cast(T)i) is null); 2827 } 2828 } 2829 template mapType(T) { 2830 auto mapType(Range)(auto ref Range range) { 2831 return range.map!(n => cast(T)n).filter!(n => n !is null); 2832 } 2833 } 2834 auto resolvePartialsAndIncludes(IR ir) { 2835 ir.nodes.each!(mod => mod.children.skipType!(ExposedConstructorNode).mapType!(FunctionNode).filter!(n => n.baseType.length > 0).each!((n){ 2836 if (auto p = n.baseType in ir.structs) 2837 p.children ~= n; 2838 else 2839 writeln("Error: Type ", n.baseType, " is unknown: "); 2840 })); 2841 ir.nodes.each!(mod => mod.children.mapType!(StructIncludesNode).each!((n){ 2842 if (auto p = n.baseType in ir.structs) { 2843 p.children ~= n; 2844 } 2845 else 2846 writeln("Error: Type ", n.baseType, " is unknown"); 2847 })); 2848 } 2849 auto moveExposedConstructors(IR ir) { 2850 foreach(mod; ir.nodes) { 2851 mod.children.mapType!(ExposedConstructorNode).each!((constructor){ 2852 if (auto p = constructor.baseType in ir.structs) { 2853 p.children ~= constructor; 2854 } else 2855 writeln("Error: cannot find type ", constructor.baseType," to exposed constructor ",constructor.name, " on."); 2856 }); 2857 } 2858 foreach(mod; ir.nodes) { 2859 mod.children = std.algorithm.remove!(n => cast(ExposedConstructorNode)n)(mod.children); 2860 } 2861 } 2862 auto mangleJsOverloads(IR ir, Semantics semantics) { 2863 void handleSet(Node[] nodes) { 2864 auto funcs = nodes.mapType!(FunctionNode).array; 2865 nodes.mapType!(StructIncludesNode).each!(i => handleSet(i.children)); 2866 auto overloadGroups = funcs.schwartzSort!((a){ 2867 if (a.customName.length > 0) 2868 return a.customName ~ a.manglePostfix; 2869 return a.name ~ a.manglePostfix; 2870 }).groupBy.map!(g => g.array).filter!(g => g.length > 1); 2871 foreach(group; overloadGroups) { 2872 foreach(fun; group) { 2873 fun.manglePostfix ~= "_" ~ fun.args.map!(arg => arg.type.mangleTypeJs(semantics)).joiner("_").text; 2874 } 2875 } 2876 } 2877 import std.algorithm : schwartzSort; 2878 foreach(item; ir.structs.byValue) { 2879 handleSet(item.children); 2880 } 2881 foreach(item; ir.nodes.map!(n => n.children).joiner) { 2882 auto mixinNode = cast(MixinNode)item; 2883 if (mixinNode) 2884 handleSet(mixinNode.children); 2885 } 2886 } 2887 string generateDBindings(IR ir, Module module_) { 2888 auto app = IndentedStringAppender(); 2889 auto context = Context(module_.semantics); 2890 ir.nodes.filter!(mod => mod.module_ is module_).each!(n => n.toDBinding(module_.semantics, &app)); 2891 return app.data; 2892 } 2893 2894 string generateDImports(IR ir, Module module_) { 2895 auto app = IndentedStringAppender(); 2896 ir.nodes.filter!(mod => mod.module_ is module_).each!(n => n.toDImport(module_.semantics, &app)); 2897 if (app.data.length > 0) 2898 return app.data[0 .. $ - 1]; // remove last newline 2899 return app.data; 2900 } 2901 2902 string generateSingleJsBinding(IR ir, string[] filtered = []) { 2903 import std.algorithm : map, filter, joiner, each, sort, uniq, cmp; 2904 import std.array : array; 2905 import std.conv : text; 2906 import std.typecons : tuple; 2907 auto semantics = ir.semantics; 2908 auto app = IndentedStringAppender(); 2909 app.putLn("// File is autogenerated with `dub spasm:webidl -- --bindgen`"); 2910 app.putLn("import {spasm as spa, encoders as encoder, decoders as decoder} from '../modules/spasm.js';"); 2911 app.putLn("let spasm = spa;"); 2912 app.putLn("let memory = {};"); 2913 app.putLn("const objects = spasm.objects;"); 2914 app.putLn("const addObject = spasm.addObject;"); 2915 app.putLn("const setupMemory = () => {"); 2916 app.putLn(" let buffer = spasm.memory.buffer;"); 2917 app.putLn(" if (memory.buffer == buffer)"); 2918 app.putLn(" return;"); 2919 app.putLn(" memory.buffer = buffer;"); 2920 app.putLn(" memory.heapi32s = new Int32Array(buffer)"); 2921 app.putLn(" memory.heapi32u = new Uint32Array(buffer)"); 2922 app.putLn(" memory.heapi16s = new Int16Array(buffer)"); 2923 app.putLn(" memory.heapi16u = new Uint16Array(buffer)"); 2924 app.putLn(" memory.heapi8s = new Int8Array(buffer)"); 2925 app.putLn(" memory.heapi8u = new Uint8Array(buffer)"); 2926 app.putLn(" memory.heapf32 = new Float32Array(buffer)"); 2927 app.putLn(" memory.heapf64 = new Float64Array(buffer)"); 2928 app.putLn("}"); 2929 2930 auto decodedTypes = ir.generateDecodedTypes(filtered).sort!((a,b){return a.mangled < b.mangled;}).uniq!((a, b){return a.mangled == b.mangled;}); 2931 auto encodedTypes = ir.generateEncodedTypes(filtered).sort!((a,b){return a.mangled < b.mangled;}).uniq!((a, b){return a.mangled == b.mangled;}); 2932 app.putLn("const setBool = (ptr, val) => (memory.heapi32u[ptr/4] = +val),"); 2933 app.putLn(" setInt = (ptr, val) => (memory.heapi32s[ptr/4] = val),"); 2934 app.putLn(" setUInt = (ptr, val) => (memory.heapi32u[ptr/4] = val),"); 2935 app.putLn(" setShort = (ptr, val) => (memory.heapi16s[ptr/2] = val),"); 2936 app.putLn(" setUShort = (ptr, val) => (memory.heapi16u[ptr/2] = val),"); 2937 app.putLn(" setByte = (ptr, val) => (memory.heapi8s[ptr] = val),"); 2938 app.putLn(" setUByte = (ptr, val) => (memory.heapi8u[ptr] = val),"); 2939 app.putLn(" setFloat = (ptr, val) => (memory.heapf32[ptr/4] = val),"); 2940 app.putLn(" setDouble = (ptr, val) => (memory.heapf64[ptr/8] = val),"); 2941 app.putLn(" getBool = (ptr) => memory.heapi32u[ptr/4],"); 2942 app.putLn(" getInt = (ptr) => memory.heapi32s[ptr/4],"); 2943 app.putLn(" getUInt = (ptr) => memory.heapi32u[ptr/4],"); 2944 app.putLn(" getShort = (ptr) => memory.heapi16s[ptr/2],"); 2945 app.putLn(" getUShort = (ptr) => memory.heapi16u[ptr/2],"); 2946 app.putLn(" getByte = (ptr) => memory.heapi8s[ptr],"); 2947 app.putLn(" getUByte = (ptr) => memory.heapi8u[ptr],"); 2948 app.putLn(" getFloat = (ptr) => memory.heapf32[ptr/4],"); 2949 app.putLn(" getDouble = (ptr) => memory.heapf64[ptr/8],"); 2950 app.putLn(" isDefined = (val) => (val != undefined && val != null),"); 2951 app.putLn(" encode_handle = (ptr, val) => { setUInt(ptr, addObject(val)); },"); 2952 app.putLn(" decode_handle = (ptr) => { return objects[getUInt(ptr)]; },"); 2953 app.putLn(" spasm_encode_string = encoder.string,"); 2954 app.putLn(" spasm_decode_string = decoder.string,"); 2955 app.put(" spasm_indirect_function_get = (ptr)=>spasm.instance.exports.__indirect_function_table.get(ptr)"); 2956 app.indent(); 2957 foreach(encoder; encodedTypes.filter!(e => e.mangled != "string")) { 2958 app.putLn(","); 2959 encoder.generateJsEncoder(app, semantics, true); 2960 } 2961 foreach(decoder; decodedTypes.filter!(e => e.mangled != "string")) { 2962 app.putLn(","); 2963 decoder.generateJsDecoder(app, semantics, true); 2964 } 2965 app.putLn(";"); 2966 app.undent(); 2967 app.putLn("export let jsExports = {"); 2968 app.indent(); 2969 app.putLn("env: {"); 2970 app.indent(); 2971 2972 auto pos = app.data.length; 2973 ir.nodes.each!(n => n.toJsExport(semantics, filtered, &app)); 2974 ir.generateJsGlobalBindings(filtered, app); 2975 2976 app.undent(); 2977 app.putLn("}"); 2978 app.undent(); 2979 app.put("}"); 2980 return app.data; 2981 } 2982 2983 class Type { 2984 ParseTree tree; 2985 ParseTree attributes; 2986 Module module_; 2987 this(ParseTree t, ParseTree a, Module m) { 2988 tree = t; 2989 attributes = a; 2990 module_ = m; 2991 } 2992 } 2993 2994 class Module { 2995 string name; 2996 Type[string] types; 2997 Type[] partials; 2998 Type[] mixins; 2999 Type[] namespaces; 3000 Semantics semantics; 3001 this(string n, Semantics semantics) { 3002 this.name = n; 3003 this.semantics = semantics; 3004 } 3005 } 3006 3007 class Semantics { 3008 Type[string] types; 3009 Type[] partials; 3010 Type[] mixins; 3011 Type[] namespaces; 3012 Module[string] modules; 3013 Module analyse(string module_, ParseTree tree) { 3014 import std.range : chunks; 3015 assert(tree.name == "WebIDL"); 3016 auto m = new Module(module_, this); 3017 foreach(chunk; tree.children[0].children.chunks(2)) { 3018 assert(chunk.length == 2); 3019 analyse(m, chunk[1], chunk[0]); 3020 } 3021 modules[module_] = m; 3022 foreach (n; m.namespaces) 3023 namespaces ~= n; 3024 foreach(p; m.partials) 3025 partials ~= p; 3026 foreach(mix; m.mixins) 3027 mixins ~= mix; 3028 foreach(k,v; m.types) 3029 types[k] = v; 3030 return m; 3031 } 3032 void dumpTypes() { 3033 import std.format; 3034 writefln("%(%s\n%)",types.keys.map!((key){return format("%s.%s", types[key].module_.name, key);}).array.sort); 3035 } 3036 private void analyse(Module module_, ParseTree tree, ParseTree attributes) { 3037 switch (tree.name) { 3038 case "WebIDL.IncludesStatement": 3039 module_.mixins ~= new Type(tree, ParseTree.init, module_); 3040 break; 3041 case "WebIDL.Dictionary": 3042 case "WebIDL.InterfaceRest": 3043 case "WebIDL.Enum": 3044 case "WebIDL.CallbackRest": 3045 case "WebIDL.MixinRest": 3046 string name = tree.children[0].matches[0]; 3047 if (auto p = name in types) { 3048 writefln("Warning: duplicated entry for %s", name); 3049 writefln("A in %s: %s",(*p).module_.name,(*p).tree.input[(*p).tree.begin .. (*p).tree.end]); 3050 writefln("B in %s: %s",module_.name,tree.input[tree.begin .. tree.end]); 3051 } 3052 module_.types[name] = new Type(tree, attributes, module_); 3053 break; 3054 case "WebIDL.Partial": 3055 module_.partials ~= new Type(tree, attributes, module_); 3056 break; 3057 case "WebIDL.Typedef": 3058 string name = tree.children[1].matches[0]; 3059 module_.types[name] = new Type(tree, attributes, module_); 3060 break; 3061 case "WebIDL.Namespace": 3062 module_.namespaces ~= new Type(tree, attributes, module_); 3063 break; 3064 default: 3065 tree.children.each!(c => analyse(module_, c, attributes)); 3066 } 3067 } 3068 } 3069 string generateDType(ParseTree tree, Context context) { 3070 auto app = IndentedStringAppender(); 3071 tree.generateDType(app, context); 3072 return app.data; 3073 } 3074 void generateDType(Appender)(ParseTree tree, ref Appender a, Context context) { 3075 switch (tree.name) { 3076 case "WebIDL.InterfaceRest": 3077 case "WebIDL.IncludesStatement": 3078 case "WebIDL.Iterable": 3079 case "WebIDL.MixinRest": 3080 case "WebIDL.Const": 3081 case "WebIDL.InterfaceMember": 3082 case "WebIDL.ReadOnlyMember": 3083 case "WebIDL.MixinMember": 3084 case "WebIDL.AttributeRest": 3085 case "WebIDL.ExtendedAttributeList": 3086 case "WebIDL.SpecialOperation": 3087 case "WebIDL.RegularOperation": 3088 case "WebIDL.ArgumentList": 3089 case "WebIDL.ArgumentName": 3090 case "WebIDL.ArgumentRest": 3091 case "WebIDL.Enum": 3092 case "WebIDL.Dictionary": 3093 case "WebIDL.DictionaryMember": 3094 case "WebIDL.SetlikeRest": 3095 case "WebIDL.MaplikeRest": 3096 case "WebIDL.DictionaryMemberRest": 3097 case "WebIDL.Typedef": 3098 case "WebIDL.Partial": 3099 case "WebIDL.PartialInterfaceRest": 3100 case "WebIDL.CallbackRest": 3101 break; 3102 case "WebIDL.SequenceType": 3103 if (tree.children[$-1].matches[0] == "?") 3104 a.put("Optional!(Sequence!("); 3105 else 3106 a.put("Sequence!("); 3107 tree.children[0].generateDType(a, context); 3108 a.put(")"); 3109 if (tree.children[$-1].matches[0] == "?") 3110 a.put(")"); 3111 break; 3112 case "WebIDL.TypeWithExtendedAttributes": 3113 context.extendedAttributeList = tree.children[0]; 3114 tree.children[1].generateDType(a, context); 3115 break; 3116 case "WebIDL.StringType": 3117 a.put("string"); 3118 break; 3119 case "WebIDL.SingleType": 3120 if (tree.matches[0] == "any") 3121 a.put("Any"); 3122 else 3123 tree.children[0].generateDType(a, context); 3124 break; 3125 case "WebIDL.NonAnyType": 3126 bool optional = !context.skipOptional && (context.optional || tree.children[$-1].name == "WebIDL.Null" && tree.children[$-1].matches[0] == "?"); 3127 if (optional) { 3128 a.put("Optional!("); 3129 } 3130 switch (tree.matches[0]) { 3131 case "object": 3132 a.put("JsObject"); 3133 break; 3134 case "symbol": 3135 a.put("Symbol"); 3136 break; 3137 case "Error": 3138 a.put("Error"); 3139 break; 3140 case "FrozenArray": 3141 a.put("FrozenArray!("); 3142 tree.children[$-2].generateDType(a, context); 3143 a.put(")"); 3144 break; 3145 default: 3146 tree.children.each!(c => c.generateDType(a, context)); 3147 } 3148 if (optional) { 3149 a.put(")"); 3150 } 3151 break; 3152 case "WebIDL.PrimitiveType": 3153 if (tree.children.length == 0) { 3154 switch (tree.matches[0]) { 3155 case "byte": a.put("byte"); break; 3156 case "octet": a.put("ubyte"); break; 3157 case "boolean": a.put("bool"); break; 3158 default: a.put(tree.matches[0]); break; 3159 } 3160 } else 3161 tree.children[0].generateDType(a, context); 3162 break; 3163 case "WebIDL.UnrestrictedFloatType": 3164 // TODO: handle unrestricted 3165 tree.children[0].generateDType(a, context); 3166 break; 3167 case "WebIDL.UnsignedIntegerType": 3168 if (tree.matches[0] == "unsigned") 3169 a.put("u"); 3170 tree.children[0].generateDType(a, context); 3171 break; 3172 case "WebIDL.IntegerType": 3173 if (tree.matches[0] == "long") { 3174 if (tree.matches.length > 1) 3175 a.put(is32Bit ? "int" : "long"); 3176 else 3177 a.put("int"); 3178 } else 3179 a.put(tree.matches[0]); 3180 break; 3181 case "WebIDL.FloatType": 3182 case "WebIDL.Identifier": 3183 string typeName = tree.matches[0]; 3184 if (context.isTypedef(typeName)) { 3185 auto aliasedType = context.getAliasedType(typeName); 3186 IndentedStringAppender app; 3187 aliasedType.generateDType(app, context); 3188 if (app.data.length < tree.matches[0].length) 3189 return a.put(app.data); 3190 } 3191 if (context.locals.canFind(tree.matches[0])) 3192 a.put("."); 3193 a.put(tree.matches[0]); 3194 break; 3195 case "WebIDL.ReturnType": 3196 if (tree.children.length > 0) 3197 tree.children[0].generateDType(a, context); 3198 else 3199 a.put(tree.matches[0]); 3200 break; 3201 case "WebIDL.Default": 3202 if (tree.children.length == 0) 3203 return; 3204 a.put("/* = "); 3205 tree.children[0].generateDType(a, context); 3206 a.put(" */"); 3207 break; 3208 case "WebIDL.DefaultValue": 3209 if (tree.children.length == 0) { 3210 a.put("[]"); 3211 return; 3212 } 3213 tree.children[0].generateDType(a, context); 3214 break; 3215 case "WebIDL.ConstValue": 3216 if (tree.children.length == 0) { 3217 a.put(tree.matches); 3218 break; 3219 } 3220 tree.children[0].generateDType(a, context); 3221 break; 3222 case "WebIDL.BooleanLiteral": 3223 case "WebIDL.Integer": 3224 a.put(tree.matches); 3225 break; 3226 case "WebIDL.Float": 3227 a.put(tree.matches[0]); 3228 break; 3229 case "WebIDL.FloatLiteral": 3230 if (tree.children.length == 0) { 3231 switch (tree.matches[0]) { 3232 case "-Infinity": a.put("-float.infinity"); break; 3233 case "Infinity": a.put("float.infinity"); break; 3234 case "NaN": a.put("float.nan"); break; 3235 default: assert(false); 3236 } 3237 break; 3238 } 3239 tree.children[0].generateDType(a, context); 3240 break; 3241 case "WebIDL.String": 3242 a.put(tree.matches); 3243 break; 3244 case "WebIDL.RecordType": 3245 a.put("Record!("); 3246 tree.children.putWithDelimiter!(generateDType)(", ", a, context); 3247 a.put(")"); 3248 break; 3249 case "WebIDL.PromiseType": 3250 a.put("Promise!("); 3251 tree.children[0].generateDType(a, context); 3252 a.put(")"); 3253 break; 3254 case "WebIDL.Type": 3255 context.optional = false; 3256 if (tree.children[0].name == "WebIDL.UnionType") { 3257 bool optional = !context.skipOptional && tree.children[1].matches[0] == "?"; 3258 context.optional = optional; 3259 if (optional) { 3260 a.put("Optional!("); 3261 } 3262 a.put("SumType!("); 3263 tree.children[0].children.putWithDelimiter!(generateDType)(", ", a, context); 3264 if (optional) 3265 a.put(")"); 3266 a.put(")"); 3267 break; 3268 } else 3269 tree.children[0].generateDType(a, context); 3270 break; 3271 case "WebIDL.UnionMemberType": 3272 context.optional = false; 3273 if (tree.children[0].name == "WebIDL.UnionType") { 3274 bool optional = !context.skipOptional && tree.matches[$-1] == "?"; 3275 context.optional = optional; 3276 if (optional) { 3277 a.put("Optional!("); 3278 } 3279 a.put("SumType!("); 3280 tree.children[0].children.putWithDelimiter!(generateDType)(", ", a, context); 3281 if (optional) 3282 a.put(")"); 3283 a.put(")"); 3284 break; 3285 } else 3286 tree.children.each!(c => c.generateDType(a, context)); 3287 break; 3288 default: 3289 tree.children.each!(c => generateDType(c, a, context)); 3290 } 3291 }