1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5(function(global, utils) {
6
7%CheckIsBootstrapping();
8
9// ----------------------------------------------------------------------------
10// Imports
11
12var GlobalArray = global.Array;
13var GlobalNumber = global.Number;
14var GlobalObject = global.Object;
15var iteratorSymbol = utils.ImportNow("iterator_symbol");
16var MakeRangeError;
17var MakeSyntaxError;
18var MakeTypeError;
19var MathAbs;
20var NaN = %GetRootNaN();
21var ObjectToString = utils.ImportNow("object_to_string");
22var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
23
24utils.Import(function(from) {
25  MakeRangeError = from.MakeRangeError;
26  MakeSyntaxError = from.MakeSyntaxError;
27  MakeTypeError = from.MakeTypeError;
28  MathAbs = from.MathAbs;
29});
30
31// ----------------------------------------------------------------------------
32
33
34// ES6 18.2.3 isNaN(number)
35function GlobalIsNaN(number) {
36  number = TO_NUMBER(number);
37  return NUMBER_IS_NAN(number);
38}
39
40
41// ES6 18.2.2 isFinite(number)
42function GlobalIsFinite(number) {
43  number = TO_NUMBER(number);
44  return NUMBER_IS_FINITE(number);
45}
46
47
48// ES6 18.2.5 parseInt(string, radix)
49function GlobalParseInt(string, radix) {
50  if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
51    // Some people use parseInt instead of Math.floor.  This
52    // optimization makes parseInt on a Smi 12 times faster (60ns
53    // vs 800ns).  The following optimization makes parseInt on a
54    // non-Smi number 9 times faster (230ns vs 2070ns).  Together
55    // they make parseInt on a string 1.4% slower (274ns vs 270ns).
56    if (%_IsSmi(string)) return string;
57    if (IS_NUMBER(string) &&
58        ((0.01 < string && string < 1e9) ||
59            (-1e9 < string && string < -0.01))) {
60      // Truncate number.
61      return string | 0;
62    }
63    string = TO_STRING(string);
64    radix = radix | 0;
65  } else {
66    // The spec says ToString should be evaluated before ToInt32.
67    string = TO_STRING(string);
68    radix = TO_INT32(radix);
69    if (!(radix == 0 || (2 <= radix && radix <= 36))) {
70      return NaN;
71    }
72  }
73
74  if (%_HasCachedArrayIndex(string) &&
75      (radix == 0 || radix == 10)) {
76    return %_GetCachedArrayIndex(string);
77  }
78  return %StringParseInt(string, radix);
79}
80
81
82// ES6 18.2.4 parseFloat(string)
83function GlobalParseFloat(string) {
84  // 1. Let inputString be ? ToString(string).
85  string = TO_STRING(string);
86  if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
87  return %StringParseFloat(string);
88}
89
90
91// ----------------------------------------------------------------------------
92
93// Set up global object.
94var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
95
96utils.InstallConstants(global, [
97  // ES6 18.1.1
98  "Infinity", INFINITY,
99  // ES6 18.1.2
100  "NaN", NaN,
101  // ES6 18.1.3
102  "undefined", UNDEFINED,
103]);
104
105// Set up non-enumerable function on the global object.
106utils.InstallFunctions(global, DONT_ENUM, [
107  "isNaN", GlobalIsNaN,
108  "isFinite", GlobalIsFinite,
109  "parseInt", GlobalParseInt,
110  "parseFloat", GlobalParseFloat,
111]);
112
113
114// ----------------------------------------------------------------------------
115// Object
116
117// ES6 19.1.3.5 Object.prototype.toLocaleString([reserved1 [,reserved2]])
118function ObjectToLocaleString() {
119  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.toLocaleString");
120  return this.toString();
121}
122
123
124// ES6 19.1.3.7 Object.prototype.valueOf()
125function ObjectValueOf() {
126  return TO_OBJECT(this);
127}
128
129
130// ES6 19.1.3.3 Object.prototype.isPrototypeOf(V)
131function ObjectIsPrototypeOf(V) {
132  if (!IS_RECEIVER(V)) return false;
133  var O = TO_OBJECT(this);
134  return %HasInPrototypeChain(V, O);
135}
136
137
138// ES6 19.1.3.4
139function ObjectPropertyIsEnumerable(V) {
140  var P = TO_NAME(V);
141  return %PropertyIsEnumerable(TO_OBJECT(this), P);
142}
143
144// ES6 7.3.9
145function GetMethod(obj, p) {
146  var func = obj[p];
147  if (IS_NULL_OR_UNDEFINED(func)) return UNDEFINED;
148  if (IS_CALLABLE(func)) return func;
149  throw MakeTypeError(kCalledNonCallable, typeof func);
150}
151
152// ES6 section 19.1.2.18.
153function ObjectSetPrototypeOf(obj, proto) {
154  CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");
155
156  if (proto !== null && !IS_RECEIVER(proto)) {
157    throw MakeTypeError(kProtoObjectOrNull, proto);
158  }
159
160  if (IS_RECEIVER(obj)) {
161    %SetPrototype(obj, proto);
162  }
163
164  return obj;
165}
166
167// ES6 B.2.2.1.1
168function ObjectGetProto() {
169  return %object_get_prototype_of(this);
170}
171
172
173// ES6 B.2.2.1.2
174function ObjectSetProto(proto) {
175  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.__proto__");
176
177  if ((IS_RECEIVER(proto) || IS_NULL(proto)) && IS_RECEIVER(this)) {
178    %SetPrototype(this, proto);
179  }
180}
181
182
183// ES6 19.1.1.1
184function ObjectConstructor(x) {
185  if (GlobalObject != new.target && !IS_UNDEFINED(new.target)) {
186    return this;
187  }
188  if (IS_NULL(x) || IS_UNDEFINED(x)) return {};
189  return TO_OBJECT(x);
190}
191
192
193// ----------------------------------------------------------------------------
194// Object
195
196%SetNativeFlag(GlobalObject);
197%SetCode(GlobalObject, ObjectConstructor);
198
199%AddNamedProperty(GlobalObject.prototype, "constructor", GlobalObject,
200                  DONT_ENUM);
201
202// Set up non-enumerable functions on the Object.prototype object.
203utils.InstallFunctions(GlobalObject.prototype, DONT_ENUM, [
204  "toString", ObjectToString,
205  "toLocaleString", ObjectToLocaleString,
206  "valueOf", ObjectValueOf,
207  "isPrototypeOf", ObjectIsPrototypeOf,
208  "propertyIsEnumerable", ObjectPropertyIsEnumerable,
209  // __defineGetter__ is added in bootstrapper.cc.
210  // __lookupGetter__ is added in bootstrapper.cc.
211  // __defineSetter__ is added in bootstrapper.cc.
212  // __lookupSetter__ is added in bootstrapper.cc.
213]);
214utils.InstallGetterSetter(
215    GlobalObject.prototype, "__proto__", ObjectGetProto, ObjectSetProto);
216
217// Set up non-enumerable functions in the Object object.
218utils.InstallFunctions(GlobalObject, DONT_ENUM, [
219  "setPrototypeOf", ObjectSetPrototypeOf,
220  // getOwnPropertySymbols is added in symbol.js.
221  // Others are added in bootstrapper.cc.
222]);
223
224
225
226// ----------------------------------------------------------------------------
227// Number
228
229// ES6 Number.prototype.toString([ radix ])
230function NumberToStringJS(radix) {
231  // NOTE: Both Number objects and values can enter here as
232  // 'this'. This is not as dictated by ECMA-262.
233  var number = this;
234  if (!IS_NUMBER(this)) {
235    if (!IS_NUMBER_WRAPPER(this)) {
236      throw MakeTypeError(kNotGeneric, 'Number.prototype.toString');
237    }
238    // Get the value of this number in case it's an object.
239    number = %_ValueOf(this);
240  }
241  // Fast case: Convert number in radix 10.
242  if (IS_UNDEFINED(radix) || radix === 10) {
243    return %_NumberToString(number);
244  }
245
246  // Convert the radix to an integer and check the range.
247  radix = TO_INTEGER(radix);
248  if (radix < 2 || radix > 36) throw MakeRangeError(kToRadixFormatRange);
249  // Convert the number to a string in the given radix.
250  return %NumberToRadixString(number, radix);
251}
252
253
254// ES6 20.1.3.4 Number.prototype.toLocaleString([reserved1 [, reserved2]])
255function NumberToLocaleString() {
256  return %_Call(NumberToStringJS, this);
257}
258
259
260// ES6 20.1.3.7 Number.prototype.valueOf()
261function NumberValueOf() {
262  // NOTE: Both Number objects and values can enter here as
263  // 'this'. This is not as dictated by ECMA-262.
264  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) {
265    throw MakeTypeError(kNotGeneric, 'Number.prototype.valueOf');
266  }
267  return %_ValueOf(this);
268}
269
270
271// ES6 20.1.3.3 Number.prototype.toFixed(fractionDigits)
272function NumberToFixedJS(fractionDigits) {
273  var x = this;
274  if (!IS_NUMBER(this)) {
275    if (!IS_NUMBER_WRAPPER(this)) {
276      throw MakeTypeError(kIncompatibleMethodReceiver,
277                          "Number.prototype.toFixed", this);
278    }
279    // Get the value of this number in case it's an object.
280    x = %_ValueOf(this);
281  }
282  var f = TO_INTEGER(fractionDigits);
283
284  if (f < 0 || f > 20) {
285    throw MakeRangeError(kNumberFormatRange, "toFixed() digits");
286  }
287
288  if (NUMBER_IS_NAN(x)) return "NaN";
289  if (x == INFINITY) return "Infinity";
290  if (x == -INFINITY) return "-Infinity";
291
292  return %NumberToFixed(x, f);
293}
294
295
296// ES6 20.1.3.2 Number.prototype.toExponential(fractionDigits)
297function NumberToExponentialJS(fractionDigits) {
298  var x = this;
299  if (!IS_NUMBER(this)) {
300    if (!IS_NUMBER_WRAPPER(this)) {
301      throw MakeTypeError(kIncompatibleMethodReceiver,
302                          "Number.prototype.toExponential", this);
303    }
304    // Get the value of this number in case it's an object.
305    x = %_ValueOf(this);
306  }
307  var f = IS_UNDEFINED(fractionDigits) ? UNDEFINED : TO_INTEGER(fractionDigits);
308
309  if (NUMBER_IS_NAN(x)) return "NaN";
310  if (x == INFINITY) return "Infinity";
311  if (x == -INFINITY) return "-Infinity";
312
313  if (IS_UNDEFINED(f)) {
314    f = -1;  // Signal for runtime function that f is not defined.
315  } else if (f < 0 || f > 20) {
316    throw MakeRangeError(kNumberFormatRange, "toExponential()");
317  }
318  return %NumberToExponential(x, f);
319}
320
321
322// ES6 20.1.3.5 Number.prototype.toPrecision(precision)
323function NumberToPrecisionJS(precision) {
324  var x = this;
325  if (!IS_NUMBER(this)) {
326    if (!IS_NUMBER_WRAPPER(this)) {
327      throw MakeTypeError(kIncompatibleMethodReceiver,
328                          "Number.prototype.toPrecision", this);
329    }
330    // Get the value of this number in case it's an object.
331    x = %_ValueOf(this);
332  }
333  if (IS_UNDEFINED(precision)) return TO_STRING(x);
334  var p = TO_INTEGER(precision);
335
336  if (NUMBER_IS_NAN(x)) return "NaN";
337  if (x == INFINITY) return "Infinity";
338  if (x == -INFINITY) return "-Infinity";
339
340  if (p < 1 || p > 21) {
341    throw MakeRangeError(kToPrecisionFormatRange);
342  }
343  return %NumberToPrecision(x, p);
344}
345
346
347// Harmony isFinite.
348function NumberIsFinite(number) {
349  return IS_NUMBER(number) && NUMBER_IS_FINITE(number);
350}
351
352
353// Harmony isInteger
354function NumberIsInteger(number) {
355  return NumberIsFinite(number) && TO_INTEGER(number) == number;
356}
357
358
359// Harmony isNaN.
360function NumberIsNaN(number) {
361  return IS_NUMBER(number) && NUMBER_IS_NAN(number);
362}
363
364
365// Harmony isSafeInteger
366function NumberIsSafeInteger(number) {
367  if (NumberIsFinite(number)) {
368    var integral = TO_INTEGER(number);
369    if (integral == number) {
370      return MathAbs(integral) <= kMaxSafeInteger;
371    }
372  }
373  return false;
374}
375
376
377// ----------------------------------------------------------------------------
378
379%FunctionSetPrototype(GlobalNumber, new GlobalNumber(0));
380
381%OptimizeObjectForAddingMultipleProperties(GlobalNumber.prototype, 8);
382// Set up the constructor property on the Number prototype object.
383%AddNamedProperty(GlobalNumber.prototype, "constructor", GlobalNumber,
384                  DONT_ENUM);
385
386utils.InstallConstants(GlobalNumber, [
387  // ECMA-262 section 15.7.3.1.
388  "MAX_VALUE", 1.7976931348623157e+308,
389  // ECMA-262 section 15.7.3.2.
390  "MIN_VALUE", 5e-324,
391  // ECMA-262 section 15.7.3.3.
392  "NaN", NaN,
393  // ECMA-262 section 15.7.3.4.
394  "NEGATIVE_INFINITY", -INFINITY,
395  // ECMA-262 section 15.7.3.5.
396  "POSITIVE_INFINITY", INFINITY,
397
398  // --- Harmony constants (no spec refs until settled.)
399
400  "MAX_SAFE_INTEGER", %_MathPow(2, 53) - 1,
401  "MIN_SAFE_INTEGER", -%_MathPow(2, 53) + 1,
402  "EPSILON", %_MathPow(2, -52)
403]);
404
405// Set up non-enumerable functions on the Number prototype object.
406utils.InstallFunctions(GlobalNumber.prototype, DONT_ENUM, [
407  "toString", NumberToStringJS,
408  "toLocaleString", NumberToLocaleString,
409  "valueOf", NumberValueOf,
410  "toFixed", NumberToFixedJS,
411  "toExponential", NumberToExponentialJS,
412  "toPrecision", NumberToPrecisionJS
413]);
414
415// Harmony Number constructor additions
416utils.InstallFunctions(GlobalNumber, DONT_ENUM, [
417  "isFinite", NumberIsFinite,
418  "isInteger", NumberIsInteger,
419  "isNaN", NumberIsNaN,
420  "isSafeInteger", NumberIsSafeInteger,
421  "parseInt", GlobalParseInt,
422  "parseFloat", GlobalParseFloat
423]);
424
425%SetForceInlineFlag(NumberIsNaN);
426
427
428// ----------------------------------------------------------------------------
429// Iterator related spec functions.
430
431// ES6 7.4.1 GetIterator(obj, method)
432function GetIterator(obj, method) {
433  if (IS_UNDEFINED(method)) {
434    method = obj[iteratorSymbol];
435  }
436  if (!IS_CALLABLE(method)) {
437    throw MakeTypeError(kNotIterable, obj);
438  }
439  var iterator = %_Call(method, obj);
440  if (!IS_RECEIVER(iterator)) {
441    throw MakeTypeError(kNotAnIterator, iterator);
442  }
443  return iterator;
444}
445
446// ----------------------------------------------------------------------------
447// Exports
448
449utils.Export(function(to) {
450  to.GetIterator = GetIterator;
451  to.GetMethod = GetMethod;
452  to.IsNaN = GlobalIsNaN;
453  to.NumberIsNaN = NumberIsNaN;
454  to.NumberIsInteger = NumberIsInteger;
455  to.ObjectHasOwnProperty = GlobalObject.prototype.hasOwnProperty;
456});
457
458%InstallToContext([
459  "object_value_of", ObjectValueOf,
460]);
461
462})
463