v8natives.js revision d91b9f7d46489a9ee00f9cb415630299c76a502b
1// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// This file relies on the fact that the following declarations have been made
29//
30// in runtime.js:
31// const $Object = global.Object;
32// const $Boolean = global.Boolean;
33// const $Number = global.Number;
34// const $Function = global.Function;
35// const $Array = global.Array;
36// const $NaN = 0/0;
37//
38// in math.js:
39// const $floor = MathFloor
40
41const $isNaN = GlobalIsNaN;
42const $isFinite = GlobalIsFinite;
43
44
45// ----------------------------------------------------------------------------
46
47
48// Helper function used to install functions on objects.
49function InstallFunctions(object, attributes, functions) {
50  if (functions.length >= 8) {
51    %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
52  }
53  for (var i = 0; i < functions.length; i += 2) {
54    var key = functions[i];
55    var f = functions[i + 1];
56    %FunctionSetName(f, key);
57    %SetProperty(object, key, f, attributes);
58  }
59  %TransformToFastProperties(object);
60}
61
62// Emulates JSC by installing functions on a hidden prototype that
63// lies above the current object/prototype.  This lets you override
64// functions on String.prototype etc. and then restore the old function
65// with delete.  See http://code.google.com/p/chromium/issues/detail?id=1717
66function InstallFunctionsOnHiddenPrototype(object, attributes, functions) {
67  var hidden_prototype = new $Object();
68  %SetHiddenPrototype(object, hidden_prototype);
69  InstallFunctions(hidden_prototype, attributes, functions);
70}
71
72
73// ----------------------------------------------------------------------------
74
75
76// ECMA 262 - 15.1.4
77function GlobalIsNaN(number) {
78  var n = ToNumber(number);
79  return NUMBER_IS_NAN(n);
80}
81
82
83// ECMA 262 - 15.1.5
84function GlobalIsFinite(number) {
85  return %NumberIsFinite(ToNumber(number));
86}
87
88
89// ECMA-262 - 15.1.2.2
90function GlobalParseInt(string, radix) {
91  if (IS_UNDEFINED(radix)) {
92    // Some people use parseInt instead of Math.floor.  This
93    // optimization makes parseInt on a Smi 12 times faster (60ns
94    // vs 800ns).  The following optimization makes parseInt on a
95    // non-Smi number 9 times faster (230ns vs 2070ns).  Together
96    // they make parseInt on a string 1.4% slower (274ns vs 270ns).
97    if (%_IsSmi(string)) return string;
98    if (IS_NUMBER(string) &&
99        ((0.01 < string && string < 1e9) ||
100            (-1e9 < string && string < -0.01))) {
101      // Truncate number.
102      return string | 0;
103    }
104    radix = 0;
105  } else {
106    radix = TO_INT32(radix);
107    if (!(radix == 0 || (2 <= radix && radix <= 36)))
108      return $NaN;
109  }
110  return %StringParseInt(ToString(string), radix);
111}
112
113
114// ECMA-262 - 15.1.2.3
115function GlobalParseFloat(string) {
116  return %StringParseFloat(ToString(string));
117}
118
119
120function GlobalEval(x) {
121  if (!IS_STRING(x)) return x;
122
123  var global_receiver = %GlobalReceiver(global);
124  var this_is_global_receiver = (this === global_receiver);
125  var global_is_detached = (global === global_receiver);
126
127  if (!this_is_global_receiver || global_is_detached) {
128    throw new $EvalError('The "this" object passed to eval must ' +
129                         'be the global object from which eval originated');
130  }
131
132  var f = %CompileString(x, false);
133  if (!IS_FUNCTION(f)) return f;
134
135  return f.call(this);
136}
137
138
139// execScript for IE compatibility.
140function GlobalExecScript(expr, lang) {
141  // NOTE: We don't care about the character casing.
142  if (!lang || /javascript/i.test(lang)) {
143    var f = %CompileString(ToString(expr), false);
144    f.call(%GlobalReceiver(global));
145  }
146  return null;
147}
148
149
150// ----------------------------------------------------------------------------
151
152
153function SetupGlobal() {
154  // ECMA 262 - 15.1.1.1.
155  %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE);
156
157  // ECMA-262 - 15.1.1.2.
158  %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE);
159
160  // ECMA-262 - 15.1.1.3.
161  %SetProperty(global, "undefined", void 0, DONT_ENUM | DONT_DELETE);
162
163  // Setup non-enumerable function on the global object.
164  InstallFunctions(global, DONT_ENUM, $Array(
165    "isNaN", GlobalIsNaN,
166    "isFinite", GlobalIsFinite,
167    "parseInt", GlobalParseInt,
168    "parseFloat", GlobalParseFloat,
169    "eval", GlobalEval,
170    "execScript", GlobalExecScript
171  ));
172}
173
174SetupGlobal();
175
176
177// ----------------------------------------------------------------------------
178// Boolean (first part of definition)
179
180
181%SetCode($Boolean, function(x) {
182  if (%_IsConstructCall()) {
183    %_SetValueOf(this, ToBoolean(x));
184  } else {
185    return ToBoolean(x);
186  }
187});
188
189%FunctionSetPrototype($Boolean, new $Boolean(false));
190
191%SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM);
192
193// ----------------------------------------------------------------------------
194// Object
195
196$Object.prototype.constructor = $Object;
197
198// ECMA-262 - 15.2.4.2
199function ObjectToString() {
200  return "[object " + %_ClassOf(ToObject(this)) + "]";
201}
202
203
204// ECMA-262 - 15.2.4.3
205function ObjectToLocaleString() {
206  return this.toString();
207}
208
209
210// ECMA-262 - 15.2.4.4
211function ObjectValueOf() {
212  return ToObject(this);
213}
214
215
216// ECMA-262 - 15.2.4.5
217function ObjectHasOwnProperty(V) {
218  return %HasLocalProperty(ToObject(this), ToString(V));
219}
220
221
222// ECMA-262 - 15.2.4.6
223function ObjectIsPrototypeOf(V) {
224  if (!IS_OBJECT(V) && !IS_FUNCTION(V)) return false;
225  return %IsInPrototypeChain(this, V);
226}
227
228
229// ECMA-262 - 15.2.4.6
230function ObjectPropertyIsEnumerable(V) {
231  if (this == null) return false;
232  if (!IS_OBJECT(this) && !IS_FUNCTION(this)) return false;
233  return %IsPropertyEnumerable(this, ToString(V));
234}
235
236
237// Extensions for providing property getters and setters.
238function ObjectDefineGetter(name, fun) {
239  if (this == null) {
240    throw new $TypeError('Object.prototype.__defineGetter__: this is Null');
241  }
242  if (!IS_FUNCTION(fun)) {
243    throw new $TypeError('Object.prototype.__defineGetter__: Expecting function');
244  }
245  return %DefineAccessor(ToObject(this), ToString(name), GETTER, fun);
246}
247
248
249function ObjectLookupGetter(name) {
250  if (this == null) {
251    throw new $TypeError('Object.prototype.__lookupGetter__: this is Null');
252  }
253  return %LookupAccessor(ToObject(this), ToString(name), GETTER);
254}
255
256
257function ObjectDefineSetter(name, fun) {
258  if (this == null) {
259    throw new $TypeError('Object.prototype.__defineSetter__: this is Null');
260  }
261  if (!IS_FUNCTION(fun)) {
262    throw new $TypeError(
263        'Object.prototype.__defineSetter__: Expecting function');
264  }
265  return %DefineAccessor(ToObject(this), ToString(name), SETTER, fun);
266}
267
268
269function ObjectLookupSetter(name) {
270  if (this == null) {
271    throw new $TypeError('Object.prototype.__lookupSetter__: this is Null');
272  }
273  return %LookupAccessor(ToObject(this), ToString(name), SETTER);
274}
275
276
277function ObjectKeys(obj) {
278  if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj))
279    throw MakeTypeError("obj_ctor_property_non_object", ["keys"]);
280  return %LocalKeys(obj);
281}
282
283
284// ES5 8.10.1.
285function IsAccessorDescriptor(desc) {
286  if (IS_UNDEFINED(desc)) return false;
287  return desc.hasGetter_ || desc.hasSetter_;
288}
289
290
291// ES5 8.10.2.
292function IsDataDescriptor(desc) {
293  if (IS_UNDEFINED(desc)) return false;
294  return desc.hasValue_ || desc.hasWritable_;
295}
296
297
298// ES5 8.10.3.
299function IsGenericDescriptor(desc) {
300  return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
301}
302
303
304function IsInconsistentDescriptor(desc) {
305  return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
306}
307
308// ES5 8.10.4
309function FromPropertyDescriptor(desc) {
310  if(IS_UNDEFINED(desc)) return desc;
311  var obj = new $Object();
312  if (IsDataDescriptor(desc)) {
313    obj.value = desc.getValue();
314    obj.writable = desc.isWritable();
315  }
316  if (IsAccessorDescriptor(desc)) {
317    obj.get = desc.getGet();
318    obj.set = desc.getSet();
319  }
320  obj.enumerable = desc.isEnumerable();
321  obj.configurable = desc.isConfigurable();
322  return obj;
323}
324
325// ES5 8.10.5.
326function ToPropertyDescriptor(obj) {
327  if (!IS_OBJECT(obj)) {
328    throw MakeTypeError("property_desc_object", [obj]);
329  }
330  var desc = new PropertyDescriptor();
331
332  if ("enumerable" in obj) {
333    desc.setEnumerable(ToBoolean(obj.enumerable));
334  }
335
336
337  if ("configurable" in obj) {
338    desc.setConfigurable(ToBoolean(obj.configurable));
339  }
340
341  if ("value" in obj) {
342    desc.setValue(obj.value);
343  }
344
345  if ("writable" in obj) {
346    desc.setWritable(ToBoolean(obj.writable));
347  }
348
349  if ("get" in obj) {
350    var get = obj.get;
351    if (!IS_UNDEFINED(get) && !IS_FUNCTION(get)) {
352      throw MakeTypeError("getter_must_be_callable", [get]);
353    }
354    desc.setGet(get);
355  }
356
357  if ("set" in obj) {
358    var set = obj.set;
359    if (!IS_UNDEFINED(set) && !IS_FUNCTION(set)) {
360      throw MakeTypeError("setter_must_be_callable", [set]);
361    }
362    desc.setSet(set);
363  }
364
365  if (IsInconsistentDescriptor(desc)) {
366    throw MakeTypeError("value_and_accessor", [obj]);
367  }
368  return desc;
369}
370
371
372function PropertyDescriptor() {
373  // Initialize here so they are all in-object and have the same map.
374  // Default values from ES5 8.6.1.
375  this.value_ = void 0;
376  this.hasValue_ = false;
377  this.writable_ = false;
378  this.hasWritable_ = false;
379  this.enumerable_ = false;
380  this.configurable_ = false;
381  this.get_ = void 0;
382  this.hasGetter_ = false;
383  this.set_ = void 0;
384  this.hasSetter_ = false;
385}
386
387
388PropertyDescriptor.prototype.setValue = function(value) {
389  this.value_ = value;
390  this.hasValue_ = true;
391}
392
393
394PropertyDescriptor.prototype.getValue = function() {
395  return this.value_;
396}
397
398
399PropertyDescriptor.prototype.setEnumerable = function(enumerable) {
400  this.enumerable_ = enumerable;
401}
402
403
404PropertyDescriptor.prototype.isEnumerable = function () {
405  return this.enumerable_;
406}
407
408
409PropertyDescriptor.prototype.setWritable = function(writable) {
410  this.writable_ = writable;
411  this.hasWritable_ = true;
412}
413
414
415PropertyDescriptor.prototype.isWritable = function() {
416  return this.writable_;
417}
418
419
420PropertyDescriptor.prototype.setConfigurable = function(configurable) {
421  this.configurable_ = configurable;
422}
423
424
425PropertyDescriptor.prototype.isConfigurable = function() {
426  return this.configurable_;
427}
428
429
430PropertyDescriptor.prototype.setGet = function(get) {
431  this.get_ = get;
432  this.hasGetter_ = true;
433}
434
435
436PropertyDescriptor.prototype.getGet = function() {
437  return this.get_;
438}
439
440
441PropertyDescriptor.prototype.setSet = function(set) {
442  this.set_ = set;
443  this.hasSetter_ = true;
444}
445
446
447PropertyDescriptor.prototype.getSet = function() {
448  return this.set_;
449}
450
451
452// ES5 section 8.12.1.
453function GetOwnProperty(obj, p) {
454  var desc = new PropertyDescriptor();
455
456  // An array with:
457  //  obj is a data property [false, value, Writeable, Enumerable, Configurable]
458  //  obj is an accessor [true, Get, Set, Enumerable, Configurable]
459  var props = %GetOwnProperty(ToObject(obj), ToString(p));
460
461  if (IS_UNDEFINED(props))
462    return void 0;
463
464  // This is an accessor
465  if (props[0]) {
466    desc.setGet(props[1]);
467    desc.setSet(props[2]);
468  } else {
469    desc.setValue(props[1]);
470    desc.setWritable(props[2]);
471  }
472  desc.setEnumerable(props[3]);
473  desc.setConfigurable(props[4]);
474
475  return desc;
476}
477
478
479// ES5 8.12.9.  This version cannot cope with the property p already
480// being present on obj.
481function DefineOwnProperty(obj, p, desc, should_throw) {
482  var flag = desc.isEnumerable() ? 0 : DONT_ENUM;
483  if (IsDataDescriptor(desc)) {
484    flag |= desc.isWritable() ? 0 : (DONT_DELETE | READ_ONLY);
485    %SetProperty(obj, p, desc.getValue(), flag);
486  } else {
487    if (IS_FUNCTION(desc.getGet())) %DefineAccessor(obj, p, GETTER, desc.getGet(), flag);
488    if (IS_FUNCTION(desc.getSet())) %DefineAccessor(obj, p, SETTER, desc.getSet(), flag);
489  }
490  return true;
491}
492
493
494// ES5 section 15.2.3.2.
495function ObjectGetPrototypeOf(obj) {
496  if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj))
497    throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]);
498  return obj.__proto__;
499}
500
501
502// ES5 section 15.2.3.3
503function ObjectGetOwnPropertyDescriptor(obj, p) {
504  if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj))
505    throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]);
506  var desc = GetOwnProperty(obj, p);
507  return FromPropertyDescriptor(desc);
508}
509
510
511// ES5 section 15.2.3.4.
512function ObjectGetOwnPropertyNames(obj) {
513  if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj))
514    throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]);
515
516  // Find all the indexed properties.
517
518  // Get the local element names.
519  var propertyNames = %GetLocalElementNames(obj);
520
521  // Get names for indexed interceptor properties.
522  if (%GetInterceptorInfo(obj) & 1) {
523    var indexedInterceptorNames =
524        %GetIndexedInterceptorElementNames(obj);
525    if (indexedInterceptorNames) {
526      propertyNames = propertyNames.concat(indexedInterceptorNames);
527    }
528  }
529
530  // Find all the named properties.
531
532  // Get the local property names.
533  propertyNames = propertyNames.concat(%GetLocalPropertyNames(obj));
534
535  // Get names for named interceptor properties if any.
536
537  if (%GetInterceptorInfo(obj) & 2) {
538    var namedInterceptorNames =
539        %GetNamedInterceptorPropertyNames(obj);
540    if (namedInterceptorNames) {
541      propertyNames = propertyNames.concat(namedInterceptorNames);
542    }
543  }
544
545  return propertyNames;
546}
547
548
549// ES5 section 15.2.3.5.
550function ObjectCreate(proto, properties) {
551  if (!IS_OBJECT(proto) && !IS_NULL(proto)) {
552    throw MakeTypeError("proto_object_or_null", [proto]);
553  }
554  var obj = new $Object();
555  obj.__proto__ = proto;
556  if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
557  return obj;
558}
559
560
561// ES5 section 15.2.3.7.  This version cannot cope with the properies already
562// being present on obj.  Therefore it is not exposed as
563// Object.defineProperties yet.
564function ObjectDefineProperties(obj, properties) {
565  var props = ToObject(properties);
566  var key_values = [];
567  for (var key in props) {
568    if (%HasLocalProperty(props, key)) {
569      key_values.push(key);
570      var value = props[key];
571      var desc = ToPropertyDescriptor(value);
572      key_values.push(desc);
573    }
574  }
575  for (var i = 0; i < key_values.length; i += 2) {
576    var key = key_values[i];
577    var desc = key_values[i + 1];
578    DefineOwnProperty(obj, key, desc, true);
579  }
580}
581
582
583%SetCode($Object, function(x) {
584  if (%_IsConstructCall()) {
585    if (x == null) return this;
586    return ToObject(x);
587  } else {
588    if (x == null) return { };
589    return ToObject(x);
590  }
591});
592
593
594// ----------------------------------------------------------------------------
595
596
597function SetupObject() {
598  // Setup non-enumerable functions on the Object.prototype object.
599  InstallFunctions($Object.prototype, DONT_ENUM, $Array(
600    "toString", ObjectToString,
601    "toLocaleString", ObjectToLocaleString,
602    "valueOf", ObjectValueOf,
603    "hasOwnProperty", ObjectHasOwnProperty,
604    "isPrototypeOf", ObjectIsPrototypeOf,
605    "propertyIsEnumerable", ObjectPropertyIsEnumerable,
606    "__defineGetter__", ObjectDefineGetter,
607    "__lookupGetter__", ObjectLookupGetter,
608    "__defineSetter__", ObjectDefineSetter,
609    "__lookupSetter__", ObjectLookupSetter
610  ));
611  InstallFunctions($Object, DONT_ENUM, $Array(
612    "keys", ObjectKeys,
613    "create", ObjectCreate,
614    "getPrototypeOf", ObjectGetPrototypeOf,
615    "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
616    "getOwnPropertyNames", ObjectGetOwnPropertyNames
617  ));
618}
619
620SetupObject();
621
622
623// ----------------------------------------------------------------------------
624// Boolean
625
626function BooleanToString() {
627  // NOTE: Both Boolean objects and values can enter here as
628  // 'this'. This is not as dictated by ECMA-262.
629  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this))
630    throw new $TypeError('Boolean.prototype.toString is not generic');
631  return ToString(%_ValueOf(this));
632}
633
634
635function BooleanValueOf() {
636  // NOTE: Both Boolean objects and values can enter here as
637  // 'this'. This is not as dictated by ECMA-262.
638  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this))
639    throw new $TypeError('Boolean.prototype.valueOf is not generic');
640  return %_ValueOf(this);
641}
642
643
644function BooleanToJSON(key) {
645  return CheckJSONPrimitive(this.valueOf());
646}
647
648
649// ----------------------------------------------------------------------------
650
651
652function SetupBoolean() {
653  InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
654    "toString", BooleanToString,
655    "valueOf", BooleanValueOf,
656    "toJSON", BooleanToJSON
657  ));
658}
659
660SetupBoolean();
661
662// ----------------------------------------------------------------------------
663// Number
664
665// Set the Number function and constructor.
666%SetCode($Number, function(x) {
667  var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
668  if (%_IsConstructCall()) {
669    %_SetValueOf(this, value);
670  } else {
671    return value;
672  }
673});
674
675%FunctionSetPrototype($Number, new $Number(0));
676
677// ECMA-262 section 15.7.4.2.
678function NumberToString(radix) {
679  // NOTE: Both Number objects and values can enter here as
680  // 'this'. This is not as dictated by ECMA-262.
681  var number = this;
682  if (!IS_NUMBER(this)) {
683    if (!IS_NUMBER_WRAPPER(this))
684      throw new $TypeError('Number.prototype.toString is not generic');
685    // Get the value of this number in case it's an object.
686    number = %_ValueOf(this);
687  }
688  // Fast case: Convert number in radix 10.
689  if (IS_UNDEFINED(radix) || radix === 10) {
690    return ToString(number);
691  }
692
693  // Convert the radix to an integer and check the range.
694  radix = TO_INTEGER(radix);
695  if (radix < 2 || radix > 36) {
696    throw new $RangeError('toString() radix argument must be between 2 and 36');
697  }
698  // Convert the number to a string in the given radix.
699  return %NumberToRadixString(number, radix);
700}
701
702
703// ECMA-262 section 15.7.4.3
704function NumberToLocaleString() {
705  return this.toString();
706}
707
708
709// ECMA-262 section 15.7.4.4
710function NumberValueOf() {
711  // NOTE: Both Number objects and values can enter here as
712  // 'this'. This is not as dictated by ECMA-262.
713  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this))
714    throw new $TypeError('Number.prototype.valueOf is not generic');
715  return %_ValueOf(this);
716}
717
718
719// ECMA-262 section 15.7.4.5
720function NumberToFixed(fractionDigits) {
721  var f = TO_INTEGER(fractionDigits);
722  if (f < 0 || f > 20) {
723    throw new $RangeError("toFixed() digits argument must be between 0 and 20");
724  }
725  var x = ToNumber(this);
726  return %NumberToFixed(x, f);
727}
728
729
730// ECMA-262 section 15.7.4.6
731function NumberToExponential(fractionDigits) {
732  var f = -1;
733  if (!IS_UNDEFINED(fractionDigits)) {
734    f = TO_INTEGER(fractionDigits);
735    if (f < 0 || f > 20) {
736      throw new $RangeError("toExponential() argument must be between 0 and 20");
737    }
738  }
739  var x = ToNumber(this);
740  return %NumberToExponential(x, f);
741}
742
743
744// ECMA-262 section 15.7.4.7
745function NumberToPrecision(precision) {
746  if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
747  var p = TO_INTEGER(precision);
748  if (p < 1 || p > 21) {
749    throw new $RangeError("toPrecision() argument must be between 1 and 21");
750  }
751  var x = ToNumber(this);
752  return %NumberToPrecision(x, p);
753}
754
755
756function CheckJSONPrimitive(val) {
757  if (!IsPrimitive(val))
758    throw MakeTypeError('result_not_primitive', ['toJSON', val]);
759  return val;
760}
761
762
763function NumberToJSON(key) {
764  return CheckJSONPrimitive(this.valueOf());
765}
766
767
768// ----------------------------------------------------------------------------
769
770function SetupNumber() {
771  %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
772  // Setup the constructor property on the Number prototype object.
773  %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);
774
775  %OptimizeObjectForAddingMultipleProperties($Number, 5);
776  // ECMA-262 section 15.7.3.1.
777  %SetProperty($Number,
778               "MAX_VALUE",
779               1.7976931348623157e+308,
780               DONT_ENUM | DONT_DELETE | READ_ONLY);
781
782  // ECMA-262 section 15.7.3.2.
783  %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY);
784
785  // ECMA-262 section 15.7.3.3.
786  %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
787
788  // ECMA-262 section 15.7.3.4.
789  %SetProperty($Number,
790               "NEGATIVE_INFINITY",
791               -1/0,
792               DONT_ENUM | DONT_DELETE | READ_ONLY);
793
794  // ECMA-262 section 15.7.3.5.
795  %SetProperty($Number,
796               "POSITIVE_INFINITY",
797               1/0,
798               DONT_ENUM | DONT_DELETE | READ_ONLY);
799  %TransformToFastProperties($Number);
800
801  // Setup non-enumerable functions on the Number prototype object.
802  InstallFunctions($Number.prototype, DONT_ENUM, $Array(
803    "toString", NumberToString,
804    "toLocaleString", NumberToLocaleString,
805    "valueOf", NumberValueOf,
806    "toFixed", NumberToFixed,
807    "toExponential", NumberToExponential,
808    "toPrecision", NumberToPrecision,
809    "toJSON", NumberToJSON
810  ));
811}
812
813SetupNumber();
814
815
816
817// ----------------------------------------------------------------------------
818// Function
819
820$Function.prototype.constructor = $Function;
821
822function FunctionSourceString(func) {
823  if (!IS_FUNCTION(func)) {
824    throw new $TypeError('Function.prototype.toString is not generic');
825  }
826
827  var source = %FunctionGetSourceCode(func);
828  if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
829    var name = %FunctionGetName(func);
830    if (name) {
831      // Mimic what KJS does.
832      return 'function ' + name + '() { [native code] }';
833    } else {
834      return 'function () { [native code] }';
835    }
836  }
837
838  var name = %FunctionGetName(func);
839  return 'function ' + name + source;
840}
841
842
843function FunctionToString() {
844  return FunctionSourceString(this);
845}
846
847
848function NewFunction(arg1) {  // length == 1
849  var n = %_ArgumentsLength();
850  var p = '';
851  if (n > 1) {
852    p = new $Array(n - 1);
853    // Explicitly convert all parameters to strings.
854    // Array.prototype.join replaces null with empty strings which is
855    // not appropriate.
856    for (var i = 0; i < n - 1; i++) p[i] = ToString(%_Arguments(i));
857    p = p.join(',');
858    // If the formal parameters string include ) - an illegal
859    // character - it may make the combined function expression
860    // compile. We avoid this problem by checking for this early on.
861    if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]);
862  }
863  var body = (n > 0) ? ToString(%_Arguments(n - 1)) : '';
864  var source = '(function(' + p + ') {\n' + body + '\n})';
865
866  // The call to SetNewFunctionAttributes will ensure the prototype
867  // property of the resulting function is enumerable (ECMA262, 15.3.5.2).
868  var f = %CompileString(source, false)();
869  %FunctionSetName(f, "anonymous");
870  return %SetNewFunctionAttributes(f);
871}
872
873%SetCode($Function, NewFunction);
874
875// ----------------------------------------------------------------------------
876
877function SetupFunction() {
878  InstallFunctions($Function.prototype, DONT_ENUM, $Array(
879    "toString", FunctionToString
880  ));
881}
882
883SetupFunction();
884