function-call.js 10 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


29
const should_throw_on_null_and_undefined =
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    [Object.prototype.toLocaleString,
     Object.prototype.valueOf,
     Object.prototype.hasOwnProperty,
     Object.prototype.isPrototypeOf,
     Object.prototype.propertyIsEnumerable,
     Array.prototype.concat,
     Array.prototype.join,
     Array.prototype.pop,
     Array.prototype.push,
     Array.prototype.reverse,
     Array.prototype.shift,
     Array.prototype.slice,
     Array.prototype.splice,
     Array.prototype.unshift,
     Array.prototype.indexOf,
     Array.prototype.lastIndexOf,
     Array.prototype.every,
     Array.prototype.some,
     Array.prototype.forEach,
     Array.prototype.map,
     Array.prototype.filter,
     Array.prototype.reduce,
     Array.prototype.reduceRight,
     String.prototype.charAt,
     String.prototype.charCodeAt,
     String.prototype.concat,
     String.prototype.indexOf,
     String.prototype.lastIndexOf,
     String.prototype.localeCompare,
     String.prototype.match,
     String.prototype.replace,
     String.prototype.search,
     String.prototype.slice,
     String.prototype.split,
     String.prototype.substring,
     String.prototype.toLowerCase,
     String.prototype.toLocaleLowerCase,
     String.prototype.toUpperCase,
     String.prototype.toLocaleUpperCase,
69
     String.prototype.trim];
70 71 72 73

// Non generic natives do not work on any input other than the specific
// type, but since this change will allow call to be invoked with undefined
// or null as this we still explicitly test that we throw on these here.
74
const non_generic =
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
    [Array.prototype.toString,
     Array.prototype.toLocaleString,
     Function.prototype.toString,
     Function.prototype.call,
     Function.prototype.apply,
     String.prototype.toString,
     String.prototype.valueOf,
     Boolean.prototype.toString,
     Boolean.prototype.valueOf,
     Number.prototype.toString,
     Number.prototype.valueOf,
     Number.prototype.toFixed,
     Number.prototype.toExponential,
     Number.prototype.toPrecision,
     Date.prototype.toString,
     Date.prototype.toDateString,
     Date.prototype.toTimeString,
     Date.prototype.toLocaleString,
     Date.prototype.toLocaleDateString,
     Date.prototype.toLocaleTimeString,
     Date.prototype.valueOf,
     Date.prototype.getTime,
     Date.prototype.getFullYear,
     Date.prototype.getUTCFullYear,
     Date.prototype.getMonth,
     Date.prototype.getUTCMonth,
     Date.prototype.getDate,
     Date.prototype.getUTCDate,
     Date.prototype.getDay,
     Date.prototype.getUTCDay,
     Date.prototype.getHours,
     Date.prototype.getUTCHours,
     Date.prototype.getMinutes,
     Date.prototype.getUTCMinutes,
     Date.prototype.getSeconds,
     Date.prototype.getUTCSeconds,
     Date.prototype.getMilliseconds,
     Date.prototype.getUTCMilliseconds,
     Date.prototype.getTimezoneOffset,
     Date.prototype.setTime,
     Date.prototype.setMilliseconds,
     Date.prototype.setUTCMilliseconds,
     Date.prototype.setSeconds,
     Date.prototype.setUTCSeconds,
     Date.prototype.setMinutes,
     Date.prototype.setUTCMinutes,
     Date.prototype.setHours,
     Date.prototype.setUTCHours,
     Date.prototype.setDate,
     Date.prototype.setUTCDate,
     Date.prototype.setMonth,
     Date.prototype.setUTCMonth,
     Date.prototype.setFullYear,
     Date.prototype.setUTCFullYear,
     Date.prototype.toUTCString,
     Date.prototype.toISOString,
     Date.prototype.toJSON,
     RegExp.prototype.exec,
     RegExp.prototype.test,
134 135
     RegExp.prototype.toString,
     Error.prototype.toString];
136 137 138


// Mapping functions.
139
const mapping_functions =
140 141 142 143 144 145 146
    [Array.prototype.every,
     Array.prototype.some,
     Array.prototype.forEach,
     Array.prototype.map,
     Array.prototype.filter];

// Reduce functions.
147
const reducing_functions =
148 149 150
    [Array.prototype.reduce,
     Array.prototype.reduceRight];

151
function checkExpectedMessage(e) {
152 153 154
  assertTrue(e.message.includes("called on null or undefined") ||
      e.message.includes("invoked on undefined or null value") ||
      e.message.includes("Cannot convert undefined or null to object"));
155 156
}

157
// Test that all natives using the ToObject call throw the right exception.
158
for (const fn of should_throw_on_null_and_undefined) {
159
  // Sanity check that all functions are correct
160
  assertEquals(typeof fn, "function");
161

162
  let exception = false;
163
  try {
164 165 166
    // We need to pass a dummy object argument ({}) to these functions because
    // of Object.prototype.isPrototypeOf's special behavior, see issue 3483
    // for more details.
167
    fn.call(null, {});
168
  } catch (e) {
169
    exception = true;
170
    checkExpectedMessage(e);
171
  }
172
  assertTrue(exception);
173

174
  exception = false;
175
  try {
176
    fn.call(undefined, {});
177
  } catch (e) {
178
    exception = true;
179
    checkExpectedMessage(e);
180
  }
181
  assertTrue(exception);
182

183
  exception = false;
184
  try {
185
    fn.apply(null, [{}]);
186
  } catch (e) {
187
    exception = true;
188
    checkExpectedMessage(e);
189
  }
190
  assertTrue(exception);
191

192
  exception = false;
193
  try {
194
    fn.apply(undefined, [{}]);
195
  } catch (e) {
196
    exception = true;
197
    checkExpectedMessage(e);
198
  }
199
  assertTrue(exception);
200 201 202
}

// Test that all natives that are non generic throw on null and undefined.
203
for (const fn of non_generic) {
204
  // Sanity check that all functions are correct
205
  assertEquals(typeof fn, "function");
206 207

  exception = false;
208
  try {
209
    fn.call(null);
210
  } catch (e) {
211
    exception = true;
212 213
    assertTrue(e instanceof TypeError);
  }
214
  assertTrue(exception);
215

216
  exception = false;
217
  try {
218
    fn.call(null);
219
  } catch (e) {
220
    exception = true;
221 222
    assertTrue(e instanceof TypeError);
  }
223
  assertTrue(exception);
224

225
  exception = false;
226
  try {
227
    fn.apply(null);
228
  } catch (e) {
229
    exception = true;
230 231
    assertTrue(e instanceof TypeError);
  }
232
  assertTrue(exception);
233

234
  exception = false;
235
  try {
236
    fn.apply(null);
237
  } catch (e) {
238
    exception = true;
239 240
    assertTrue(e instanceof TypeError);
  }
241
  assertTrue(exception);
242 243 244 245 246
}


// Test that we still throw when calling with thisArg null or undefined
// through an array mapping function.
247 248
// We need to make sure that the elements of `array` are all object values,
// see issue 3483 for more details.
249 250 251
const array = [{}, [], new Number, new Map, new WeakSet];
for (const mapping_function of mapping_functions) {
  for (const fn of should_throw_on_null_and_undefined) {
252
    exception = false;
253
    try {
254 255 256
      mapping_function.call(array,
                            fn,
                            null);
257
    } catch (e) {
258
      exception = true;
259
      checkExpectedMessage(e);
260
    }
261
    assertTrue(exception);
262

263
    exception = false;
264
    try {
265 266 267
      mapping_function.call(array,
                            fn,
                            undefined);
268
    } catch (e) {
269
      exception = true;
270
      checkExpectedMessage(e);
271
    }
272
    assertTrue(exception);
273 274 275
  }
}

276 277
for (const mapping_function of mapping_functions) {
  for (const fn of non_generic) {
278
    exception = false;
279
    try {
280 281 282
      mapping_function.call(array,
                            fn,
                            null);
283
    } catch (e) {
284
      exception = true;
285 286
      assertTrue(e instanceof TypeError);
    }
287
    assertTrue(exception);
288

289
    exception = false;
290
    try {
291 292 293
      mapping_function.call(array,
                            fn,
                            undefined);
294
    } catch (e) {
295
      exception = true;
296 297
      assertTrue(e instanceof TypeError);
    }
298
    assertTrue(exception);
299 300 301 302 303
  }
}


// Reduce functions do a call with null as this argument.
304 305
for (const reducing_function of reducing_functions) {
  for (const fn of should_throw_on_null_and_undefined) {
306
    exception = false;
307
    try {
308
      reducing_function.call(array, fn);
309
    } catch (e) {
310
      exception = true;
311
      checkExpectedMessage(e);
312
    }
313
    assertTrue(exception);
314

315
    exception = false;
316
    try {
317
      reducing_function.call(array, fn);
318
    } catch (e) {
319
      exception = true;
320
      checkExpectedMessage(e);
321
    }
322
    assertTrue(exception);
323 324 325
  }
}

326 327
for (const reducing_function of reducing_functions) {
  for (const fn of non_generic) {
328
    exception = false;
329
    try {
330
      reducing_function.call(array, fn);
331
    } catch (e) {
332
      exception = true;
333 334
      assertTrue(e instanceof TypeError);
    }
335
    assertTrue(exception);
336

337
    exception = false;
338
    try {
339
      reducing_function.call(array, fn);
340
    } catch (e) {
341
      exception = true;
342 343
      assertTrue(e instanceof TypeError);
    }
344
    assertTrue(exception);
345 346 347 348 349 350 351 352 353 354
  }
}


// Object.prototype.toString()
assertEquals(Object.prototype.toString.call(null),
             '[object Null]')

assertEquals(Object.prototype.toString.call(undefined),
             '[object Undefined]')