1 module webidl.binding.test; 2 3 import webidl.binding.generator; 4 import std.stdio; 5 import webidl.grammar; 6 import pegged.grammar : ParseTree; 7 8 import std.array : appender, array, Appender; 9 import std.algorithm : each, sort, schwartzSort, filter, uniq, sum, max, maxElement, copy; 10 import std.algorithm : each, joiner, map; 11 import std.range : chain, enumerate; 12 import std.conv : text, to; 13 import std.range : zip, only; 14 15 version (unittest) { 16 import unit_threaded; 17 __gshared bool updatedMethods = false; 18 static this() { 19 import openmethods : updateMethods; 20 if (!updatedMethods) { 21 updateMethods(); 22 updatedMethods = true; 23 } 24 } 25 auto getGenerator(string input) { 26 auto semantics = new Semantics(); 27 auto document = WebIDL(input); 28 if (!document.successful) 29 writeln(document); 30 document.successful.shouldEqual(true); 31 semantics.analyse("mod",document); 32 struct Helper { 33 IR ir; 34 Module module_; 35 Semantics semantics; 36 string generateDBindings() { return ir.generateDBindings(module_); } 37 string generateDImports() { return ir.generateDImports(module_); } 38 string generateJsExports(string[] filtered = []) { 39 auto app = IndentedStringAppender(); 40 ir.nodes.each!(n => n.toJsExport(semantics, filtered, &app)); 41 return app.data; 42 } 43 string generateJsGlobalBindings(string[] filtered = []) { 44 auto app = IndentedStringAppender(); 45 ir.generateJsGlobalBindings(filtered, app); 46 return app.data; 47 } 48 string generateJsDecoders(string[] filtered = []) { 49 auto decodedTypes = ir.generateDecodedTypes(filtered).sort!((a,b){return a.mangled < b.mangled;}).uniq!((a, b){return a.mangled == b.mangled;}); 50 bool first = true; 51 auto app = IndentedStringAppender(); 52 foreach(decoder; decodedTypes.filter!(e => e.mangled != "string")) { 53 if (!first) 54 app.putLn(","); 55 else 56 first = false; 57 decoder.generateJsDecoder(app, semantics, true); 58 } 59 return app.data; 60 } 61 string generateJsEncoders(string[] filtered = []) { 62 auto encodedTypes = ir.generateEncodedTypes(filtered).sort!((a,b){return a.mangled < b.mangled;}).uniq!((a, b){return a.mangled == b.mangled;}); 63 bool first = true; 64 auto app = IndentedStringAppender(); 65 foreach(encoder; encodedTypes.filter!(e => e.mangled != "string")) { 66 if (!first) 67 app.putLn(","); 68 else 69 first = false; 70 encoder.generateJsEncoder(app, semantics, true); 71 } 72 return app.data; 73 } 74 } 75 return Helper(semantics.toIr(), semantics.modules["mod"], semantics); 76 } 77 void shouldBeLike(string a, string b, in string file = __FILE__, in size_t line = __LINE__) { 78 import std.string : lineSplitter, strip; 79 import std.algorithm : map, filter, equal; 80 import std.range : empty; 81 auto as = a.lineSplitter.map!(l => l.strip).filter!(l => !l.empty); 82 auto bs = b.lineSplitter.map!(l => l.strip).filter!(l => !l.empty); 83 while (!as.empty && !bs.empty) { 84 if (!as.front.equal(bs.front)) 85 a.shouldEqual(b, file, line); 86 as.popFront(); 87 bs.popFront(); 88 } 89 if (as.empty != bs.empty) 90 a.shouldEqual(b); 91 } 92 } 93 94 @("default") 95 unittest { 96 auto gen = getGenerator(q{ 97 interface Event { 98 DelayNode createDelay (optional double maxDelayTime = 1.0); 99 }; 100 }); 101 gen.generateDBindings.shouldBeLike(q{ 102 struct Event { 103 nothrow: 104 JsHandle handle; 105 alias handle this; 106 this(Handle h) { 107 this.handle = JsHandle(h); 108 } 109 auto createDelay()(double maxDelayTime /* = 1.0 */) { 110 return DelayNode(Event_createDelay(this.handle, maxDelayTime)); 111 } 112 auto createDelay()() { 113 return DelayNode(Event_createDelay_0(this.handle)); 114 } 115 } 116 }); 117 gen.generateDImports.shouldBeLike(q{ 118 extern (C) Handle Event_createDelay(Handle, double); 119 extern (C) Handle Event_createDelay_0(Handle); 120 }); 121 } 122 123 @("test-b") 124 unittest { 125 auto gen = getGenerator(q{ 126 [Constructor(DOMString type, optional EventInit eventInitDict), 127 Exposed=(Window,Worker,AudioWorklet)] 128 interface Event { 129 readonly attribute DOMString type; 130 attribute EventTarget? target; 131 sequence<EventTarget> composedPath(); 132 133 const unsigned short NONE = 0; 134 attribute unsigned short eventPhase; 135 }; 136 interface Window { 137 }; 138 interface AudioWorklet { 139 }; 140 }); 141 gen.generateDBindings.shouldBeLike(q{ 142 struct AudioWorklet { 143 nothrow: 144 JsHandle handle; 145 alias handle this; 146 this(Handle h) { 147 this.handle = JsHandle(h); 148 } 149 auto Event()(string type, scope ref EventInit eventInitDict) { 150 return .Event(AudioWorklet_Event(this.handle, type, eventInitDict.handle)); 151 } 152 } 153 struct Event { 154 nothrow: 155 JsHandle handle; 156 alias handle this; 157 this(Handle h) { 158 this.handle = JsHandle(h); 159 } 160 auto type()() { 161 return Event_type_Get(this.handle); 162 } 163 void target(T0)(scope auto ref Optional!(T0) target) if (isTOrPointer!(T0, EventTarget)) { 164 Event_target_Set(this.handle, !target.empty, target.front.handle); 165 } 166 auto target()() { 167 return Event_target_Get(this.handle); 168 } 169 auto composedPath()() { 170 return Sequence!(EventTarget)(Event_composedPath(this.handle)); 171 } 172 enum ushort NONE = 0; 173 void eventPhase()(ushort eventPhase) { 174 Event_eventPhase_Set(this.handle, eventPhase); 175 } 176 auto eventPhase()() { 177 return Event_eventPhase_Get(this.handle); 178 } 179 } 180 struct Window { 181 nothrow: 182 JsHandle handle; 183 alias handle this; 184 this(Handle h) { 185 this.handle = JsHandle(h); 186 } 187 auto Event()(string type, scope ref EventInit eventInitDict) { 188 return .Event(Window_Event(this.handle, type, eventInitDict.handle)); 189 } 190 } 191 }); 192 gen.generateDImports.shouldBeLike(q{ 193 extern (C) Handle AudioWorklet_Event(Handle, string, Handle); 194 extern (C) string Event_type_Get(Handle); 195 extern (C) void Event_target_Set(Handle, bool, Handle); 196 extern (C) Optional!(EventTarget) Event_target_Get(Handle); 197 extern (C) Handle Event_composedPath(Handle); 198 extern (C) void Event_eventPhase_Set(Handle, ushort); 199 extern (C) ushort Event_eventPhase_Get(Handle); 200 extern (C) Handle Window_Event(Handle, string, Handle); 201 }); 202 gen.generateJsExports.shouldBeLike(" 203 AudioWorklet_Event: (ctx, typeLen, typePtr, eventInitDict) => { 204 setupMemory(); 205 return addObject(new objects[ctx].Event(spasm_decode_string(typeLen, typePtr), objects[eventInitDict])); 206 }, 207 Event_type_Get: (rawResult, ctx) => { 208 setupMemory(); 209 spasm_encode_string(rawResult, objects[ctx].type); 210 }, 211 Event_target_Set: (ctx, targetDefined, target) => { 212 setupMemory(); 213 objects[ctx].target = targetDefined ? objects[target] : undefined; 214 }, 215 Event_target_Get: (rawResult, ctx) => { 216 setupMemory(); 217 spasm_encode_optional_Handle(rawResult, objects[ctx].target); 218 }, 219 Event_composedPath: (ctx) => { 220 setupMemory(); 221 return addObject(objects[ctx].composedPath()); 222 }, 223 Event_eventPhase_Set: (ctx, eventPhase) => { 224 setupMemory(); 225 objects[ctx].eventPhase = eventPhase; 226 }, 227 Event_eventPhase_Get: (ctx) => { 228 setupMemory(); 229 return objects[ctx].eventPhase; 230 }, 231 Window_Event: (ctx, typeLen, typePtr, eventInitDict) => { 232 setupMemory(); 233 return addObject(new objects[ctx].Event(spasm_decode_string(typeLen, typePtr), objects[eventInitDict])); 234 }, 235 "); 236 } 237 238 @("enum") 239 unittest { 240 auto gen = getGenerator(q{ 241 enum ImageOrientation { "none", "flipY", "with-hypen" }; 242 interface Foo { 243 attribute ImageOrientation orientation; 244 ImageOrientation needs(); 245 void wants(ImageOrientation o); 246 }; 247 }); 248 249 gen.generateDBindings.shouldBeLike(q{ 250 struct Foo { 251 nothrow: 252 JsHandle handle; 253 alias handle this; 254 this(Handle h) { 255 this.handle = JsHandle(h); 256 } 257 void orientation()(ImageOrientation orientation) { 258 Foo_orientation_Set(this.handle, orientation); 259 } 260 auto orientation()() { 261 return Foo_orientation_Get(this.handle); 262 } 263 auto needs()() { 264 return Foo_needs(this.handle); 265 } 266 void wants()(ImageOrientation o) { 267 Foo_wants(this.handle, o); 268 } 269 } 270 enum ImageOrientation { 271 none, 272 flipY, 273 with_hypen 274 } 275 }); 276 gen.generateDImports.shouldBeLike(q{ 277 extern (C) void Foo_orientation_Set(Handle, ImageOrientation); 278 extern (C) ImageOrientation Foo_orientation_Get(Handle); 279 extern (C) ImageOrientation Foo_needs(Handle); 280 extern (C) void Foo_wants(Handle, ImageOrientation); 281 }); 282 gen.generateJsExports.shouldBeLike(" 283 Foo_orientation_Set: (ctx, orientation) => { 284 setupMemory(); 285 objects[ctx].orientation = spasm_decode_ImageOrientation(orientation); 286 }, 287 Foo_orientation_Get: (ctx) => { 288 setupMemory(); 289 return spasm_encode_ImageOrientation(objects[ctx].orientation); 290 }, 291 Foo_needs: (ctx) => { 292 setupMemory(); 293 return spasm_encode_ImageOrientation(objects[ctx].needs()); 294 }, 295 Foo_wants: (ctx, o) => { 296 setupMemory(); 297 objects[ctx].wants(spasm_decode_ImageOrientation(o)); 298 }, 299 "); 300 } 301 302 unittest { 303 auto gen = getGenerator(q{ 304 callback SomethingCallback = DOMString (DOMString msg, boolean v); 305 callback DecodeErrorCallback = void (DOMException error); 306 }); 307 gen.generateDBindings.shouldBeLike(q{ 308 alias DecodeErrorCallback = void delegate(DOMException); 309 alias SomethingCallback = string delegate(string, bool); 310 }); 311 gen.generateDImports.shouldBeEmpty; 312 gen.generateJsExports.shouldBeLike(""); 313 } 314 315 @("sequence") 316 unittest { 317 auto gen = getGenerator(q{ 318 interface BaseAudioContext : EventTarget { 319 PeriodicWave createPeriodicWave (sequence<float> real, optional PeriodicWaveConstraints constraints); 320 }; 321 }); 322 gen.generateDBindings.shouldBeLike(q{ 323 struct BaseAudioContext { 324 nothrow: 325 EventTarget _parent; 326 alias _parent this; 327 this(Handle h) { 328 _parent = .EventTarget(h); 329 } 330 auto createPeriodicWave()(scope ref Sequence!(float) real_, scope ref PeriodicWaveConstraints constraints) { 331 return PeriodicWave(BaseAudioContext_createPeriodicWave(this._parent, real_.handle, constraints.handle)); 332 } 333 auto createPeriodicWave()(scope ref Sequence!(float) real_) { 334 return PeriodicWave(BaseAudioContext_createPeriodicWave_0(this._parent, real_.handle)); 335 } 336 } 337 }); 338 gen.generateDImports.shouldBeLike(q{ 339 extern (C) Handle BaseAudioContext_createPeriodicWave(Handle, Handle, Handle); 340 extern (C) Handle BaseAudioContext_createPeriodicWave_0(Handle, Handle); 341 }); 342 gen.generateJsExports.shouldBeLike(" 343 BaseAudioContext_createPeriodicWave: (ctx, real, constraints) => { 344 setupMemory(); 345 return addObject(objects[ctx].createPeriodicWave(objects[real], objects[constraints])); 346 }, 347 BaseAudioContext_createPeriodicWave_0: (ctx, real) => { 348 setupMemory(); 349 return addObject(objects[ctx].createPeriodicWave(objects[real])); 350 }, 351 "); 352 } 353 354 @("optional") 355 unittest { 356 auto gen = getGenerator(q{ 357 interface Foo { 358 void bar (optional unsigned long number); 359 }; 360 }); 361 gen.generateDBindings.shouldBeLike(q{ 362 struct Foo { 363 nothrow: 364 JsHandle handle; 365 alias handle this; 366 this(Handle h) { 367 this.handle = JsHandle(h); 368 } 369 void bar()(uint number) { 370 Foo_bar(this.handle, number); 371 } 372 void bar()() { 373 Foo_bar_0(this.handle); 374 } 375 } 376 }); 377 gen.generateDImports.shouldBeLike(q{ 378 extern (C) void Foo_bar(Handle, uint); 379 extern (C) void Foo_bar_0(Handle); 380 }); 381 gen.generateJsExports.shouldBeLike(" 382 Foo_bar: (ctx, number) => { 383 setupMemory(); 384 objects[ctx].bar(number); 385 }, 386 Foo_bar_0: (ctx) => { 387 setupMemory(); 388 objects[ctx].bar(); 389 }, 390 "); 391 } 392 393 @("null") 394 unittest { 395 auto gen = getGenerator(q{ 396 interface Foo { 397 void bar (unsigned long? number, Bar? constraints); 398 }; 399 }); 400 gen.generateDBindings.shouldBeLike(q{ 401 struct Foo { 402 nothrow: 403 JsHandle handle; 404 alias handle this; 405 this(Handle h) { 406 this.handle = JsHandle(h); 407 } 408 void bar(T0, T1)(scope auto ref Optional!(T0) number, scope auto ref Optional!(T1) constraints) if (isTOrPointer!(T0, uint) && isTOrPointer!(T1, Bar)) { 409 Foo_bar(this.handle, !number.empty, number.front, !constraints.empty, constraints.front.handle); 410 } 411 } 412 }); 413 gen.generateDImports.shouldBeLike(q{ 414 extern (C) void Foo_bar(Handle, bool, uint, bool, Handle); 415 }); 416 gen.generateJsExports.shouldBeLike(" 417 Foo_bar: (ctx, numberDefined, number, constraintsDefined, constraints) => { 418 setupMemory(); 419 objects[ctx].bar(numberDefined ? number : undefined, constraintsDefined ? objects[constraints] : undefined); 420 }, 421 "); 422 } 423 424 @("interface.callback") 425 unittest { 426 auto gen = getGenerator(q{ 427 callback DecodeErrorCallback = void (DOMException error); 428 callback DecodeSuccessCallback = void (AudioBuffer decodedData); 429 interface BaseAudioContext : EventTarget { 430 Promise<AudioBuffer> decodeAudioData (ArrayBuffer audioData, optional DecodeSuccessCallback? successCallback, optional DecodeErrorCallback? errorCallback); 431 }; 432 }); 433 gen.generateDBindings.shouldBeLike(q{ 434 struct BaseAudioContext { 435 nothrow: 436 EventTarget _parent; 437 alias _parent this; 438 this(Handle h) { 439 _parent = .EventTarget(h); 440 } 441 auto decodeAudioData(T1, T2)(scope ref ArrayBuffer audioData, scope auto ref Optional!(T1) successCallback, scope auto ref Optional!(T2) errorCallback) if (isTOrPointer!(T1, DecodeSuccessCallback) && isTOrPointer!(T2, DecodeErrorCallback)) { 442 return Promise!(AudioBuffer)(BaseAudioContext_decodeAudioData(this._parent, audioData.handle, !successCallback.empty, successCallback.front, !errorCallback.empty, errorCallback.front)); 443 } 444 auto decodeAudioData(T1)(scope ref ArrayBuffer audioData, scope auto ref Optional!(T1) successCallback) if (isTOrPointer!(T1, DecodeSuccessCallback)) { 445 return Promise!(AudioBuffer)(BaseAudioContext_decodeAudioData_0(this._parent, audioData.handle, !successCallback.empty, successCallback.front)); 446 } 447 auto decodeAudioData()(scope ref ArrayBuffer audioData) { 448 return Promise!(AudioBuffer)(BaseAudioContext_decodeAudioData_1(this._parent, audioData.handle)); 449 } 450 } 451 alias DecodeErrorCallback = void delegate(DOMException); 452 alias DecodeSuccessCallback = void delegate(AudioBuffer); 453 }); 454 gen.generateDImports.shouldBeLike(q{ 455 extern (C) Handle BaseAudioContext_decodeAudioData(Handle, Handle, bool, DecodeSuccessCallback, bool, DecodeErrorCallback); 456 extern (C) Handle BaseAudioContext_decodeAudioData_0(Handle, Handle, bool, DecodeSuccessCallback); 457 extern (C) Handle BaseAudioContext_decodeAudioData_1(Handle, Handle); 458 }); 459 gen.generateJsExports.shouldBeLike(" 460 BaseAudioContext_decodeAudioData: (ctx, audioData, successCallbackDefined, successCallbackCtx, successCallbackPtr, errorCallbackDefined, errorCallbackCtx, errorCallbackPtr) => { 461 setupMemory(); 462 return addObject(objects[ctx].decodeAudioData(objects[audioData], successCallbackDefined ? (decodedData)=>{encode_handle(0, decodedData);spasm_indirect_function_get(successCallbackPtr)(successCallbackCtx, 0)} : undefined, errorCallbackDefined ? (error)=>{encode_handle(0, error);spasm_indirect_function_get(errorCallbackPtr)(errorCallbackCtx, 0)} : undefined)); 463 }, 464 BaseAudioContext_decodeAudioData_0: (ctx, audioData, successCallbackDefined, successCallbackCtx, successCallbackPtr) => { 465 setupMemory(); 466 return addObject(objects[ctx].decodeAudioData(objects[audioData], successCallbackDefined ? (decodedData)=>{encode_handle(0, decodedData);spasm_indirect_function_get(successCallbackPtr)(successCallbackCtx, 0)} : undefined)); 467 }, 468 BaseAudioContext_decodeAudioData_1: (ctx, audioData) => { 469 setupMemory(); 470 return addObject(objects[ctx].decodeAudioData(objects[audioData])); 471 }, 472 "); 473 } 474 475 @("sumType.interface") 476 unittest { 477 auto gen = getGenerator(q{ 478 enum AudioContextLatencyCategory { "balanced", "interactive", "playback" }; 479 interface AudioContextOptions { 480 attribute (AudioContextLatencyCategory or double) latencyHint; 481 attribute (boolean or double)? sampleRate; 482 (DOMString or AudioContextLatencyCategory) fooBar(); 483 }; 484 }); 485 gen.generateDBindings.shouldBeLike(q{ 486 enum AudioContextLatencyCategory { 487 balanced, 488 interactive, 489 playback 490 } 491 struct AudioContextOptions { 492 nothrow: 493 JsHandle handle; 494 alias handle this; 495 this(Handle h) { 496 this.handle = JsHandle(h); 497 } 498 void latencyHint()(scope ref SumType!(AudioContextLatencyCategory, double) latencyHint) { 499 AudioContextOptions_latencyHint_Set(this.handle, latencyHint); 500 } 501 auto latencyHint()() { 502 return AudioContextOptions_latencyHint_Get(this.handle); 503 } 504 void sampleRate(T0)(scope auto ref Optional!(T0) sampleRate) if (isTOrPointer!(T0, SumType!(bool, double))) { 505 AudioContextOptions_sampleRate_Set(this.handle, !sampleRate.empty, *sampleRate.frontRef); 506 } 507 auto sampleRate()() { 508 return AudioContextOptions_sampleRate_Get(this.handle); 509 } 510 auto fooBar()() { 511 return AudioContextOptions_fooBar(this.handle); 512 } 513 } 514 }); 515 gen.generateDImports.shouldBeLike(q{ 516 extern (C) void AudioContextOptions_latencyHint_Set(Handle, scope ref SumType!(AudioContextLatencyCategory, double)); 517 extern (C) SumType!(AudioContextLatencyCategory, double) AudioContextOptions_latencyHint_Get(Handle); 518 extern (C) void AudioContextOptions_sampleRate_Set(Handle, bool, scope ref SumType!(bool, double)); 519 extern (C) Optional!(SumType!(bool, double)) AudioContextOptions_sampleRate_Get(Handle); 520 extern (C) SumType!(string, AudioContextLatencyCategory) AudioContextOptions_fooBar(Handle); 521 }); 522 // TODO: the optionals and unions returned from js should probably be stored in first extra param 523 gen.generateJsExports.shouldBeLike(" 524 AudioContextOptions_latencyHint_Set: (ctx, latencyHint) => { 525 setupMemory(); 526 objects[ctx].latencyHint = spasm_decode_union2_AudioContextLatencyCategory_double(latencyHint); 527 }, 528 AudioContextOptions_latencyHint_Get: (rawResult, ctx) => { 529 setupMemory(); 530 spasm_encode_union2_AudioContextLatencyCategory_double(rawResult, objects[ctx].latencyHint); 531 }, 532 AudioContextOptions_sampleRate_Set: (ctx, sampleRateDefined, sampleRate) => { 533 setupMemory(); 534 objects[ctx].sampleRate = sampleRateDefined ? spasm_decode_union2_bool_double(sampleRate) : undefined; 535 }, 536 AudioContextOptions_sampleRate_Get: (rawResult, ctx) => { 537 setupMemory(); 538 spasm_encode_optional_union2_bool_double(rawResult, objects[ctx].sampleRate); 539 }, 540 AudioContextOptions_fooBar: (rawResult, ctx) => { 541 setupMemory(); 542 spasm_encode_union2_string_AudioContextLatencyCategory(rawResult, objects[ctx].fooBar()); 543 }, 544 "); 545 } 546 547 @("sumType.dictionary") 548 unittest { 549 auto gen = getGenerator(q{ 550 enum AudioContextLatencyCategory { "balanced", "interactive", "playback" }; 551 dictionary AudioContextOptions { 552 (AudioContextLatencyCategory or double) latencyHint = "interactive"; 553 (boolean or double)? sampleRate; 554 }; 555 }); 556 gen.generateDBindings.shouldBeLike(q{ 557 enum AudioContextLatencyCategory { 558 balanced, 559 interactive, 560 playback 561 } 562 struct AudioContextOptions { 563 nothrow: 564 JsHandle handle; 565 alias handle this; 566 this(Handle h) { 567 this.handle = JsHandle(h); 568 } 569 static auto create() { 570 return AudioContextOptions(spasm_add__object()); 571 } 572 void latencyHint()(scope ref SumType!(AudioContextLatencyCategory, double) latencyHint) { 573 AudioContextOptions_latencyHint_Set(this.handle, latencyHint); 574 } 575 auto latencyHint()() { 576 return AudioContextOptions_latencyHint_Get(this.handle); 577 } 578 void sampleRate(T0)(scope auto ref Optional!(T0) sampleRate) if (isTOrPointer!(T0, SumType!(bool, double))) { 579 AudioContextOptions_sampleRate_Set(this.handle, !sampleRate.empty, *sampleRate.frontRef); 580 } 581 auto sampleRate()() { 582 return AudioContextOptions_sampleRate_Get(this.handle); 583 } 584 } 585 }); 586 gen.generateDImports.shouldBeLike(q{ 587 extern (C) void AudioContextOptions_latencyHint_Set(Handle, scope ref SumType!(AudioContextLatencyCategory, double)); 588 extern (C) SumType!(AudioContextLatencyCategory, double) AudioContextOptions_latencyHint_Get(Handle); 589 extern (C) void AudioContextOptions_sampleRate_Set(Handle, bool, scope ref SumType!(bool, double)); 590 extern (C) Optional!(SumType!(bool, double)) AudioContextOptions_sampleRate_Get(Handle); 591 }); 592 gen.generateJsExports.shouldBeLike(" 593 AudioContextOptions_latencyHint_Set: (ctx, latencyHint) => { 594 setupMemory(); 595 objects[ctx].latencyHint = spasm_decode_union2_AudioContextLatencyCategory_double(latencyHint); 596 }, 597 AudioContextOptions_latencyHint_Get: (rawResult, ctx) => { 598 setupMemory(); 599 spasm_encode_union2_AudioContextLatencyCategory_double(rawResult, objects[ctx].latencyHint); 600 }, 601 AudioContextOptions_sampleRate_Set: (ctx, sampleRateDefined, sampleRate) => { 602 setupMemory(); 603 objects[ctx].sampleRate = sampleRateDefined ? spasm_decode_union2_bool_double(sampleRate) : undefined; 604 }, 605 AudioContextOptions_sampleRate_Get: (rawResult, ctx) => { 606 setupMemory(); 607 spasm_encode_optional_union2_bool_double(rawResult, objects[ctx].sampleRate); 608 }, 609 "); 610 611 } 612 613 @("partial") 614 unittest { 615 // TODO: test partial interface with an optional attribute 616 } 617 618 @("partial.friendlyName") 619 unittest { 620 auto gen = getGenerator(q{ 621 interface double { 622 }; 623 partial interface double { 624 [CEReactions] attribute DOMString real; 625 }; 626 }); 627 gen.generateDBindings.shouldBeLike(q{ 628 struct double_ { 629 nothrow: 630 JsHandle handle; 631 alias handle this; 632 this(Handle h) { 633 this.handle = JsHandle(h); 634 } 635 void real_()(string real_) { 636 double_real_Set(this.handle, real_); 637 } 638 auto real_()() { 639 return double_real_Get(this.handle); 640 } 641 } 642 }); 643 gen.generateDImports.shouldBeLike(q{ 644 extern (C) void double_real_Set(Handle, string); 645 extern (C) string double_real_Get(Handle); 646 }); 647 gen.generateJsExports.shouldBeLike(" 648 double_real_Set: (ctx, realLen, realPtr) => { 649 setupMemory(); 650 objects[ctx].real = spasm_decode_string(realLen, realPtr); 651 }, 652 double_real_Get: (rawResult, ctx) => { 653 setupMemory(); 654 spasm_encode_string(rawResult, objects[ctx].real); 655 }, 656 "); 657 } 658 659 @("dictionary.primitives") 660 unittest { 661 auto gen = getGenerator(q{ 662 dictionary AudioTimestamp { 663 double contextTime; 664 }; 665 }); 666 gen.generateDBindings.shouldBeLike(q{ 667 struct AudioTimestamp { 668 nothrow: 669 JsHandle handle; 670 alias handle this; 671 this(Handle h) { 672 this.handle = JsHandle(h); 673 } 674 static auto create() { 675 return AudioTimestamp(spasm_add__object()); 676 } 677 void contextTime()(double contextTime) { 678 AudioTimestamp_contextTime_Set(this.handle, contextTime); 679 } 680 auto contextTime()() { 681 return AudioTimestamp_contextTime_Get(this.handle); 682 } 683 } 684 }); 685 gen.generateDImports.shouldBeLike(q{ 686 extern (C) void AudioTimestamp_contextTime_Set(Handle, double); 687 extern (C) double AudioTimestamp_contextTime_Get(Handle); 688 }); 689 gen.generateJsExports.shouldBeLike(" 690 AudioTimestamp_contextTime_Set: (ctx, contextTime) => { 691 setupMemory(); 692 objects[ctx].contextTime = contextTime; 693 }, 694 AudioTimestamp_contextTime_Get: (ctx) => { 695 setupMemory(); 696 return objects[ctx].contextTime; 697 }, 698 "); 699 } 700 701 @("dictionary.inheritance") 702 unittest { 703 auto gen = getGenerator(q{ 704 dictionary AudioWorkletNodeOptions : AudioNodeOptions { 705 }; 706 }); 707 gen.generateDBindings.shouldBeLike(q{ 708 struct AudioWorkletNodeOptions { 709 nothrow: 710 AudioNodeOptions _parent; 711 alias _parent this; 712 this(Handle h) { 713 _parent = .AudioNodeOptions(h); 714 } 715 static auto create() { 716 return AudioWorkletNodeOptions(spasm_add__object()); 717 } 718 } 719 }); 720 gen.generateDImports.shouldBeLike(q{}); 721 gen.generateJsExports.shouldBeLike(""); 722 } 723 724 @("interface.friendlyName") 725 unittest { 726 auto gen = getGenerator(q{ 727 interface double { 728 attribute double real; 729 }; 730 }); 731 gen.generateDBindings.shouldBeLike(q{ 732 struct double_ { 733 nothrow: 734 JsHandle handle; 735 alias handle this; 736 this(Handle h) { 737 this.handle = JsHandle(h); 738 } 739 void real_()(double real_) { 740 double_real_Set(this.handle, real_); 741 } 742 auto real_()() { 743 return double_real_Get(this.handle); 744 } 745 } 746 }); 747 gen.generateDImports.shouldBeLike(q{ 748 extern (C) void double_real_Set(Handle, double); 749 extern (C) double double_real_Get(Handle); 750 }); 751 gen.generateJsExports.shouldBeLike(" 752 double_real_Set: (ctx, real) => { 753 setupMemory(); 754 objects[ctx].real = real; 755 }, 756 double_real_Get: (ctx) => { 757 setupMemory(); 758 return objects[ctx].real; 759 }, 760 "); 761 } 762 763 @("dictionary.record") 764 unittest { 765 auto gen = getGenerator(q{ 766 dictionary AudioWorkletNodeOptions { 767 record<DOMString, double> real; 768 }; 769 }); 770 gen.generateDBindings.shouldBeLike(q{ 771 struct AudioWorkletNodeOptions { 772 nothrow: 773 JsHandle handle; 774 alias handle this; 775 this(Handle h) { 776 this.handle = JsHandle(h); 777 } 778 static auto create() { 779 return AudioWorkletNodeOptions(spasm_add__object()); 780 } 781 void real_()(scope ref Record!(string, double) real_) { 782 AudioWorkletNodeOptions_real_Set(this.handle, real_.handle); 783 } 784 auto real_()() { 785 return Record!(string, double)(AudioWorkletNodeOptions_real_Get(this.handle)); 786 } 787 } 788 }); 789 gen.generateDImports.shouldBeLike(q{ 790 extern (C) void AudioWorkletNodeOptions_real_Set(Handle, Handle); 791 extern (C) Handle AudioWorkletNodeOptions_real_Get(Handle); 792 }); 793 // TODO: jsExports 794 } 795 796 @("dictionary.float") 797 unittest { 798 auto gen = getGenerator(q{ 799 dictionary AudioParamDescriptor { 800 float maxValue = 3.4028235e38; 801 float minValue = -3.4028235e38; 802 }; 803 }); 804 gen.generateDBindings.shouldBeLike(q{ 805 struct AudioParamDescriptor { 806 nothrow: 807 JsHandle handle; 808 alias handle this; 809 this(Handle h) { 810 this.handle = JsHandle(h); 811 } 812 static auto create() { 813 return AudioParamDescriptor(spasm_add__object()); 814 } 815 void maxValue()(float maxValue) { 816 AudioParamDescriptor_maxValue_Set(this.handle, maxValue); 817 } 818 auto maxValue()() { 819 return AudioParamDescriptor_maxValue_Get(this.handle); 820 } 821 void minValue()(float minValue) { 822 AudioParamDescriptor_minValue_Set(this.handle, minValue); 823 } 824 auto minValue()() { 825 return AudioParamDescriptor_minValue_Get(this.handle); 826 } 827 } 828 }); 829 gen.generateDImports.shouldBeLike(q{ 830 extern (C) void AudioParamDescriptor_maxValue_Set(Handle, float); 831 extern (C) float AudioParamDescriptor_maxValue_Get(Handle); 832 extern (C) void AudioParamDescriptor_minValue_Set(Handle, float); 833 extern (C) float AudioParamDescriptor_minValue_Get(Handle); 834 }); 835 gen.generateJsExports.shouldBeLike(" 836 AudioParamDescriptor_maxValue_Set: (ctx, maxValue) => { 837 setupMemory(); 838 objects[ctx].maxValue = maxValue; 839 }, 840 AudioParamDescriptor_maxValue_Get: (ctx) => { 841 setupMemory(); 842 return objects[ctx].maxValue; 843 }, 844 AudioParamDescriptor_minValue_Set: (ctx, minValue) => { 845 setupMemory(); 846 objects[ctx].minValue = minValue; 847 }, 848 AudioParamDescriptor_minValue_Get: (ctx) => { 849 setupMemory(); 850 return objects[ctx].minValue; 851 }, 852 "); 853 } 854 855 @("interface.maplike") 856 unittest { 857 auto gen = getGenerator(q{ 858 [Exposed=Window] 859 interface AudioParamMap { 860 readonly maplike<DOMString, AudioParam>; 861 }; 862 }); 863 // TODO: a readonly maplike should probably not have clear, set, delete, etc. 864 gen.generateDBindings.shouldBeLike(q{ 865 struct AudioParamMap { 866 nothrow: 867 JsHandle handle; 868 alias handle this; 869 this(Handle h) { 870 this.handle = JsHandle(h); 871 } 872 uint size() { 873 return Maplike_string_Handle_size(this.handle); 874 } 875 void clear() { 876 Maplike_string_Handle_clear(this.handle); 877 } 878 void delete_(string key) { 879 Maplike_string_Handle_delete(this.handle, key); 880 } 881 Iterator!(ArrayPair!(string, AudioParam)) entries() { 882 return Iterator!(ArrayPair!(string, AudioParam))(Maplike_string_Handle_entries(this.handle)); 883 } 884 extern(C) void forEach(void delegate(string, Handle, Handle) callback) { 885 Maplike_string_Handle_forEach(this.handle, callback); 886 } 887 AudioParam get(string key) { 888 return AudioParam(Maplike_string_Handle_get(this.handle, key)); 889 } 890 bool has(string key) { 891 return Maplike_string_Handle_has(this.handle, key); 892 } 893 Iterator!(string) keys() { 894 return Iterator!(string)(Maplike_string_Handle_keys(this.handle)); 895 } 896 void set(string key, scope ref AudioParam value) { 897 Maplike_string_Handle_set(this.handle, key, value.handle); 898 } 899 Iterator!(AudioParam) values() { 900 return Iterator!(AudioParam)(Maplike_string_Handle_values(this.handle)); 901 } 902 } 903 }); 904 gen.generateDImports.shouldBeLike(q{ 905 extern (C) uint Maplike_string_Handle_size(Handle); 906 extern (C) void Maplike_string_Handle_clear(Handle); 907 extern (C) void Maplike_string_Handle_delete(Handle, string key); 908 extern (C) Handle Maplike_string_Handle_entries(Handle); 909 extern (C) void Maplike_string_Handle_forEach(Handle, void delegate(string, Handle, Handle)); 910 extern (C) AudioParam Maplike_string_Handle_get(Handle, string); 911 extern (C) bool Maplike_string_Handle_has(Handle, string); 912 extern (C) Handle Maplike_string_Handle_keys(Handle); 913 extern (C) void Maplike_string_Handle_set(Handle, string key, Handle value); 914 extern (C) Handle Maplike_string_Handle_values(Handle); 915 }); 916 gen.generateJsExports.shouldBeLike(""); 917 } 918 919 @("friendlyName") 920 unittest { 921 "real".friendlyName.shouldEqual("real_"); 922 "with-hypen".friendlyName.shouldEqual("with_hypen"); 923 "0with-number".friendlyName.shouldEqual("_0with_number"); 924 } 925 926 @("putCamelCase") 927 unittest { 928 { 929 auto app = appender!string; 930 app.putCamelCase("HTMLAnchorElement"); 931 app.data.shouldEqual("htmlAnchorElement"); 932 } 933 { 934 auto app = appender!string; 935 app.putCamelCase("HtmlAnchorElement"); 936 app.data.shouldEqual("htmlAnchorElement"); 937 } 938 { 939 auto app = appender!string; 940 app.putCamelCase("htmlAnchorElement"); 941 app.data.shouldEqual("htmlAnchorElement"); 942 } 943 } 944 945 @("optional.string") 946 unittest { 947 auto gen = getGenerator(q{ 948 interface BaseAudioContext : EventTarget { 949 DOMString? createPeriodicWave(); 950 attribute DOMString? name; 951 void foo(DOMString? title); 952 }; 953 }); 954 gen.generateDBindings.shouldBeLike(q{ 955 struct BaseAudioContext { 956 nothrow: 957 EventTarget _parent; 958 alias _parent this; 959 this(Handle h) { 960 _parent = .EventTarget(h); 961 } 962 auto createPeriodicWave()() { 963 return BaseAudioContext_createPeriodicWave(this._parent); 964 } 965 void name(T0)(scope auto ref Optional!(T0) name) if (isTOrPointer!(T0, string)) { 966 BaseAudioContext_name_Set(this._parent, !name.empty, name.front); 967 } 968 auto name()() { 969 return BaseAudioContext_name_Get(this._parent); 970 } 971 void foo(T0)(scope auto ref Optional!(T0) title) if (isTOrPointer!(T0, string)) { 972 BaseAudioContext_foo(this._parent, !title.empty, title.front); 973 } 974 } 975 }); 976 gen.generateDImports.shouldBeLike(q{ 977 extern (C) Optional!(string) BaseAudioContext_createPeriodicWave(Handle); 978 extern (C) void BaseAudioContext_name_Set(Handle, bool, string); 979 extern (C) Optional!(string) BaseAudioContext_name_Get(Handle); 980 extern (C) void BaseAudioContext_foo(Handle, bool, string); 981 }); 982 gen.generateJsExports.shouldBeLike(" 983 BaseAudioContext_createPeriodicWave: (rawResult, ctx) => { 984 setupMemory(); 985 spasm_encode_optional_string(rawResult, objects[ctx].createPeriodicWave()); 986 }, 987 BaseAudioContext_name_Set: (ctx, nameDefined, nameLen, namePtr) => { 988 setupMemory(); 989 objects[ctx].name = nameDefined ? spasm_decode_string(nameLen, namePtr) : undefined; 990 }, 991 BaseAudioContext_name_Get: (rawResult, ctx) => { 992 setupMemory(); 993 spasm_encode_optional_string(rawResult, objects[ctx].name); 994 }, 995 BaseAudioContext_foo: (ctx, titleDefined, titleLen, titlePtr) => { 996 setupMemory(); 997 objects[ctx].foo(titleDefined ? spasm_decode_string(titleLen, titlePtr) : undefined); 998 }, 999 "); 1000 } 1001 1002 @("interface.nullable") 1003 unittest { 1004 auto gen = getGenerator(q{ 1005 interface ClipboardEvent : Event { 1006 readonly attribute DataTransfer? clipboardData; 1007 }; 1008 }); 1009 gen.generateDBindings.shouldBeLike(q{ 1010 struct ClipboardEvent { 1011 nothrow: 1012 Event _parent; 1013 alias _parent this; 1014 this(Handle h) { 1015 _parent = .Event(h); 1016 } 1017 auto clipboardData()() { 1018 return ClipboardEvent_clipboardData_Get(this._parent); 1019 } 1020 } 1021 }); 1022 gen.generateDImports.shouldBeLike(q{ 1023 extern (C) Optional!(DataTransfer) ClipboardEvent_clipboardData_Get(Handle); 1024 }); 1025 gen.generateJsExports.shouldBeLike(" 1026 ClipboardEvent_clipboardData_Get: (rawResult, ctx) => { 1027 setupMemory(); 1028 spasm_encode_optional_Handle(rawResult, objects[ctx].clipboardData); 1029 }, 1030 "); 1031 } 1032 1033 @("dictionary.nullable") 1034 unittest { 1035 auto gen = getGenerator(q{ 1036 dictionary FocusEventInit { 1037 EventTarget? relatedTarget = null; 1038 }; 1039 }); 1040 gen.generateDBindings.shouldBeLike(q{ 1041 struct FocusEventInit { 1042 nothrow: 1043 JsHandle handle; 1044 alias handle this; 1045 this(Handle h) { 1046 this.handle = JsHandle(h); 1047 } 1048 static auto create() { 1049 return FocusEventInit(spasm_add__object()); 1050 } 1051 void relatedTarget(T0)(scope auto ref Optional!(T0) relatedTarget) if (isTOrPointer!(T0, EventTarget)) { 1052 FocusEventInit_relatedTarget_Set(this.handle, !relatedTarget.empty, relatedTarget.front.handle); 1053 } 1054 auto relatedTarget()() { 1055 return FocusEventInit_relatedTarget_Get(this.handle); 1056 } 1057 } 1058 }); 1059 gen.generateDImports.shouldBeLike(q{ 1060 extern (C) void FocusEventInit_relatedTarget_Set(Handle, bool, Handle); 1061 extern (C) Optional!(EventTarget) FocusEventInit_relatedTarget_Get(Handle); 1062 }); 1063 gen.generateJsExports.shouldBeLike(" 1064 FocusEventInit_relatedTarget_Set: (ctx, relatedTargetDefined, relatedTarget) => { 1065 setupMemory(); 1066 objects[ctx].relatedTarget = relatedTargetDefined ? objects[relatedTarget] : undefined; 1067 }, 1068 FocusEventInit_relatedTarget_Get: (rawResult, ctx) => { 1069 setupMemory(); 1070 spasm_encode_optional_Handle(rawResult, objects[ctx].relatedTarget); 1071 }, 1072 "); 1073 } 1074 1075 @("interface.mixin") 1076 unittest { 1077 auto gen = getGenerator(q{ 1078 interface mixin GenericTransformStream { 1079 readonly attribute WritableStream writable; 1080 readonly attribute ReadbleStream readable; 1081 }; 1082 interface TextDecoderStream { 1083 }; 1084 TextDecoderStream includes GenericTransformStream; 1085 }); 1086 gen.generateDBindings.shouldBeLike(q{ 1087 struct TextDecoderStream { 1088 nothrow: 1089 JsHandle handle; 1090 alias handle this; 1091 this(Handle h) { 1092 this.handle = JsHandle(h); 1093 } 1094 auto writable()() { 1095 return WritableStream(GenericTransformStream_writable_Get(this.handle)); 1096 } 1097 auto readable()() { 1098 return ReadbleStream(GenericTransformStream_readable_Get(this.handle)); 1099 } 1100 } 1101 }); 1102 gen.generateDImports.shouldBeLike(q{ 1103 extern (C) Handle GenericTransformStream_writable_Get(Handle); 1104 extern (C) Handle GenericTransformStream_readable_Get(Handle); 1105 }); 1106 gen.generateJsExports.shouldBeLike(" 1107 GenericTransformStream_writable_Get: (ctx) => { 1108 setupMemory(); 1109 return addObject(objects[ctx].writable); 1110 }, 1111 GenericTransformStream_readable_Get: (ctx) => { 1112 setupMemory(); 1113 return addObject(objects[ctx].readable); 1114 }, 1115 "); 1116 } 1117 1118 @("interface.ExtendedAttribute") 1119 unittest { 1120 auto gen = getGenerator(q{ 1121 interface HTMLOrSVGElement { 1122 [SameObject] readonly attribute DOMStringMap datacube; 1123 }; 1124 }); 1125 gen.generateDBindings.shouldBeLike(q{ 1126 struct HTMLOrSVGElement { 1127 nothrow: 1128 JsHandle handle; 1129 alias handle this; 1130 this(Handle h) { 1131 this.handle = JsHandle(h); 1132 } 1133 auto datacube()() { 1134 return DOMStringMap(HTMLOrSVGElement_datacube_Get(this.handle)); 1135 } 1136 } 1137 }); 1138 gen.generateDImports.shouldBeLike(q{ 1139 extern (C) Handle HTMLOrSVGElement_datacube_Get(Handle); 1140 }); 1141 gen.generateJsExports.shouldBeLike(" 1142 HTMLOrSVGElement_datacube_Get: (ctx) => { 1143 setupMemory(); 1144 return addObject(objects[ctx].datacube); 1145 }, 1146 "); 1147 } 1148 1149 @("interface.special") 1150 unittest { 1151 auto gen = getGenerator(q{ 1152 [Exposed=Window, 1153 OverrideBuiltins] 1154 interface DOMStringMap { 1155 getter DOMString (DOMString name); 1156 [CEReactions] setter void (DOMString name, DOMString value); 1157 [CEReactions] deleter void (DOMString name); 1158 getter DOMString byKey(DOMString name); 1159 [CEReactions] setter void byKey(DOMString name, DOMString value); 1160 }; 1161 }); 1162 gen.generateDBindings.shouldBeLike(q{ 1163 struct DOMStringMap { 1164 nothrow: 1165 JsHandle handle; 1166 alias handle this; 1167 this(Handle h) { 1168 this.handle = JsHandle(h); 1169 } 1170 auto opIndex()(string name) { 1171 return DOMStringMap_getter__string(this.handle, name); 1172 } 1173 auto opDispatch(string name)() { 1174 return DOMStringMap_getter__string(this.handle, name); 1175 } 1176 void opIndexAssign()(string value, string name) { 1177 DOMStringMap_setter__string_string(this.handle, name, value); 1178 } 1179 void opDispatch(string name)(string value) { 1180 DOMStringMap_setter__string_string(this.handle, name, value); 1181 } 1182 void remove()(string name) { 1183 DOMStringMap_deleter(this.handle, name); 1184 } 1185 auto byKey()(string name) { 1186 return DOMStringMap_byKey_getter(this.handle, name); 1187 } 1188 void byKey()(string name, string value) { 1189 DOMStringMap_byKey_setter(this.handle, name, value); 1190 } 1191 } 1192 }); 1193 gen.generateDImports.shouldBeLike(q{ 1194 extern (C) string DOMStringMap_getter__string(Handle, string); 1195 extern (C) void DOMStringMap_setter__string_string(Handle, string, string); 1196 extern (C) void DOMStringMap_deleter(Handle, string); 1197 extern (C) string DOMStringMap_byKey_getter(Handle, string); 1198 extern (C) void DOMStringMap_byKey_setter(Handle, string, string); 1199 }); 1200 gen.generateJsExports.shouldBeLike(" 1201 DOMStringMap_getter__string: (rawResult, ctx, nameLen, namePtr) => { 1202 setupMemory(); 1203 spasm_encode_string(rawResult, objects[ctx][spasm_decode_string(nameLen, namePtr)]); 1204 }, 1205 DOMStringMap_setter__string_string: (ctx, nameLen, namePtr, valueLen, valuePtr) => { 1206 setupMemory(); 1207 objects[ctx][spasm_decode_string(nameLen, namePtr)] = spasm_decode_string(valueLen, valuePtr); 1208 }, 1209 DOMStringMap_deleter: (ctx, nameLen, namePtr) => { 1210 setupMemory(); 1211 delete objects[ctx][spasm_decode_string(nameLen, namePtr)]; 1212 }, 1213 DOMStringMap_byKey_getter: (rawResult, ctx, nameLen, namePtr) => { 1214 setupMemory(); 1215 spasm_encode_string(rawResult, objects[ctx].byKey(spasm_decode_string(nameLen, namePtr))); 1216 }, 1217 DOMStringMap_byKey_setter: (ctx, nameLen, namePtr, valueLen, valuePtr) => { 1218 setupMemory(); 1219 objects[ctx].byKey(spasm_decode_string(nameLen, namePtr), spasm_decode_string(valueLen, valuePtr)); 1220 }, 1221 "); 1222 } 1223 1224 @("interface.extendedAttributeList") 1225 unittest { 1226 getGenerator(q{ 1227 [Constructor(DOMString type, ErrorEventInit eventInitDict, symbolBar bar, any thing)] 1228 interface ErrorEvent : Event { 1229 }; 1230 }); 1231 } 1232 1233 @("interface.mixin.required") 1234 unittest { 1235 getGenerator(q{ 1236 interface mixin SVGTests { 1237 [SameObject] readonly attribute Foo requiredExtensions; 1238 }; 1239 }); 1240 } 1241 1242 @("comments") 1243 unittest { 1244 getGenerator(q{ 1245 interface Foo { 1246 /* AlphaFunction (not supported in ES20) */ 1247 /* NEVER */ 1248 /* LESS */ 1249 /* EQUAL */ 1250 /* LEQUAL */ 1251 }; 1252 }); 1253 } 1254 1255 @("typedef.callback") 1256 unittest { 1257 auto gen = getGenerator(q{ 1258 typedef double DOMHighResTimeStamp; 1259 callback FrameRequestCallback = void (DOMHighResTimeStamp time); 1260 1261 interface AnimationFrameProvider { 1262 unsigned long requestAnimationFrame(FrameRequestCallback callback); 1263 }; 1264 }); 1265 gen.generateDBindings.shouldBeLike(q{ 1266 struct AnimationFrameProvider { 1267 nothrow: 1268 JsHandle handle; 1269 alias handle this; 1270 this(Handle h) { 1271 this.handle = JsHandle(h); 1272 } 1273 auto requestAnimationFrame()(FrameRequestCallback callback) { 1274 return AnimationFrameProvider_requestAnimationFrame(this.handle, callback); 1275 } 1276 } 1277 alias DOMHighResTimeStamp = double; 1278 alias FrameRequestCallback = void delegate(double); 1279 }); 1280 gen.generateDImports.shouldBeLike(q{ 1281 extern (C) uint AnimationFrameProvider_requestAnimationFrame(Handle, FrameRequestCallback); 1282 }); 1283 gen.generateJsExports.shouldBeLike(" 1284 AnimationFrameProvider_requestAnimationFrame: (ctx, callbackCtx, callbackPtr) => { 1285 setupMemory(); 1286 return objects[ctx].requestAnimationFrame((time)=>{spasm_indirect_function_get(callbackPtr)(callbackCtx, time)}); 1287 },"); 1288 } 1289 1290 @("typedef.interface") 1291 unittest { 1292 auto gen = getGenerator(q{ 1293 interface TextDecoder { 1294 USVString decode(optional BufferSource input, optional TextDecodeOptions options); 1295 BufferSource encode(optional TextDecodeOptions options); 1296 }; 1297 typedef (ArrayBufferView or ArrayBuffer) BufferSource; 1298 }); 1299 gen.generateDBindings.shouldBeLike(q{ 1300 alias BufferSource = SumType!(ArrayBufferView, ArrayBuffer); 1301 struct TextDecoder { 1302 nothrow: 1303 JsHandle handle; 1304 alias handle this; 1305 this(Handle h) { 1306 this.handle = JsHandle(h); 1307 } 1308 auto decode()(scope ref BufferSource input, scope ref TextDecodeOptions options) { 1309 return TextDecoder_decode(this.handle, input, options.handle); 1310 } 1311 auto decode()(scope ref BufferSource input) { 1312 return TextDecoder_decode_0(this.handle, input); 1313 } 1314 auto decode()() { 1315 return TextDecoder_decode_1(this.handle); 1316 } 1317 auto encode()(scope ref TextDecodeOptions options) { 1318 return TextDecoder_encode(this.handle, options.handle); 1319 } 1320 auto encode()() { 1321 return TextDecoder_encode_0(this.handle); 1322 } 1323 } 1324 }); 1325 gen.generateDImports.shouldBeLike(q{ 1326 extern (C) string TextDecoder_decode(Handle, scope ref BufferSource, Handle); 1327 extern (C) string TextDecoder_decode_0(Handle, scope ref BufferSource); 1328 extern (C) string TextDecoder_decode_1(Handle); 1329 extern (C) BufferSource TextDecoder_encode(Handle, Handle); 1330 extern (C) BufferSource TextDecoder_encode_0(Handle); 1331 }); 1332 gen.generateJsExports.shouldBeLike(" 1333 TextDecoder_decode: (rawResult, ctx, input, options) => { 1334 setupMemory(); 1335 spasm_encode_string(rawResult, objects[ctx].decode(spasm_decode_BufferSource(input), objects[options])); 1336 }, 1337 TextDecoder_decode_0: (rawResult, ctx, input) => { 1338 setupMemory(); 1339 spasm_encode_string(rawResult, objects[ctx].decode(spasm_decode_BufferSource(input))); 1340 }, 1341 TextDecoder_decode_1: (rawResult, ctx) => { 1342 setupMemory(); 1343 spasm_encode_string(rawResult, objects[ctx].decode()); 1344 }, 1345 TextDecoder_encode: (rawResult, ctx, options) => { 1346 setupMemory(); 1347 spasm_encode_BufferSource(rawResult, objects[ctx].encode(objects[options])); 1348 }, 1349 TextDecoder_encode_0: (rawResult, ctx) => { 1350 setupMemory(); 1351 spasm_encode_BufferSource(rawResult, objects[ctx].encode()); 1352 }, 1353 "); 1354 } 1355 1356 @("typedef.nullable") 1357 unittest { 1358 auto gen = getGenerator(q{ 1359 typedef (Blob or BufferSource or FormData or URLSearchParams or ReadableStream or USVString) BodyInit; 1360 dictionary RequestInit { 1361 BodyInit? body; 1362 }; 1363 }); 1364 gen.generateDBindings.shouldBeLike(q{ 1365 alias BodyInit = SumType!(Blob, BufferSource, FormData, URLSearchParams, ReadableStream, string); 1366 struct RequestInit { 1367 nothrow: 1368 JsHandle handle; 1369 alias handle this; 1370 this(Handle h) { 1371 this.handle = JsHandle(h); 1372 } 1373 static auto create() { 1374 return RequestInit(spasm_add__object()); 1375 } 1376 void body_(T0)(scope auto ref Optional!(T0) body_) if (isTOrPointer!(T0, BodyInit)) { 1377 RequestInit_body_Set(this.handle, !body_.empty, *body_.frontRef); 1378 } 1379 auto body_()() { 1380 return RequestInit_body_Get(this.handle); 1381 } 1382 } 1383 }); 1384 gen.generateDImports.shouldBeLike(q{ 1385 extern (C) void RequestInit_body_Set(Handle, bool, scope ref BodyInit); 1386 extern (C) Optional!(BodyInit) RequestInit_body_Get(Handle); 1387 }); 1388 gen.generateJsExports.shouldBeLike(" 1389 RequestInit_body_Set: (ctx, bodyDefined, body) => { 1390 setupMemory(); 1391 objects[ctx].body = bodyDefined ? spasm_decode_BodyInit(body) : undefined; 1392 }, 1393 RequestInit_body_Get: (rawResult, ctx) => { 1394 setupMemory(); 1395 spasm_encode_optional_BodyInit(rawResult, objects[ctx].body); 1396 }, 1397 "); 1398 } 1399 1400 1401 @("sequence.nullable") 1402 unittest { 1403 auto gen = getGenerator(q{ 1404 interface Foo { 1405 sequence<long>? bar(sequence<DOMString> names); 1406 }; 1407 }); 1408 gen.generateDBindings.shouldBeLike(q{ 1409 struct Foo { 1410 nothrow: 1411 JsHandle handle; 1412 alias handle this; 1413 this(Handle h) { 1414 this.handle = JsHandle(h); 1415 } 1416 auto bar()(scope ref Sequence!(string) names) { 1417 return Foo_bar(this.handle, names.handle); 1418 } 1419 } 1420 }); 1421 gen.generateDImports.shouldBeLike(q{ 1422 extern (C) Optional!(Sequence!(int)) Foo_bar(Handle, Handle); 1423 }); 1424 gen.generateJsExports.shouldBeLike(" 1425 Foo_bar: (rawResult, ctx, names) => { 1426 setupMemory(); 1427 spasm_encode_optional_sequence(rawResult, objects[ctx].bar(objects[names])); 1428 }, 1429 "); 1430 } 1431 1432 @("return.nullable") 1433 unittest { 1434 auto gen = getGenerator(q{ 1435 interface Foo { 1436 getter File? item(unsigned long index); 1437 }; 1438 }); 1439 // TODO: also opIndex 1440 gen.generateDBindings.shouldBeLike(q{ 1441 struct Foo { 1442 nothrow: 1443 JsHandle handle; 1444 alias handle this; 1445 this(Handle h) { 1446 this.handle = JsHandle(h); 1447 } 1448 auto item()(uint index) { 1449 return Foo_item_getter(this.handle, index); 1450 } 1451 } 1452 }); 1453 gen.generateDImports.shouldBeLike(q{ 1454 extern (C) Optional!(File) Foo_item_getter(Handle, uint); 1455 }); 1456 gen.generateJsExports.shouldBeLike(" 1457 Foo_item_getter: (rawResult, ctx, index) => { 1458 setupMemory(); 1459 spasm_encode_optional_Handle(rawResult, objects[ctx].item(index)); 1460 }, 1461 "); 1462 } 1463 1464 @("partial.mixin") 1465 unittest { 1466 auto gen = getGenerator(q{ 1467 interface mixin Foo { 1468 DOMString name(); 1469 readonly attribute boolean fatal; 1470 }; 1471 partial interface Foo { 1472 long long size(); 1473 }; 1474 interface Bar { 1475 }; 1476 Bar includes Foo; 1477 }); 1478 gen.generateDBindings.shouldBeLike(q{ 1479 struct Bar { 1480 nothrow: 1481 JsHandle handle; 1482 alias handle this; 1483 this(Handle h) { 1484 this.handle = JsHandle(h); 1485 } 1486 auto name()() { 1487 return Foo_name(this.handle); 1488 } 1489 auto fatal()() { 1490 return Foo_fatal_Get(this.handle); 1491 } 1492 auto size()() { 1493 return Foo_size(this.handle); 1494 } 1495 } 1496 }); 1497 gen.generateDImports.shouldBeLike(q{ 1498 extern (C) string Foo_name(Handle); 1499 extern (C) bool Foo_fatal_Get(Handle); 1500 extern (C) int Foo_size(Handle); 1501 }); 1502 gen.generateJsExports.shouldBeLike(" 1503 Foo_name: (rawResult, ctx) => { 1504 setupMemory(); 1505 spasm_encode_string(rawResult, objects[ctx].name()); 1506 }, 1507 Foo_fatal_Get: (ctx) => { 1508 setupMemory(); 1509 return objects[ctx].fatal; 1510 }, 1511 Foo_size: (ctx) => { 1512 setupMemory(); 1513 return objects[ctx].size(); 1514 }, 1515 "); 1516 } 1517 1518 1519 @("namespace") 1520 unittest { 1521 auto gen = getGenerator(q{ 1522 namespace console { 1523 void clear(); 1524 }; 1525 }); 1526 auto funcs = gen.semantics.toIr(); 1527 gen.generateDBindings.shouldBeLike(q{ 1528 struct console { 1529 nothrow: 1530 static: 1531 void clear()() { 1532 console_clear(); 1533 } 1534 } 1535 }); 1536 gen.generateDImports.shouldBeLike(q{ 1537 extern (C) void console_clear(); 1538 }); 1539 gen.generateJsExports.shouldBeLike(" 1540 console_clear: () => { 1541 setupMemory(); 1542 console.clear(); 1543 }, 1544 "); 1545 } 1546 1547 @("callback.sumtype") 1548 unittest { 1549 auto gen = getGenerator(q{ 1550 callback OnErrorEventHandlerNonNull = any ((Event or DOMString) event, optional DOMString source, optional unsigned long lineno, optional unsigned long colno, optional any error); 1551 }); 1552 gen.generateDBindings.shouldBeLike(q{ 1553 alias OnErrorEventHandlerNonNull = Any delegate(SumType!(Event, string), string, uint, uint, Any); 1554 }); 1555 } 1556 1557 @("module.imports.mixin") 1558 unittest { 1559 auto semantics = new Semantics(); 1560 auto documentA = WebIDL(q{ 1561 interface Foo { 1562 }; 1563 Foo includes Bar; 1564 }); 1565 auto documentB = WebIDL(q{ 1566 interface mixin Bar { 1567 Hup get(); 1568 }; 1569 interface Hup { 1570 }; 1571 }); 1572 documentA.successful.shouldBeTrue; 1573 documentB.successful.shouldBeTrue; 1574 auto moduleA = semantics.analyse("a",documentA); 1575 auto moduleB = semantics.analyse("b",documentB); 1576 auto ir = semantics.toIr(); 1577 ir.getImports(moduleA).shouldEqual(["import spasm.bindings.b;"]); 1578 ir.getImports(moduleB).shouldEqual([]); 1579 } 1580 1581 @("module.imports.mixin.indirect") 1582 unittest { 1583 auto semantics = new Semantics(); 1584 auto documentA = WebIDL(q{ 1585 interface Foo { 1586 }; 1587 Foo includes Bar; 1588 }); 1589 auto documentB = WebIDL(q{ 1590 interface mixin Bar { 1591 Hup get(); 1592 }; 1593 }); 1594 auto documentC = WebIDL(q{ 1595 interface Hup { 1596 }; 1597 }); 1598 documentA.successful.shouldBeTrue; 1599 documentB.successful.shouldBeTrue; 1600 documentC.successful.shouldBeTrue; 1601 auto moduleA = semantics.analyse("a",documentA); 1602 auto moduleB = semantics.analyse("b",documentB); 1603 auto moduleC = semantics.analyse("c",documentC); 1604 auto ir = semantics.toIr(); 1605 ir.getImports(moduleA).shouldEqual(["import spasm.bindings.b;","import spasm.bindings.c;"]); 1606 ir.getImports(moduleB).shouldEqual(["import spasm.bindings.c;"]); 1607 ir.generateDImports(moduleB).shouldBeLike("extern (C) Handle Bar_get(Handle);"); 1608 } 1609 1610 @("module.imports.partial") 1611 unittest { 1612 auto semantics = new Semantics(); 1613 auto documentA = WebIDL(q{ 1614 interface Foo { 1615 }; 1616 }); 1617 auto documentB = WebIDL(q{ 1618 partial interface Foo { 1619 Hup get(); 1620 }; 1621 interface Hup { 1622 }; 1623 }); 1624 documentA.successful.shouldBeTrue; 1625 documentB.successful.shouldBeTrue; 1626 auto moduleA = semantics.analyse("a",documentA); 1627 auto moduleB = semantics.analyse("b",documentB); 1628 auto ir = semantics.toIr(); 1629 ir.getImports(moduleA).shouldEqual(["import spasm.bindings.b;"]); 1630 ir.getImports(moduleB).shouldEqual([]); 1631 } 1632 1633 @("module.imports.partial.indirect") 1634 unittest { 1635 auto semantics = new Semantics(); 1636 auto documentA = WebIDL(q{ 1637 interface Foo { 1638 }; 1639 }); 1640 auto documentB = WebIDL(q{ 1641 partial interface Foo { 1642 Hup get(); 1643 }; 1644 }); 1645 auto documentC = WebIDL(q{ 1646 interface Hup { 1647 }; 1648 }); 1649 documentA.successful.shouldBeTrue; 1650 documentB.successful.shouldBeTrue; 1651 documentC.successful.shouldBeTrue; 1652 auto moduleA = semantics.analyse("a",documentA); 1653 auto moduleB = semantics.analyse("b",documentB); 1654 auto moduleC = semantics.analyse("c",documentC); 1655 auto ir = semantics.toIr(); 1656 ir.generateDImports(moduleA).shouldBeLike("extern (C) Handle Foo_get(Handle);"); 1657 ir.getImports(moduleA).shouldEqual(["import spasm.bindings.c;"]); 1658 // TODO: we don't need to import c 1659 ir.getImports(moduleB).shouldEqual(["import spasm.bindings.c;"]); 1660 } 1661 1662 @("any") 1663 unittest { 1664 auto gen = getGenerator(q{ 1665 [Exposed=(Window,Worker,Worklet)] 1666 namespace foo { 1667 void log(any data); 1668 any get(); 1669 }; 1670 }); 1671 auto funcs = gen.semantics.toIr(); 1672 gen.generateDBindings.shouldBeLike(q{ 1673 struct foo { 1674 nothrow: 1675 static: 1676 void log(T0)(scope auto ref T0 data) { 1677 Handle _handle_data = getOrCreateHandle(data); 1678 foo_log(_handle_data); 1679 dropHandle!(T0)(_handle_data); 1680 } 1681 auto get()() { 1682 return Any(foo_get()); 1683 } 1684 } 1685 }); 1686 gen.generateDImports.shouldBeLike(q{ 1687 extern (C) void foo_log(Handle); 1688 extern (C) Handle foo_get(); 1689 }); 1690 gen.generateJsExports.shouldBeLike(" 1691 foo_log: (data) => { 1692 setupMemory(); 1693 foo.log(objects[data]); 1694 }, 1695 foo_get: () => { 1696 setupMemory(); 1697 return addObject(foo.get()); 1698 }, 1699 "); 1700 } 1701 1702 @("optional.typedef") 1703 unittest { 1704 auto gen = getGenerator(q{ 1705 callback OnErrorEventHandlerNonNull = any ((Event or DOMString) event, optional DOMString source, optional unsigned long lineno, optional unsigned long colno, optional any error); 1706 typedef OnErrorEventHandlerNonNull? OnErrorEventHandler; 1707 interface WorkerGlobalScope : EventTarget { 1708 attribute OnErrorEventHandler onerror; 1709 }; 1710 }); 1711 gen.generateDBindings.shouldBeLike(q{ 1712 alias OnErrorEventHandler = Optional!(OnErrorEventHandlerNonNull); 1713 alias OnErrorEventHandlerNonNull = Any delegate(SumType!(Event, string), string, uint, uint, Any); 1714 struct WorkerGlobalScope { 1715 nothrow: 1716 EventTarget _parent; 1717 alias _parent this; 1718 this(Handle h) { 1719 _parent = .EventTarget(h); 1720 } 1721 void onerror(T0)(scope auto ref Optional!(T0) onerror) if (isTOrPointer!(T0, OnErrorEventHandler)) { 1722 WorkerGlobalScope_onerror_Set(this._parent, !onerror.empty, onerror.front); 1723 } 1724 auto onerror()() { 1725 return WorkerGlobalScope_onerror_Get(this._parent); 1726 } 1727 } 1728 }); 1729 gen.generateDImports.shouldBeLike(q{ 1730 extern (C) void WorkerGlobalScope_onerror_Set(Handle, bool, OnErrorEventHandlerNonNull); 1731 extern (C) OnErrorEventHandler WorkerGlobalScope_onerror_Get(Handle); 1732 }); 1733 gen.generateJsExports.shouldBeLike(" 1734 WorkerGlobalScope_onerror_Set: (ctx, onerrorDefined, onerrorCtx, onerrorPtr) => { 1735 setupMemory(); 1736 objects[ctx].onerror = onerrorDefined ? (event, source, lineno, colno, error)=>{spasm_encode_union2_Event_string(0, event);spasm_encode_string(12, source);encode_handle(20, error);spasm_indirect_function_get(onerrorPtr)(24, onerrorCtx, 0, 12, lineno, colno, 20); return spasm_decode_Handle(24)} : undefined; 1737 }, 1738 WorkerGlobalScope_onerror_Get: (rawResult, ctx) => { 1739 setupMemory(); 1740 spasm_encode_optional_Handle(rawResult, objects[ctx].onerror); 1741 }, 1742 "); 1743 } 1744 1745 @("sumtype.nested") 1746 unittest { 1747 auto gen = getGenerator(q{ 1748 interface XMLHttpRequest : XMLHttpRequestEventTarget { 1749 void send(optional (Document or BodyInit)? body = null); 1750 }; 1751 typedef (Blob or BufferSource or FormData or URLSearchParams or ReadableStream or USVString) BodyInit; 1752 }); 1753 gen.generateDBindings.shouldBeLike(q{ 1754 alias BodyInit = SumType!(Blob, BufferSource, FormData, URLSearchParams, ReadableStream, string); 1755 struct XMLHttpRequest { 1756 nothrow: 1757 XMLHttpRequestEventTarget _parent; 1758 alias _parent this; 1759 this(Handle h) { 1760 _parent = .XMLHttpRequestEventTarget(h); 1761 } 1762 void send(T0)(scope auto ref Optional!(T0) body_ /* = no!(SumType!(Document, BodyInit)) */) if (isTOrPointer!(T0, SumType!(Document, BodyInit))) { 1763 XMLHttpRequest_send(this._parent, !body_.empty, *body_.frontRef); 1764 } 1765 void send()() { 1766 XMLHttpRequest_send_0(this._parent); 1767 } 1768 } 1769 }); 1770 gen.generateDImports.shouldBeLike(q{ 1771 extern (C) void XMLHttpRequest_send(Handle, bool, scope ref SumType!(Document, BodyInit)); 1772 extern (C) void XMLHttpRequest_send_0(Handle); 1773 }); 1774 gen.generateJsExports.shouldBeLike(" 1775 XMLHttpRequest_send: (ctx, bodyDefined, body) => { 1776 setupMemory(); 1777 objects[ctx].send(bodyDefined ? spasm_decode_union2_Document_BodyInit(body) : undefined); 1778 }, 1779 XMLHttpRequest_send_0: (ctx) => { 1780 setupMemory(); 1781 objects[ctx].send(); 1782 }, 1783 "); 1784 } 1785 1786 @("inheritance.mixin") 1787 unittest { 1788 auto gen = getGenerator(q{ 1789 interface mixin GenericTransformStream { 1790 readonly attribute WritableStream writable; 1791 }; 1792 interface TextDecoderStream : Foo { 1793 }; 1794 TextDecoderStream includes GenericTransformStream; 1795 }); 1796 gen.generateDBindings.shouldBeLike(q{ 1797 struct TextDecoderStream { 1798 nothrow: 1799 Foo _parent; 1800 alias _parent this; 1801 this(Handle h) { 1802 _parent = .Foo(h); 1803 } 1804 auto writable()() { 1805 return WritableStream(GenericTransformStream_writable_Get(this._parent)); 1806 } 1807 } 1808 }); 1809 gen.generateDImports.shouldBeLike(q{ 1810 extern (C) Handle GenericTransformStream_writable_Get(Handle); 1811 }); 1812 gen.generateJsExports.shouldBeLike(" 1813 GenericTransformStream_writable_Get: (ctx) => { 1814 setupMemory(); 1815 return addObject(objects[ctx].writable); 1816 }, 1817 "); 1818 } 1819 1820 @("exposed.constructor.overloads") 1821 unittest { 1822 auto gen = getGenerator(q{ 1823 [Constructor(unsigned long sw, unsigned long sh), 1824 Constructor(Uint8ClampedArray data, unsigned long sw, optional unsigned long sh), 1825 Exposed=(Window), 1826 Serializable] 1827 interface ImageData { 1828 }; 1829 interface Window { 1830 void stuff((ImageData or string) s); 1831 }; 1832 }); 1833 gen.generateDBindings.shouldBeLike(q{ 1834 struct ImageData { 1835 nothrow: 1836 JsHandle handle; 1837 alias handle this; 1838 this(Handle h) { 1839 this.handle = JsHandle(h); 1840 } 1841 } 1842 struct Window { 1843 nothrow: 1844 JsHandle handle; 1845 alias handle this; 1846 this(Handle h) { 1847 this.handle = JsHandle(h); 1848 } 1849 void stuff()(scope ref SumType!(.ImageData, string) s) { 1850 Window_stuff(this.handle, s); 1851 } 1852 auto ImageData()(uint sw, uint sh) { 1853 return .ImageData(Window_ImageData__uint_uint(this.handle, sw, sh)); 1854 } 1855 auto ImageData()(scope ref Uint8ClampedArray data, uint sw, uint sh) { 1856 return .ImageData(Window_ImageData__Handle_uint_uint(this.handle, data.handle, sw, sh)); 1857 } 1858 } 1859 }); 1860 gen.generateDImports.shouldBeLike(q{ 1861 extern (C) void Window_stuff(Handle, scope ref SumType!(ImageData, string)); 1862 extern (C) Handle Window_ImageData__uint_uint(Handle, uint, uint); 1863 extern (C) Handle Window_ImageData__Handle_uint_uint(Handle, Handle, uint, uint); 1864 }); 1865 gen.generateJsExports.shouldBeLike(" 1866 Window_stuff: (ctx, s) => { 1867 setupMemory(); 1868 objects[ctx].stuff(spasm_decode_union2_ImageData_string(s)); 1869 }, 1870 Window_ImageData__uint_uint: (ctx, sw, sh) => { 1871 setupMemory(); 1872 return addObject(new objects[ctx].ImageData(sw, sh)); 1873 }, 1874 Window_ImageData__Handle_uint_uint: (ctx, data, sw, sh) => { 1875 setupMemory(); 1876 return addObject(new objects[ctx].ImageData(objects[data], sw, sh)); 1877 }, 1878 "); 1879 gen.generateJsDecoders.shouldBeLike(" 1880 spasm_decode_Handle = decode_handle, 1881 spasm_decode_union2_ImageData_string = (ptr)=>{ 1882 if (getUInt(ptr) == 0) { 1883 return spasm_decode_Handle(ptr+4); 1884 } else if (getUInt(ptr) == 1) { 1885 return spasm_decode_Handle(ptr+4); 1886 } 1887 }"); 1888 } 1889 1890 @("mixin.partial") 1891 unittest { 1892 auto gen = getGenerator(q{ 1893 callback EventHandlerNonNull = any (Event event); 1894 typedef EventHandlerNonNull EventHandler; 1895 interface mixin GlobalEventHandlers { 1896 attribute EventHandler onabort; 1897 }; 1898 partial interface GlobalEventHandlers { 1899 attribute EventHandler ongotpointercapture; 1900 }; 1901 partial interface GlobalEventHandlers { 1902 attribute EventHandler ontouchstart; 1903 }; 1904 interface Window { 1905 }; 1906 Window includes GlobalEventHandlers; 1907 }); 1908 gen.generateDBindings.shouldBeLike(q{ 1909 alias EventHandler = EventHandlerNonNull; 1910 alias EventHandlerNonNull = Any delegate(Event); 1911 struct Window { 1912 nothrow: 1913 JsHandle handle; 1914 alias handle this; 1915 this(Handle h) { 1916 this.handle = JsHandle(h); 1917 } 1918 void onabort()(EventHandler onabort) { 1919 GlobalEventHandlers_onabort_Set(this.handle, onabort); 1920 } 1921 auto onabort()() { 1922 return GlobalEventHandlers_onabort_Get(this.handle); 1923 } 1924 void ongotpointercapture()(EventHandler ongotpointercapture) { 1925 GlobalEventHandlers_ongotpointercapture_Set(this.handle, ongotpointercapture); 1926 } 1927 auto ongotpointercapture()() { 1928 return GlobalEventHandlers_ongotpointercapture_Get(this.handle); 1929 } 1930 void ontouchstart()(EventHandler ontouchstart) { 1931 GlobalEventHandlers_ontouchstart_Set(this.handle, ontouchstart); 1932 } 1933 auto ontouchstart()() { 1934 return GlobalEventHandlers_ontouchstart_Get(this.handle); 1935 } 1936 } 1937 }); 1938 gen.generateDImports.shouldBeLike(q{ 1939 extern (C) void GlobalEventHandlers_onabort_Set(Handle, EventHandler); 1940 extern (C) EventHandler GlobalEventHandlers_onabort_Get(Handle); 1941 extern (C) void GlobalEventHandlers_ongotpointercapture_Set(Handle, EventHandler); 1942 extern (C) EventHandler GlobalEventHandlers_ongotpointercapture_Get(Handle); 1943 extern (C) void GlobalEventHandlers_ontouchstart_Set(Handle, EventHandler); 1944 extern (C) EventHandler GlobalEventHandlers_ontouchstart_Get(Handle); }); 1945 gen.generateJsExports.shouldBeLike(" 1946 GlobalEventHandlers_onabort_Set: (ctx, onabortCtx, onabortPtr) => { 1947 setupMemory(); 1948 objects[ctx].onabort = (event)=>{encode_handle(0, event);spasm_indirect_function_get(onabortPtr)(4, onabortCtx, 0); return spasm_decode_Handle(4)}; 1949 }, 1950 GlobalEventHandlers_onabort_Get: (ctx) => { 1951 setupMemory(); 1952 return objects[ctx].onabort; 1953 }, 1954 GlobalEventHandlers_ongotpointercapture_Set: (ctx, ongotpointercaptureCtx, ongotpointercapturePtr) => { 1955 setupMemory(); 1956 objects[ctx].ongotpointercapture = (event)=>{encode_handle(0, event);spasm_indirect_function_get(ongotpointercapturePtr)(4, ongotpointercaptureCtx, 0); return spasm_decode_Handle(4)}; 1957 }, 1958 GlobalEventHandlers_ongotpointercapture_Get: (ctx) => { 1959 setupMemory(); 1960 return objects[ctx].ongotpointercapture; 1961 }, 1962 GlobalEventHandlers_ontouchstart_Set: (ctx, ontouchstartCtx, ontouchstartPtr) => { 1963 setupMemory(); 1964 objects[ctx].ontouchstart = (event)=>{encode_handle(0, event);spasm_indirect_function_get(ontouchstartPtr)(4, ontouchstartCtx, 0); return spasm_decode_Handle(4)}; 1965 }, 1966 GlobalEventHandlers_ontouchstart_Get: (ctx) => { 1967 setupMemory(); 1968 return objects[ctx].ontouchstart; 1969 }, 1970 "); 1971 gen.generateJsDecoders.shouldBeLike("spasm_decode_Handle = decode_handle"); 1972 } 1973 1974 @("decode.sequence") 1975 unittest { 1976 auto gen = getGenerator(q{ 1977 [Constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols = []), Exposed=(Window)] 1978 interface WebSocket : EventTarget { 1979 }; 1980 interface Window { 1981 }; 1982 }); 1983 gen.generateDBindings.shouldBeLike(q{ 1984 struct WebSocket { 1985 nothrow: 1986 EventTarget _parent; 1987 alias _parent this; 1988 this(Handle h) { 1989 _parent = .EventTarget(h); 1990 } 1991 } 1992 struct Window { 1993 nothrow: 1994 JsHandle handle; 1995 alias handle this; 1996 this(Handle h) { 1997 this.handle = JsHandle(h); 1998 } 1999 auto WebSocket()(string url, scope ref SumType!(string, Sequence!(string)) protocols /* = [] */) { 2000 return .WebSocket(Window_WebSocket(this.handle, url, protocols)); 2001 } 2002 } 2003 }); 2004 gen.generateDImports.shouldBeLike(q{ 2005 extern (C) Handle Window_WebSocket(Handle, string, scope ref SumType!(string, Sequence!(string))); 2006 }); 2007 gen.generateJsExports.shouldBeLike(" 2008 Window_WebSocket: (ctx, urlLen, urlPtr, protocols) => { 2009 setupMemory(); 2010 return addObject(new objects[ctx].WebSocket(spasm_decode_string(urlLen, urlPtr), spasm_decode_union2_string_sequence(protocols))); 2011 }, 2012 "); 2013 gen.generateJsDecoders.should == "spasm_decode_sequence = decode_handle, 2014 spasm_decode_union2_string_sequence = (ptr)=>{ 2015 if (getUInt(ptr) == 0) { 2016 return spasm_decode_string(ptr+4); 2017 } else if (getUInt(ptr) == 1) { 2018 return spasm_decode_sequence(ptr+4); 2019 } 2020 }"; 2021 } 2022 2023 @("abi.callback.return") 2024 unittest { 2025 auto gen = getGenerator(q{ 2026 callback AnyCallback = any (Event event); 2027 callback IntCallback = int (Event event); 2028 callback StringCallback = string (Event event); 2029 callback InterfaceCallback = WebSocket (Event event); 2030 callback VoidCallback = void (Event event); 2031 interface WebSocket {}; 2032 interface Window { 2033 attribute AnyCallback onany; 2034 attribute IntCallback onint; 2035 attribute stringCallback onstring; 2036 attribute interfaceCallback oninterface; 2037 attribute VoidCallback onvoid; 2038 }; 2039 }); 2040 gen.generateJsExports.shouldBeLike(" 2041 Window_onany_Set: (ctx, onanyCtx, onanyPtr) => { 2042 setupMemory(); 2043 objects[ctx].onany = (event)=>{encode_handle(0, event);spasm_indirect_function_get(onanyPtr)(4, onanyCtx, 0); return spasm_decode_Handle(4)}; 2044 }, 2045 Window_onany_Get: (ctx) => { 2046 setupMemory(); 2047 return objects[ctx].onany; 2048 }, 2049 Window_onint_Set: (ctx, onintCtx, onintPtr) => { 2050 setupMemory(); 2051 objects[ctx].onint = (event)=>{encode_handle(0, event);return spasm_indirect_function_get(onintPtr)(onintCtx, 0)}; 2052 }, 2053 Window_onint_Get: (ctx) => { 2054 setupMemory(); 2055 return objects[ctx].onint; 2056 }, 2057 Window_onstring_Set: (ctx, onstring) => { 2058 setupMemory(); 2059 objects[ctx].onstring = objects[onstring]; 2060 }, 2061 Window_onstring_Get: (ctx) => { 2062 setupMemory(); 2063 return addObject(objects[ctx].onstring); 2064 }, 2065 Window_oninterface_Set: (ctx, oninterface) => { 2066 setupMemory(); 2067 objects[ctx].oninterface = objects[oninterface]; 2068 }, 2069 Window_oninterface_Get: (ctx) => { 2070 setupMemory(); 2071 return addObject(objects[ctx].oninterface); 2072 }, 2073 Window_onvoid_Set: (ctx, onvoidCtx, onvoidPtr) => { 2074 setupMemory(); 2075 objects[ctx].onvoid = (event)=>{encode_handle(0, event);spasm_indirect_function_get(onvoidPtr)(onvoidCtx, 0)}; 2076 }, 2077 Window_onvoid_Get: (ctx) => { 2078 setupMemory(); 2079 return objects[ctx].onvoid; 2080 }, 2081 "); 2082 }