call.js (6813B)
1 // |reftest| skip-if(!this.hasOwnProperty("Intl")) 2 3 function IsIntlService(c) { 4 return typeof c === "function" && 5 c.hasOwnProperty("prototype") && 6 c.prototype.hasOwnProperty("resolvedOptions"); 7 } 8 9 function IsObject(o) { 10 return Object(o) === o; 11 } 12 13 function IsPrimitive(o) { 14 return Object(o) !== o; 15 } 16 17 function thisValues() { 18 const intlConstructors = Object.getOwnPropertyNames(Intl).map(name => Intl[name]).filter(IsIntlService); 19 20 return [ 21 // Primitive values. 22 ...[undefined, null, true, "abc", Symbol(), 123], 23 24 // Object values. 25 ...[{}, [], /(?:)/, function(){}, new Proxy({}, {})], 26 27 // Intl objects. 28 ...[].concat(...intlConstructors.map(ctor => { 29 let args = []; 30 if (ctor === Intl.DisplayNames) { 31 // Intl.DisplayNames can't be constructed without any arguments. 32 args = [undefined, {type: "language"}]; 33 } 34 35 return [ 36 // Instance of an Intl constructor. 37 new ctor(...args), 38 39 // Instance of a subclassed Intl constructor. 40 new class extends ctor {}(...args), 41 42 // Object inheriting from an Intl constructor prototype. 43 Object.create(ctor.prototype), 44 45 // Intl object not inheriting from its default prototype. 46 Object.setPrototypeOf(new ctor(...args), Object.prototype), 47 ]; 48 })), 49 ]; 50 } 51 52 const intlFallbackSymbol = Object.getOwnPropertySymbols(Intl.NumberFormat.call(Object.create(Intl.NumberFormat.prototype)))[0]; 53 54 // Invoking [[Call]] for Intl.NumberFormat returns a new instance unless called 55 // with an instance inheriting from Intl.NumberFormat.prototype. 56 for (let thisValue of thisValues()) { 57 let obj = Intl.NumberFormat.call(thisValue); 58 59 if (!Intl.NumberFormat.prototype.isPrototypeOf(thisValue)) { 60 assertEq(Object.is(obj, thisValue), false); 61 assertEq(obj instanceof Intl.NumberFormat, true); 62 if (IsObject(thisValue)) 63 assertEqArray(Object.getOwnPropertySymbols(thisValue), []); 64 } else { 65 assertEq(Object.is(obj, thisValue), true); 66 assertEq(obj instanceof Intl.NumberFormat, true); 67 assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); 68 } 69 } 70 71 // Intl.NumberFormat uses the legacy Intl constructor compromise semantics. 72 // - Test when InstanceofOperator(thisValue, %NumberFormat%) returns true. 73 for (let thisValue of thisValues().filter(IsObject)) { 74 let isPrototypeOf = Intl.NumberFormat.prototype.isPrototypeOf(thisValue); 75 let hasInstanceCalled = false; 76 Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { 77 value() { 78 assertEq(hasInstanceCalled, false); 79 hasInstanceCalled = true; 80 return true; 81 }, configurable: true 82 }); 83 let obj = Intl.NumberFormat.call(thisValue); 84 delete Intl.NumberFormat[Symbol.hasInstance]; 85 86 assertEq(Object.is(obj, thisValue), isPrototypeOf); 87 assertEq(hasInstanceCalled, false); 88 assertEqArray(Object.getOwnPropertySymbols(thisValue), isPrototypeOf ? [intlFallbackSymbol] : []); 89 } 90 // - Test when InstanceofOperator(thisValue, %NumberFormat%) returns false. 91 for (let thisValue of thisValues().filter(IsObject)) { 92 let isPrototypeOf = Intl.NumberFormat.prototype.isPrototypeOf(thisValue); 93 let hasInstanceCalled = false; 94 Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { 95 value() { 96 assertEq(hasInstanceCalled, false); 97 hasInstanceCalled = true; 98 return false; 99 }, configurable: true 100 }); 101 let obj = Intl.NumberFormat.call(thisValue); 102 delete Intl.NumberFormat[Symbol.hasInstance]; 103 104 assertEq(Object.is(obj, thisValue), isPrototypeOf); 105 assertEq(obj instanceof Intl.NumberFormat, true); 106 assertEq(hasInstanceCalled, false); 107 assertEqArray(Object.getOwnPropertySymbols(thisValue), isPrototypeOf ? [intlFallbackSymbol] : []); 108 } 109 // - Test with primitive values. 110 for (let thisValue of thisValues().filter(IsPrimitive)) { 111 // Ensure @@hasInstance is not called. 112 Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { 113 value() { assertEq(true, false); }, configurable: true 114 }); 115 let obj = Intl.NumberFormat.call(thisValue); 116 delete Intl.NumberFormat[Symbol.hasInstance]; 117 118 assertEq(Object.is(obj, thisValue), false); 119 assertEq(obj instanceof Intl.NumberFormat, true); 120 } 121 122 // Throws an error when attempting to install [[FallbackSymbol]] twice. 123 { 124 let thisValue = Object.create(Intl.NumberFormat.prototype); 125 assertEqArray(Object.getOwnPropertySymbols(thisValue), []); 126 127 assertEq(Intl.NumberFormat.call(thisValue), thisValue); 128 assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); 129 130 assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError); 131 assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); 132 } 133 134 // Throws an error when the thisValue is non-extensible. 135 { 136 let thisValue = Object.create(Intl.NumberFormat.prototype); 137 Object.preventExtensions(thisValue); 138 139 assertThrowsInstanceOf(() => Intl.NumberFormat.call(thisValue), TypeError); 140 assertEqArray(Object.getOwnPropertySymbols(thisValue), []); 141 } 142 143 // [[FallbackSymbol]] is installed as a frozen property holding an Intl.NumberFormat instance. 144 { 145 let thisValue = Object.create(Intl.NumberFormat.prototype); 146 Intl.NumberFormat.call(thisValue); 147 148 let desc = Object.getOwnPropertyDescriptor(thisValue, intlFallbackSymbol); 149 assertEq(desc !== undefined, true); 150 assertEq(desc.writable, false); 151 assertEq(desc.enumerable, false); 152 assertEq(desc.configurable, false); 153 assertEq(desc.value instanceof Intl.NumberFormat, true); 154 } 155 156 // Ensure [[FallbackSymbol]] is installed last by changing the [[Prototype]] 157 // during initialization. 158 { 159 let thisValue = {}; 160 let options = { 161 get useGrouping() { 162 Object.setPrototypeOf(thisValue, Intl.NumberFormat.prototype); 163 return false; 164 } 165 }; 166 let obj = Intl.NumberFormat.call(thisValue, undefined, options); 167 assertEq(Object.is(obj, thisValue), true); 168 assertEqArray(Object.getOwnPropertySymbols(thisValue), [intlFallbackSymbol]); 169 } 170 { 171 let thisValue = Object.create(Intl.NumberFormat.prototype); 172 let options = { 173 get useGrouping() { 174 Object.setPrototypeOf(thisValue, Object.prototype); 175 return false; 176 } 177 }; 178 let obj = Intl.NumberFormat.call(thisValue, undefined, options); 179 assertEq(Object.is(obj, thisValue), false); 180 assertEqArray(Object.getOwnPropertySymbols(thisValue), []); 181 } 182 183 if (typeof reportCompare === "function") 184 reportCompare(true, true);