@@ -467,6 +467,11 @@ public void defineOwnPropertyOutOfBoundsWriteIsDiscarded() throws Exception {
467467 loadPageVerifyTitle2 (html );
468468 }
469469
470+ /**
471+ * Test common workaround for subclassing built-in objects (like Set) without using ES6
472+ * class/extends syntax, by using Reflect.construct() with a newTarget argument.
473+ * @throws Exception if the test fails
474+ */
470475 @ Test
471476 @ Alerts ({"1,2,3,4" , "true" , "true" , "true" })
472477 public void constructSubclassBuiltin () throws Exception {
@@ -491,4 +496,196 @@ public void constructSubclassBuiltin() throws Exception {
491496
492497 loadPageVerifyTitle2 (html );
493498 }
499+
500+ /**
501+ * Test common workaround for subclassing built-in objects (like Map) without using ES6
502+ * class/extends syntax, by using Reflect.construct() with a newTarget argument.
503+ * @throws Exception if the test fails
504+ */
505+ @ Test
506+ @ Alerts ({"a,b,c" , "true" , "true" , "true" })
507+ public void constructSubclassBuiltinMap () throws Exception {
508+ final String html = DOCTYPE_HTML
509+ + "<html></head>\n "
510+ + "<body>"
511+ + "<script>\n "
512+ + LOG_TITLE_FUNCTION
513+ + "function CustomMap() {\n "
514+ + " return Reflect.construct(Map, arguments, this.constructor);\n "
515+ + "}\n "
516+ + "CustomMap.prototype = Object.create(Map.prototype);\n "
517+ + "CustomMap.prototype.constructor = CustomMap;\n "
518+ + "var map = new CustomMap([['a', 1], ['b', 2]]);\n "
519+ + "map.set('c', 3);\n "
520+ + "log(Array.from(map.keys()));\n "
521+ + "log(map instanceof CustomMap);\n "
522+ + "log(map instanceof Map);\n "
523+ + "log(Object.getPrototypeOf(map) === CustomMap.prototype);\n "
524+ + "</script>\n "
525+ + "</body></html>" ;
526+
527+ loadPageVerifyTitle2 (html );
528+ }
529+
530+ /**
531+ * Test the same workaround for Array, which additionally propagates the subclass through
532+ * derived methods (map/filter/slice/...) via Symbol.species, and has exotic "length" and
533+ * isArray behavior that Set/Map don't.
534+ * @throws Exception if the test fails
535+ */
536+ @ Test
537+ @ Alerts ({"1,2,3" , "true" , "true" , "true" , "false" , "true" })
538+ public void constructSubclassBuiltinArray () throws Exception {
539+ final String html = DOCTYPE_HTML
540+ + "<html></head>\n "
541+ + "<body>"
542+ + "<script>\n "
543+ + LOG_TITLE_FUNCTION
544+ + "function CustomArray() {\n "
545+ + " return Reflect.construct(Array, arguments, this.constructor);\n "
546+ + "}\n "
547+ + "CustomArray.prototype = Object.create(Array.prototype);\n "
548+ + "CustomArray.prototype.constructor = CustomArray;\n "
549+ + "var arr = new CustomArray(1, 2, 3);\n "
550+ + "var mapped = arr.map(function(x) { return x * 2; });\n "
551+ + "log(Array.from(arr));\n "
552+ + "log(arr instanceof CustomArray);\n "
553+ + "log(arr instanceof Array);\n "
554+ + "log(Array.isArray(arr));\n "
555+ + "log(mapped instanceof CustomArray);\n "
556+ + "log(Object.getPrototypeOf(arr) === CustomArray.prototype);\n "
557+ + "</script>\n "
558+ + "</body></html>" ;
559+
560+ loadPageVerifyTitle2 (html );
561+ }
562+
563+ /**
564+ * Test the workaround for WeakSet, which has no Symbol.iterator/Array.from support, so
565+ * membership must be verified via has() instead of reading contents out.
566+ * @throws Exception if the test fails
567+ */
568+ @ Test
569+ @ Alerts ({"true" , "true" , "true" , "true" })
570+ public void constructSubclassBuiltinWeakSet () throws Exception {
571+ final String html = DOCTYPE_HTML
572+ + "<html></head>\n "
573+ + "<body>"
574+ + "<script>\n "
575+ + LOG_TITLE_FUNCTION
576+ + "function CustomWeakSet() {\n "
577+ + " return Reflect.construct(WeakSet, arguments, this.constructor);\n "
578+ + "}\n "
579+ + "CustomWeakSet.prototype = Object.create(WeakSet.prototype);\n "
580+ + "CustomWeakSet.prototype.constructor = CustomWeakSet;\n "
581+ + "var key = {};\n "
582+ + "var ws = new CustomWeakSet([key]);\n "
583+ + "ws.add({});\n "
584+ + "log(ws.has(key));\n "
585+ + "log(ws instanceof CustomWeakSet);\n "
586+ + "log(ws instanceof WeakSet);\n "
587+ + "log(Object.getPrototypeOf(ws) === CustomWeakSet.prototype);\n "
588+ + "</script>\n "
589+ + "</body></html>" ;
590+
591+ loadPageVerifyTitle2 (html );
592+ }
593+
594+ /**
595+ * Test the workaround for a typed array (Uint8Array), which is species-driven like Array
596+ * (slice/subarray return instances via Symbol.species) and has an overloaded constructor
597+ * signature (length vs buffer vs iterable) that Set/Map don't have.
598+ * @throws Exception if the test fails
599+ */
600+ @ Test
601+ @ Alerts ({"1,2,3" , "true" , "false" , "true" , "true" })
602+ public void constructSubclassBuiltinTypedArray () throws Exception {
603+ final String html = DOCTYPE_HTML
604+ + "<html></head>\n "
605+ + "<body>"
606+ + "<script>\n "
607+ + LOG_TITLE_FUNCTION
608+ + "function CustomUint8Array() {\n "
609+ + " return Reflect.construct(Uint8Array, arguments, this.constructor);\n "
610+ + "}\n "
611+ + "CustomUint8Array.prototype = Object.create(Uint8Array.prototype);\n "
612+ + "CustomUint8Array.prototype.constructor = CustomUint8Array;\n "
613+ + "var ta = new CustomUint8Array([1, 2, 3]);\n "
614+ + "var sliced = ta.slice(1);\n "
615+ + "log(Array.from(ta));\n "
616+ + "log(Object.getPrototypeOf(ta) === CustomUint8Array.prototype);\n "
617+ + "log(Object.getPrototypeOf(ta) === Uint8Array.prototype);\n "
618+ + "log(Object.getPrototypeOf(sliced) === Uint8Array.prototype);\n "
619+ + "log(Object.getPrototypeOf(sliced) !== CustomUint8Array.prototype);\n "
620+ + "</script>\n "
621+ + "</body></html>" ;
622+
623+ loadPageVerifyTitle2 (html );
624+ }
625+
626+ /**
627+ * Test the workaround applied to Proxy, which is expected to diverge from Set/Map/Array: a
628+ * Proxy without a getPrototypeOf trap forwards [[GetPrototypeOf]] to its target rather than to
629+ * whatever prototype Reflect.construct's newTarget would otherwise assign, since ProxyCreate
630+ * ignores newTarget entirely. So the proxy's actual prototype must be Object.prototype
631+ * (inherited from its target, a plain object), and must NOT be CustomProxy.prototype, even
632+ * though construction otherwise looks identical to the Set/Map/Array cases.
633+ * @throws Exception if the test fails
634+ */
635+ @ Test
636+ @ Alerts ({"true" , "true" , "true" })
637+ public void constructSubclassBuiltinProxy () throws Exception {
638+ final String html = DOCTYPE_HTML
639+ + "<html></head>\n "
640+ + "<body>"
641+ + "<script>\n "
642+ + LOG_TITLE_FUNCTION
643+ + "function CustomProxy() {\n "
644+ + " return Reflect.construct(Proxy, arguments, this.constructor);\n "
645+ + "}\n "
646+ + "CustomProxy.prototype = Object.create(Proxy.prototype || Object.prototype);\n "
647+ + "CustomProxy.prototype.constructor = CustomProxy;\n "
648+ + "var target = {};\n "
649+ + "var p = new CustomProxy(target, {});\n "
650+ + "log(Object.getPrototypeOf(p) === Object.prototype);\n "
651+ + "log(Object.getPrototypeOf(p) === Object.getPrototypeOf(target));\n "
652+ + "log(Object.getPrototypeOf(p) !== CustomProxy.prototype);\n "
653+ + "</script>\n "
654+ + "</body></html>" ;
655+
656+ loadPageVerifyTitle2 (html );
657+ }
658+
659+ /**
660+ * Test the workaround for Error, included as a contrast case: unlike Set/Map/Array, naive
661+ * ES5-style inheritance (Error.call(this, message)) already gets most behavior "for free"
662+ * without Reflect.construct, since Error does not hold hidden internal slots the way the
663+ * collection/typed-array types do. This test documents that the Reflect.construct approach
664+ * still works, and still correctly wires up the prototype chain and instanceof checks.
665+ * @throws Exception if the test fails
666+ */
667+ @ Test
668+ @ Alerts ({"boom" , "true" , "true" , "true" })
669+ public void constructSubclassBuiltinError () throws Exception {
670+ final String html = DOCTYPE_HTML
671+ + "<html></head>\n "
672+ + "<body>"
673+ + "<script>\n "
674+ + LOG_TITLE_FUNCTION
675+ + "function CustomError() {\n "
676+ + " var err = Reflect.construct(Error, arguments, this.constructor);\n "
677+ + " return err;\n "
678+ + "}\n "
679+ + "CustomError.prototype = Object.create(Error.prototype);\n "
680+ + "CustomError.prototype.constructor = CustomError;\n "
681+ + "var err = new CustomError('boom');\n "
682+ + "log(err.message);\n "
683+ + "log(err instanceof CustomError);\n "
684+ + "log(err instanceof Error);\n "
685+ + "log(Object.getPrototypeOf(err) === CustomError.prototype);\n "
686+ + "</script>\n "
687+ + "</body></html>" ;
688+
689+ loadPageVerifyTitle2 (html );
690+ }
494691}
0 commit comments