Allen Wirfs-Brock (2015-04-29T22:32:42.000Z)
d at domenic.me (2015-05-11T16:51:01.211Z)
On Apr 29, 2015, at 2:36 PM, C. Scott Ananian wrote: > This is still not correct. > > You can't call `super.resolve(x)` safely unless you know that this.constructor is trustworthy. If you've bailed out of the `switch`, you don't know that. you're correct. Let's go with: ```js class DefensivePromise extends Promise { constructor(x) { super(x); if (new.target === DefensivePromise) { Object.freeze(this); } } static resolve(x) { if (this===DefensivePromise) { switch (true) { default: // assuming frozen primordial if (x.constructor !== DefensivePromise) break; //just a quick exit, doesn't guarantee much if (!Object.isFrozen(x)) break; If (Object.getOwnPropertyDescriptor(x, 'then')) break; //a more subclass friendly approach would walk x's [[Prototype]] chain to ensure that the correct 'then' is inherited from frozen prototypes if (Object.getPrototypeOf(x) !== DefensivePromise.prototype) break; //Assert: x.then === Promise.prototype.then, now and forever after return x; }; return new this(r => {r(x)}); } // must be called on a subclass of DefensivePromise, so we don't need to enforce the DefensivePromise 'then' invariant return super.resolve(x); } } Object.freeze(DefensivePromise); Object.freeze(DefensivePromise.prototype); ``` > > A correct implementation just does `return new this(r => { r(x); })` after the switch. > > The "another test" that you propose seems to be exactly the fix which Brandon proposed and I codified, to wit: > > ``` > 2. If IsPromise(x) is true, > a. Let constructor be the value of SpeciesConstructor(x, %Promise%) > b. If SameValue(constructor, C) is true, return x. > ``` That can't right. x could be an object that is not a C but whose SpeciesConstructor is C. In that case, a new C promise on x still has to be created. But your code would just return x. I would use a 'construtor' test in step 2. But if 'constructor' is not C and SpeciesConstructor is also not C then an extra level of C wrapping is needed.