v8natives.js revision c7cc028aaeedbbfa11c11d0b7b243b3d9e837ed9
1// Copyright 2011 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// Helper function used to install functions on objects.
48function InstallFunctions(object, attributes, functions) {
49  if (functions.length >= 8) {
50    %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
51  }
52  for (var i = 0; i < functions.length; i += 2) {
53    var key = functions[i];
54    var f = functions[i + 1];
55    %FunctionSetName(f, key);
56    %FunctionRemovePrototype(f);
57    %SetProperty(object, key, f, attributes);
58    %SetNativeFlag(f);
59  }
60  %ToFastProperties(object);
61}
62
63// Prevents changes to the prototype of a built-infunction.
64// The "prototype" property of the function object is made non-configurable,
65// and the prototype object is made non-extensible. The latter prevents
66// changing the __proto__ property.
67function SetUpLockedPrototype(constructor, fields, methods) {
68  %CheckIsBootstrapping();
69  var prototype = constructor.prototype;
70  // Install functions first, because this function is used to initialize
71  // PropertyDescriptor itself.
72  var property_count = (methods.length >> 1) + (fields ? fields.length : 0);
73  if (property_count >= 4) {
74    %OptimizeObjectForAddingMultipleProperties(prototype, property_count);
75  }
76  if (fields) {
77    for (var i = 0; i < fields.length; i++) {
78      %SetProperty(prototype, fields[i], void 0, DONT_ENUM | DONT_DELETE);
79    }
80  }
81  for (var i = 0; i < methods.length; i += 2) {
82    var key = methods[i];
83    var f = methods[i + 1];
84    %SetProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY);
85    %SetNativeFlag(f);
86  }
87  prototype.__proto__ = null;
88  %ToFastProperties(prototype);
89}
90
91
92// ----------------------------------------------------------------------------
93
94
95// ECMA 262 - 15.1.4
96function GlobalIsNaN(number) {
97  if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
98  return NUMBER_IS_NAN(number);
99}
100
101
102// ECMA 262 - 15.1.5
103function GlobalIsFinite(number) {
104  if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
105  return NUMBER_IS_FINITE(number);
106}
107
108
109// ECMA-262 - 15.1.2.2
110function GlobalParseInt(string, radix) {
111  if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
112    // Some people use parseInt instead of Math.floor.  This
113    // optimization makes parseInt on a Smi 12 times faster (60ns
114    // vs 800ns).  The following optimization makes parseInt on a
115    // non-Smi number 9 times faster (230ns vs 2070ns).  Together
116    // they make parseInt on a string 1.4% slower (274ns vs 270ns).
117    if (%_IsSmi(string)) return string;
118    if (IS_NUMBER(string) &&
119        ((0.01 < string && string < 1e9) ||
120            (-1e9 < string && string < -0.01))) {
121      // Truncate number.
122      return string | 0;
123    }
124    string = TO_STRING_INLINE(string);
125    radix = radix | 0;
126  } else {
127    // The spec says ToString should be evaluated before ToInt32.
128    string = TO_STRING_INLINE(string);
129    radix = TO_INT32(radix);
130    if (!(radix == 0 || (2 <= radix && radix <= 36))) {
131      return $NaN;
132    }
133  }
134
135  if (%_HasCachedArrayIndex(string) &&
136      (radix == 0 || radix == 10)) {
137    return %_GetCachedArrayIndex(string);
138  }
139  return %StringParseInt(string, radix);
140}
141
142
143// ECMA-262 - 15.1.2.3
144function GlobalParseFloat(string) {
145  string = TO_STRING_INLINE(string);
146  if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
147  return %StringParseFloat(string);
148}
149
150
151function GlobalEval(x) {
152  if (!IS_STRING(x)) return x;
153
154  var global_receiver = %GlobalReceiver(global);
155  var global_is_detached = (global === global_receiver);
156
157  // For consistency with JSC we require the global object passed to
158  // eval to be the global object from which 'eval' originated. This
159  // is not mandated by the spec.
160  // We only throw if the global has been detached, since we need the
161  // receiver as this-value for the call.
162  if (global_is_detached) {
163    throw new $EvalError('The "this" value passed to eval must ' +
164                         'be the global object from which eval originated');
165  }
166
167  var f = %CompileString(x);
168  if (!IS_FUNCTION(f)) return f;
169
170  return %_CallFunction(global_receiver, f);
171}
172
173
174// ----------------------------------------------------------------------------
175
176// Set up global object.
177function SetUpGlobal() {
178  %CheckIsBootstrapping();
179  // ECMA 262 - 15.1.1.1.
180  %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
181
182  // ECMA-262 - 15.1.1.2.
183  %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE | READ_ONLY);
184
185  // ECMA-262 - 15.1.1.3.
186  %SetProperty(global, "undefined", void 0,
187               DONT_ENUM | DONT_DELETE | READ_ONLY);
188
189  // Set up non-enumerable function on the global object.
190  InstallFunctions(global, DONT_ENUM, $Array(
191    "isNaN", GlobalIsNaN,
192    "isFinite", GlobalIsFinite,
193    "parseInt", GlobalParseInt,
194    "parseFloat", GlobalParseFloat,
195    "eval", GlobalEval
196  ));
197}
198
199SetUpGlobal();
200
201// ----------------------------------------------------------------------------
202// Boolean (first part of definition)
203
204
205%SetCode($Boolean, function(x) {
206  if (%_IsConstructCall()) {
207    %_SetValueOf(this, ToBoolean(x));
208  } else {
209    return ToBoolean(x);
210  }
211});
212
213%FunctionSetPrototype($Boolean, new $Boolean(false));
214
215%SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM);
216
217// ----------------------------------------------------------------------------
218// Object
219
220$Object.prototype.constructor = $Object;
221
222// ECMA-262 - 15.2.4.2
223function ObjectToString() {
224  if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
225    return '[object Undefined]';
226  }
227  if (IS_NULL(this)) return '[object Null]';
228  return "[object " + %_ClassOf(ToObject(this)) + "]";
229}
230
231
232// ECMA-262 - 15.2.4.3
233function ObjectToLocaleString() {
234  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
235    throw MakeTypeError("called_on_null_or_undefined",
236                        ["Object.prototype.toLocaleString"]);
237  }
238  return this.toString();
239}
240
241
242// ECMA-262 - 15.2.4.4
243function ObjectValueOf() {
244  return ToObject(this);
245}
246
247
248// ECMA-262 - 15.2.4.5
249function ObjectHasOwnProperty(V) {
250  if (%IsJSProxy(this)) {
251    var handler = %GetHandler(this);
252    return CallTrap1(handler, "hasOwn", DerivedHasOwnTrap, TO_STRING_INLINE(V));
253  }
254  return %HasLocalProperty(TO_OBJECT_INLINE(this), TO_STRING_INLINE(V));
255}
256
257
258// ECMA-262 - 15.2.4.6
259function ObjectIsPrototypeOf(V) {
260  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
261    throw MakeTypeError("called_on_null_or_undefined",
262                        ["Object.prototype.isPrototypeOf"]);
263  }
264  if (!IS_SPEC_OBJECT(V)) return false;
265  return %IsInPrototypeChain(this, V);
266}
267
268
269// ECMA-262 - 15.2.4.6
270function ObjectPropertyIsEnumerable(V) {
271  var P = ToString(V);
272  if (%IsJSProxy(this)) {
273    var desc = GetOwnProperty(this, P);
274    return IS_UNDEFINED(desc) ? false : desc.isEnumerable();
275  }
276  return %IsPropertyEnumerable(ToObject(this), P);
277}
278
279
280// Extensions for providing property getters and setters.
281function ObjectDefineGetter(name, fun) {
282  var receiver = this;
283  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
284    receiver = %GlobalReceiver(global);
285  }
286  if (!IS_SPEC_FUNCTION(fun)) {
287    throw new $TypeError(
288        'Object.prototype.__defineGetter__: Expecting function');
289  }
290  var desc = new PropertyDescriptor();
291  desc.setGet(fun);
292  desc.setEnumerable(true);
293  desc.setConfigurable(true);
294  DefineOwnProperty(ToObject(receiver), ToString(name), desc, false);
295}
296
297
298function ObjectLookupGetter(name) {
299  var receiver = this;
300  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
301    receiver = %GlobalReceiver(global);
302  }
303  return %LookupAccessor(ToObject(receiver), ToString(name), GETTER);
304}
305
306
307function ObjectDefineSetter(name, fun) {
308  var receiver = this;
309  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
310    receiver = %GlobalReceiver(global);
311  }
312  if (!IS_SPEC_FUNCTION(fun)) {
313    throw new $TypeError(
314        'Object.prototype.__defineSetter__: Expecting function');
315  }
316  var desc = new PropertyDescriptor();
317  desc.setSet(fun);
318  desc.setEnumerable(true);
319  desc.setConfigurable(true);
320  DefineOwnProperty(ToObject(receiver), ToString(name), desc, false);
321}
322
323
324function ObjectLookupSetter(name) {
325  var receiver = this;
326  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
327    receiver = %GlobalReceiver(global);
328  }
329  return %LookupAccessor(ToObject(receiver), ToString(name), SETTER);
330}
331
332
333function ObjectKeys(obj) {
334  if (!IS_SPEC_OBJECT(obj)) {
335    throw MakeTypeError("obj_ctor_property_non_object", ["keys"]);
336  }
337  if (%IsJSProxy(obj)) {
338    var handler = %GetHandler(obj);
339    var names = CallTrap0(handler, "keys", DerivedKeysTrap);
340    return ToStringArray(names);
341  }
342  return %LocalKeys(obj);
343}
344
345
346// ES5 8.10.1.
347function IsAccessorDescriptor(desc) {
348  if (IS_UNDEFINED(desc)) return false;
349  return desc.hasGetter() || desc.hasSetter();
350}
351
352
353// ES5 8.10.2.
354function IsDataDescriptor(desc) {
355  if (IS_UNDEFINED(desc)) return false;
356  return desc.hasValue() || desc.hasWritable();
357}
358
359
360// ES5 8.10.3.
361function IsGenericDescriptor(desc) {
362  if (IS_UNDEFINED(desc)) return false;
363  return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
364}
365
366
367function IsInconsistentDescriptor(desc) {
368  return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
369}
370
371
372// ES5 8.10.4
373function FromPropertyDescriptor(desc) {
374  if (IS_UNDEFINED(desc)) return desc;
375
376  if (IsDataDescriptor(desc)) {
377    return { value: desc.getValue(),
378             writable: desc.isWritable(),
379             enumerable: desc.isEnumerable(),
380             configurable: desc.isConfigurable() };
381  }
382  // Must be an AccessorDescriptor then. We never return a generic descriptor.
383  return { get: desc.getGet(),
384           set: desc.getSet(),
385           enumerable: desc.isEnumerable(),
386           configurable: desc.isConfigurable() };
387}
388
389
390// Harmony Proxies
391function FromGenericPropertyDescriptor(desc) {
392  if (IS_UNDEFINED(desc)) return desc;
393  var obj = new $Object();
394
395  if (desc.hasValue()) {
396    %IgnoreAttributesAndSetProperty(obj, "value", desc.getValue(), NONE);
397  }
398  if (desc.hasWritable()) {
399    %IgnoreAttributesAndSetProperty(obj, "writable", desc.isWritable(), NONE);
400  }
401  if (desc.hasGetter()) {
402    %IgnoreAttributesAndSetProperty(obj, "get", desc.getGet(), NONE);
403  }
404  if (desc.hasSetter()) {
405    %IgnoreAttributesAndSetProperty(obj, "set", desc.getSet(), NONE);
406  }
407  if (desc.hasEnumerable()) {
408    %IgnoreAttributesAndSetProperty(obj, "enumerable",
409                                    desc.isEnumerable(), NONE);
410  }
411  if (desc.hasConfigurable()) {
412    %IgnoreAttributesAndSetProperty(obj, "configurable",
413                                    desc.isConfigurable(), NONE);
414  }
415  return obj;
416}
417
418
419// ES5 8.10.5.
420function ToPropertyDescriptor(obj) {
421  if (!IS_SPEC_OBJECT(obj)) {
422    throw MakeTypeError("property_desc_object", [obj]);
423  }
424  var desc = new PropertyDescriptor();
425
426  if ("enumerable" in obj) {
427    desc.setEnumerable(ToBoolean(obj.enumerable));
428  }
429
430  if ("configurable" in obj) {
431    desc.setConfigurable(ToBoolean(obj.configurable));
432  }
433
434  if ("value" in obj) {
435    desc.setValue(obj.value);
436  }
437
438  if ("writable" in obj) {
439    desc.setWritable(ToBoolean(obj.writable));
440  }
441
442  if ("get" in obj) {
443    var get = obj.get;
444    if (!IS_UNDEFINED(get) && !IS_SPEC_FUNCTION(get)) {
445      throw MakeTypeError("getter_must_be_callable", [get]);
446    }
447    desc.setGet(get);
448  }
449
450  if ("set" in obj) {
451    var set = obj.set;
452    if (!IS_UNDEFINED(set) && !IS_SPEC_FUNCTION(set)) {
453      throw MakeTypeError("setter_must_be_callable", [set]);
454    }
455    desc.setSet(set);
456  }
457
458  if (IsInconsistentDescriptor(desc)) {
459    throw MakeTypeError("value_and_accessor", [obj]);
460  }
461  return desc;
462}
463
464
465// For Harmony proxies.
466function ToCompletePropertyDescriptor(obj) {
467  var desc = ToPropertyDescriptor(obj);
468  if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) {
469    if (!desc.hasValue()) desc.setValue(void 0);
470    if (!desc.hasWritable()) desc.setWritable(false);
471  } else {
472    // Is accessor descriptor.
473    if (!desc.hasGetter()) desc.setGet(void 0);
474    if (!desc.hasSetter()) desc.setSet(void 0);
475  }
476  if (!desc.hasEnumerable()) desc.setEnumerable(false);
477  if (!desc.hasConfigurable()) desc.setConfigurable(false);
478  return desc;
479}
480
481
482function PropertyDescriptor() {
483  // Initialize here so they are all in-object and have the same map.
484  // Default values from ES5 8.6.1.
485  this.value_ = void 0;
486  this.hasValue_ = false;
487  this.writable_ = false;
488  this.hasWritable_ = false;
489  this.enumerable_ = false;
490  this.hasEnumerable_ = false;
491  this.configurable_ = false;
492  this.hasConfigurable_ = false;
493  this.get_ = void 0;
494  this.hasGetter_ = false;
495  this.set_ = void 0;
496  this.hasSetter_ = false;
497}
498
499SetUpLockedPrototype(PropertyDescriptor, $Array(
500    "value_",
501    "hasValue_",
502    "writable_",
503    "hasWritable_",
504    "enumerable_",
505    "hasEnumerable_",
506    "configurable_",
507    "hasConfigurable_",
508    "get_",
509    "hasGetter_",
510    "set_",
511    "hasSetter_"
512  ), $Array(
513    "toString", function() {
514      return "[object PropertyDescriptor]";
515    },
516    "setValue", function(value) {
517      this.value_ = value;
518      this.hasValue_ = true;
519    },
520    "getValue", function() {
521      return this.value_;
522    },
523    "hasValue", function() {
524      return this.hasValue_;
525    },
526    "setEnumerable", function(enumerable) {
527      this.enumerable_ = enumerable;
528        this.hasEnumerable_ = true;
529    },
530    "isEnumerable", function () {
531      return this.enumerable_;
532    },
533    "hasEnumerable", function() {
534      return this.hasEnumerable_;
535    },
536    "setWritable", function(writable) {
537      this.writable_ = writable;
538      this.hasWritable_ = true;
539    },
540    "isWritable", function() {
541      return this.writable_;
542    },
543    "hasWritable", function() {
544      return this.hasWritable_;
545    },
546    "setConfigurable", function(configurable) {
547      this.configurable_ = configurable;
548      this.hasConfigurable_ = true;
549    },
550    "hasConfigurable", function() {
551      return this.hasConfigurable_;
552    },
553    "isConfigurable", function() {
554      return this.configurable_;
555    },
556    "setGet", function(get) {
557      this.get_ = get;
558        this.hasGetter_ = true;
559    },
560    "getGet", function() {
561      return this.get_;
562    },
563    "hasGetter", function() {
564      return this.hasGetter_;
565    },
566    "setSet", function(set) {
567      this.set_ = set;
568      this.hasSetter_ = true;
569    },
570    "getSet", function() {
571      return this.set_;
572    },
573    "hasSetter", function() {
574      return this.hasSetter_;
575  }));
576
577
578// Converts an array returned from Runtime_GetOwnProperty to an actual
579// property descriptor. For a description of the array layout please
580// see the runtime.cc file.
581function ConvertDescriptorArrayToDescriptor(desc_array) {
582  if (desc_array === false) {
583    throw 'Internal error: invalid desc_array';
584  }
585
586  if (IS_UNDEFINED(desc_array)) {
587    return void 0;
588  }
589
590  var desc = new PropertyDescriptor();
591  // This is an accessor.
592  if (desc_array[IS_ACCESSOR_INDEX]) {
593    desc.setGet(desc_array[GETTER_INDEX]);
594    desc.setSet(desc_array[SETTER_INDEX]);
595  } else {
596    desc.setValue(desc_array[VALUE_INDEX]);
597    desc.setWritable(desc_array[WRITABLE_INDEX]);
598  }
599  desc.setEnumerable(desc_array[ENUMERABLE_INDEX]);
600  desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]);
601
602  return desc;
603}
604
605
606// For Harmony proxies.
607function GetTrap(handler, name, defaultTrap) {
608  var trap = handler[name];
609  if (IS_UNDEFINED(trap)) {
610    if (IS_UNDEFINED(defaultTrap)) {
611      throw MakeTypeError("handler_trap_missing", [handler, name]);
612    }
613    trap = defaultTrap;
614  } else if (!IS_SPEC_FUNCTION(trap)) {
615    throw MakeTypeError("handler_trap_must_be_callable", [handler, name]);
616  }
617  return trap;
618}
619
620
621function CallTrap0(handler, name, defaultTrap) {
622  return %_CallFunction(handler, GetTrap(handler, name, defaultTrap));
623}
624
625
626function CallTrap1(handler, name, defaultTrap, x) {
627  return %_CallFunction(handler, x, GetTrap(handler, name, defaultTrap));
628}
629
630
631function CallTrap2(handler, name, defaultTrap, x, y) {
632  return %_CallFunction(handler, x, y, GetTrap(handler, name, defaultTrap));
633}
634
635
636// ES5 section 8.12.1.
637function GetOwnProperty(obj, v) {
638  var p = ToString(v);
639  if (%IsJSProxy(obj)) {
640    var handler = %GetHandler(obj);
641    var descriptor = CallTrap1(handler, "getOwnPropertyDescriptor", void 0, p);
642    if (IS_UNDEFINED(descriptor)) return descriptor;
643    var desc = ToCompletePropertyDescriptor(descriptor);
644    if (!desc.isConfigurable()) {
645      throw MakeTypeError("proxy_prop_not_configurable",
646                          [handler, "getOwnPropertyDescriptor", p, descriptor]);
647    }
648    return desc;
649  }
650
651  // GetOwnProperty returns an array indexed by the constants
652  // defined in macros.py.
653  // If p is not a property on obj undefined is returned.
654  var props = %GetOwnProperty(ToObject(obj), ToString(v));
655
656  // A false value here means that access checks failed.
657  if (props === false) return void 0;
658
659  return ConvertDescriptorArrayToDescriptor(props);
660}
661
662
663// ES5 section 8.12.7.
664function Delete(obj, p, should_throw) {
665  var desc = GetOwnProperty(obj, p);
666  if (IS_UNDEFINED(desc)) return true;
667  if (desc.isConfigurable()) {
668    %DeleteProperty(obj, p, 0);
669    return true;
670  } else if (should_throw) {
671    throw MakeTypeError("define_disallowed", [p]);
672  } else {
673    return;
674  }
675}
676
677
678// Harmony proxies.
679function DefineProxyProperty(obj, p, attributes, should_throw) {
680  var handler = %GetHandler(obj);
681  var result = CallTrap2(handler, "defineProperty", void 0, p, attributes);
682  if (!ToBoolean(result)) {
683    if (should_throw) {
684      throw MakeTypeError("handler_returned_false",
685                          [handler, "defineProperty"]);
686    } else {
687      return false;
688    }
689  }
690  return true;
691}
692
693
694// ES5 8.12.9.
695function DefineObjectProperty(obj, p, desc, should_throw) {
696  var current_or_access = %GetOwnProperty(ToObject(obj), ToString(p));
697  // A false value here means that access checks failed.
698  if (current_or_access === false) return void 0;
699
700  var current = ConvertDescriptorArrayToDescriptor(current_or_access);
701  var extensible = %IsExtensible(ToObject(obj));
702
703  // Error handling according to spec.
704  // Step 3
705  if (IS_UNDEFINED(current) && !extensible) {
706    if (should_throw) {
707      throw MakeTypeError("define_disallowed", [p]);
708    } else {
709      return false;
710    }
711  }
712
713  if (!IS_UNDEFINED(current)) {
714    // Step 5 and 6
715    if ((IsGenericDescriptor(desc) ||
716         IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
717        (!desc.hasEnumerable() ||
718         SameValue(desc.isEnumerable(), current.isEnumerable())) &&
719        (!desc.hasConfigurable() ||
720         SameValue(desc.isConfigurable(), current.isConfigurable())) &&
721        (!desc.hasWritable() ||
722         SameValue(desc.isWritable(), current.isWritable())) &&
723        (!desc.hasValue() ||
724         SameValue(desc.getValue(), current.getValue())) &&
725        (!desc.hasGetter() ||
726         SameValue(desc.getGet(), current.getGet())) &&
727        (!desc.hasSetter() ||
728         SameValue(desc.getSet(), current.getSet()))) {
729      return true;
730    }
731    if (!current.isConfigurable()) {
732      // Step 7
733      if (desc.isConfigurable() ||
734          (desc.hasEnumerable() &&
735           desc.isEnumerable() != current.isEnumerable())) {
736        if (should_throw) {
737          throw MakeTypeError("redefine_disallowed", [p]);
738        } else {
739          return false;
740        }
741      }
742      // Step 8
743      if (!IsGenericDescriptor(desc)) {
744        // Step 9a
745        if (IsDataDescriptor(current) != IsDataDescriptor(desc)) {
746          if (should_throw) {
747            throw MakeTypeError("redefine_disallowed", [p]);
748          } else {
749            return false;
750          }
751        }
752        // Step 10a
753        if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
754          if (!current.isWritable() && desc.isWritable()) {
755            if (should_throw) {
756              throw MakeTypeError("redefine_disallowed", [p]);
757            } else {
758              return false;
759            }
760          }
761          if (!current.isWritable() && desc.hasValue() &&
762              !SameValue(desc.getValue(), current.getValue())) {
763            if (should_throw) {
764              throw MakeTypeError("redefine_disallowed", [p]);
765            } else {
766              return false;
767            }
768          }
769        }
770        // Step 11
771        if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
772          if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) {
773            if (should_throw) {
774              throw MakeTypeError("redefine_disallowed", [p]);
775            } else {
776              return false;
777            }
778          }
779          if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) {
780            if (should_throw) {
781              throw MakeTypeError("redefine_disallowed", [p]);
782            } else {
783              return false;
784            }
785          }
786        }
787      }
788    }
789  }
790
791  // Send flags - enumerable and configurable are common - writable is
792  // only send to the data descriptor.
793  // Take special care if enumerable and configurable is not defined on
794  // desc (we need to preserve the existing values from current).
795  var flag = NONE;
796  if (desc.hasEnumerable()) {
797    flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
798  } else if (!IS_UNDEFINED(current)) {
799    flag |= current.isEnumerable() ? 0 : DONT_ENUM;
800  } else {
801    flag |= DONT_ENUM;
802  }
803
804  if (desc.hasConfigurable()) {
805    flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
806  } else if (!IS_UNDEFINED(current)) {
807    flag |= current.isConfigurable() ? 0 : DONT_DELETE;
808  } else
809    flag |= DONT_DELETE;
810
811  if (IsDataDescriptor(desc) ||
812      (IsGenericDescriptor(desc) &&
813       (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
814    // There are 3 cases that lead here:
815    // Step 4a - defining a new data property.
816    // Steps 9b & 12 - replacing an existing accessor property with a data
817    //                 property.
818    // Step 12 - updating an existing data property with a data or generic
819    //           descriptor.
820
821    if (desc.hasWritable()) {
822      flag |= desc.isWritable() ? 0 : READ_ONLY;
823    } else if (!IS_UNDEFINED(current)) {
824      flag |= current.isWritable() ? 0 : READ_ONLY;
825    } else {
826      flag |= READ_ONLY;
827    }
828
829    var value = void 0;  // Default value is undefined.
830    if (desc.hasValue()) {
831      value = desc.getValue();
832    } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
833      value = current.getValue();
834    }
835
836    %DefineOrRedefineDataProperty(obj, p, value, flag);
837  } else if (IsGenericDescriptor(desc)) {
838    // Step 12 - updating an existing accessor property with generic
839    //           descriptor. Changing flags only.
840    %DefineOrRedefineAccessorProperty(obj, p, GETTER, current.getGet(), flag);
841  } else {
842    // There are 3 cases that lead here:
843    // Step 4b - defining a new accessor property.
844    // Steps 9c & 12 - replacing an existing data property with an accessor
845    //                 property.
846    // Step 12 - updating an existing accessor property with an accessor
847    //           descriptor.
848    if (desc.hasGetter()) {
849      %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag);
850    }
851    if (desc.hasSetter()) {
852      %DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag);
853    }
854  }
855  return true;
856}
857
858
859// ES5 section 15.4.5.1.
860function DefineArrayProperty(obj, p, desc, should_throw) {
861  // Note that the length of an array is not actually stored as part of the
862  // property, hence we use generated code throughout this function instead of
863  // DefineObjectProperty() to modify its value.
864
865  // Step 3 - Special handling for length property.
866  if (p == "length") {
867    var length = obj.length;
868    if (!desc.hasValue()) {
869      return DefineObjectProperty(obj, "length", desc, should_throw);
870    }
871    var new_length = ToUint32(desc.getValue());
872    if (new_length != ToNumber(desc.getValue())) {
873      throw new $RangeError('defineProperty() array length out of range');
874    }
875    var length_desc = GetOwnProperty(obj, "length");
876    if (new_length != length && !length_desc.isWritable()) {
877      if (should_throw) {
878        throw MakeTypeError("redefine_disallowed", [p]);
879      } else {
880        return false;
881      }
882    }
883    var threw = false;
884    while (new_length < length--) {
885      if (!Delete(obj, ToString(length), false)) {
886        new_length = length + 1;
887        threw = true;
888        break;
889      }
890    }
891    // Make sure the below call to DefineObjectProperty() doesn't overwrite
892    // any magic "length" property by removing the value.
893    obj.length = new_length;
894    desc.value_ = void 0;
895    desc.hasValue_ = false;
896    if (!DefineObjectProperty(obj, "length", desc, should_throw) || threw) {
897      if (should_throw) {
898        throw MakeTypeError("redefine_disallowed", [p]);
899      } else {
900        return false;
901      }
902    }
903    return true;
904  }
905
906  // Step 4 - Special handling for array index.
907  var index = ToUint32(p);
908  if (index == ToNumber(p) && index != 4294967295) {
909    var length = obj.length;
910    var length_desc = GetOwnProperty(obj, "length");
911    if ((index >= length && !length_desc.isWritable()) ||
912        !DefineObjectProperty(obj, p, desc, true)) {
913      if (should_throw) {
914        throw MakeTypeError("define_disallowed", [p]);
915      } else {
916        return false;
917      }
918    }
919    if (index >= length) {
920      obj.length = index + 1;
921    }
922    return true;
923  }
924
925  // Step 5 - Fallback to default implementation.
926  return DefineObjectProperty(obj, p, desc, should_throw);
927}
928
929
930// ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies.
931function DefineOwnProperty(obj, p, desc, should_throw) {
932  if (%IsJSProxy(obj)) {
933    var attributes = FromGenericPropertyDescriptor(desc);
934    return DefineProxyProperty(obj, p, attributes, should_throw);
935  } else if (IS_ARRAY(obj)) {
936    return DefineArrayProperty(obj, p, desc, should_throw);
937  } else {
938    return DefineObjectProperty(obj, p, desc, should_throw);
939  }
940}
941
942
943// ES5 section 15.2.3.2.
944function ObjectGetPrototypeOf(obj) {
945  if (!IS_SPEC_OBJECT(obj)) {
946    throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]);
947  }
948  return %GetPrototype(obj);
949}
950
951
952// ES5 section 15.2.3.3
953function ObjectGetOwnPropertyDescriptor(obj, p) {
954  if (!IS_SPEC_OBJECT(obj)) {
955    throw MakeTypeError("obj_ctor_property_non_object",
956                        ["getOwnPropertyDescriptor"]);
957  }
958  var desc = GetOwnProperty(obj, p);
959  return FromPropertyDescriptor(desc);
960}
961
962
963// For Harmony proxies
964function ToStringArray(obj, trap) {
965  if (!IS_SPEC_OBJECT(obj)) {
966    throw MakeTypeError("proxy_non_object_prop_names", [obj, trap]);
967  }
968  var n = ToUint32(obj.length);
969  var array = new $Array(n);
970  var names = {};  // TODO(rossberg): use sets once they are ready.
971  for (var index = 0; index < n; index++) {
972    var s = ToString(obj[index]);
973    if (s in names) {
974      throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s]);
975    }
976    array[index] = s;
977    names[s] = 0;
978  }
979  return array;
980}
981
982
983// ES5 section 15.2.3.4.
984function ObjectGetOwnPropertyNames(obj) {
985  if (!IS_SPEC_OBJECT(obj)) {
986    throw MakeTypeError("obj_ctor_property_non_object",
987                        ["getOwnPropertyNames"]);
988  }
989  // Special handling for proxies.
990  if (%IsJSProxy(obj)) {
991    var handler = %GetHandler(obj);
992    var names = CallTrap0(handler, "getOwnPropertyNames", void 0);
993    return ToStringArray(names, "getOwnPropertyNames");
994  }
995
996  // Find all the indexed properties.
997
998  // Get the local element names.
999  var propertyNames = %GetLocalElementNames(obj);
1000
1001  // Get names for indexed interceptor properties.
1002  if (%GetInterceptorInfo(obj) & 1) {
1003    var indexedInterceptorNames =
1004        %GetIndexedInterceptorElementNames(obj);
1005    if (indexedInterceptorNames) {
1006      propertyNames = propertyNames.concat(indexedInterceptorNames);
1007    }
1008  }
1009
1010  // Find all the named properties.
1011
1012  // Get the local property names.
1013  propertyNames = propertyNames.concat(%GetLocalPropertyNames(obj));
1014
1015  // Get names for named interceptor properties if any.
1016
1017  if (%GetInterceptorInfo(obj) & 2) {
1018    var namedInterceptorNames =
1019        %GetNamedInterceptorPropertyNames(obj);
1020    if (namedInterceptorNames) {
1021      propertyNames = propertyNames.concat(namedInterceptorNames);
1022    }
1023  }
1024
1025  // Property names are expected to be unique strings.
1026  var propertySet = {};
1027  var j = 0;
1028  for (var i = 0; i < propertyNames.length; ++i) {
1029    var name = ToString(propertyNames[i]);
1030    // We need to check for the exact property value since for intrinsic
1031    // properties like toString if(propertySet["toString"]) will always
1032    // succeed.
1033    if (propertySet[name] === true) {
1034      continue;
1035    }
1036    propertySet[name] = true;
1037    propertyNames[j++] = name;
1038  }
1039  propertyNames.length = j;
1040
1041  return propertyNames;
1042}
1043
1044
1045// ES5 section 15.2.3.5.
1046function ObjectCreate(proto, properties) {
1047  if (!IS_SPEC_OBJECT(proto) && proto !== null) {
1048    throw MakeTypeError("proto_object_or_null", [proto]);
1049  }
1050  var obj = new $Object();
1051  obj.__proto__ = proto;
1052  if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
1053  return obj;
1054}
1055
1056
1057// ES5 section 15.2.3.6.
1058function ObjectDefineProperty(obj, p, attributes) {
1059  if (!IS_SPEC_OBJECT(obj)) {
1060    throw MakeTypeError("obj_ctor_property_non_object", ["defineProperty"]);
1061  }
1062  var name = ToString(p);
1063  if (%IsJSProxy(obj)) {
1064    // Clone the attributes object for protection.
1065    // TODO(rossberg): not spec'ed yet, so not sure if this should involve
1066    // non-own properties as it does (or non-enumerable ones, as it doesn't?).
1067    var attributesClone = {};
1068    for (var a in attributes) {
1069      attributesClone[a] = attributes[a];
1070    }
1071    DefineProxyProperty(obj, name, attributesClone, true);
1072    // The following would implement the spec as in the current proposal,
1073    // but after recent comments on es-discuss, is most likely obsolete.
1074    /*
1075    var defineObj = FromGenericPropertyDescriptor(desc);
1076    var names = ObjectGetOwnPropertyNames(attributes);
1077    var standardNames =
1078      {value: 0, writable: 0, get: 0, set: 0, enumerable: 0, configurable: 0};
1079    for (var i = 0; i < names.length; i++) {
1080      var N = names[i];
1081      if (!(%HasLocalProperty(standardNames, N))) {
1082        var attr = GetOwnProperty(attributes, N);
1083        DefineOwnProperty(descObj, N, attr, true);
1084      }
1085    }
1086    // This is really confusing the types, but it is what the proxies spec
1087    // currently requires:
1088    desc = descObj;
1089    */
1090  } else {
1091    var desc = ToPropertyDescriptor(attributes);
1092    DefineOwnProperty(obj, name, desc, true);
1093  }
1094  return obj;
1095}
1096
1097
1098function GetOwnEnumerablePropertyNames(properties) {
1099  var names = new InternalArray();
1100  for (var key in properties) {
1101    if (%HasLocalProperty(properties, key)) {
1102      names.push(key);
1103    }
1104  }
1105  return names;
1106}
1107
1108
1109// ES5 section 15.2.3.7.
1110function ObjectDefineProperties(obj, properties) {
1111  if (!IS_SPEC_OBJECT(obj)) {
1112    throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]);
1113  }
1114  var props = ToObject(properties);
1115  var names = GetOwnEnumerablePropertyNames(props);
1116  var descriptors = new InternalArray();
1117  for (var i = 0; i < names.length; i++) {
1118    descriptors.push(ToPropertyDescriptor(props[names[i]]));
1119  }
1120  for (var i = 0; i < names.length; i++) {
1121    DefineOwnProperty(obj, names[i], descriptors[i], true);
1122  }
1123  return obj;
1124}
1125
1126
1127// Harmony proxies.
1128function ProxyFix(obj) {
1129  var handler = %GetHandler(obj);
1130  var props = CallTrap0(handler, "fix", void 0);
1131  if (IS_UNDEFINED(props)) {
1132    throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
1133  }
1134
1135  if (%IsJSFunctionProxy(obj)) {
1136    var callTrap = %GetCallTrap(obj);
1137    var constructTrap = %GetConstructTrap(obj);
1138    var code = DelegateCallAndConstruct(callTrap, constructTrap);
1139    %Fix(obj);  // becomes a regular function
1140    %SetCode(obj, code);
1141    // TODO(rossberg): What about length and other properties? Not specified.
1142    // We just put in some half-reasonable defaults for now.
1143    var prototype = new $Object();
1144    $Object.defineProperty(prototype, "constructor",
1145      {value: obj, writable: true, enumerable: false, configurable: true});
1146    // TODO(v8:1530): defineProperty does not handle prototype and length.
1147    %FunctionSetPrototype(obj, prototype);
1148    obj.length = 0;
1149  } else {
1150    %Fix(obj);
1151  }
1152  ObjectDefineProperties(obj, props);
1153}
1154
1155
1156// ES5 section 15.2.3.8.
1157function ObjectSeal(obj) {
1158  if (!IS_SPEC_OBJECT(obj)) {
1159    throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
1160  }
1161  if (%IsJSProxy(obj)) {
1162    ProxyFix(obj);
1163  }
1164  var names = ObjectGetOwnPropertyNames(obj);
1165  for (var i = 0; i < names.length; i++) {
1166    var name = names[i];
1167    var desc = GetOwnProperty(obj, name);
1168    if (desc.isConfigurable()) {
1169      desc.setConfigurable(false);
1170      DefineOwnProperty(obj, name, desc, true);
1171    }
1172  }
1173  %PreventExtensions(obj);
1174  return obj;
1175}
1176
1177
1178// ES5 section 15.2.3.9.
1179function ObjectFreeze(obj) {
1180  if (!IS_SPEC_OBJECT(obj)) {
1181    throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
1182  }
1183  if (%IsJSProxy(obj)) {
1184    ProxyFix(obj);
1185  }
1186  var names = ObjectGetOwnPropertyNames(obj);
1187  for (var i = 0; i < names.length; i++) {
1188    var name = names[i];
1189    var desc = GetOwnProperty(obj, name);
1190    if (desc.isWritable() || desc.isConfigurable()) {
1191      if (IsDataDescriptor(desc)) desc.setWritable(false);
1192      desc.setConfigurable(false);
1193      DefineOwnProperty(obj, name, desc, true);
1194    }
1195  }
1196  %PreventExtensions(obj);
1197  return obj;
1198}
1199
1200
1201// ES5 section 15.2.3.10
1202function ObjectPreventExtension(obj) {
1203  if (!IS_SPEC_OBJECT(obj)) {
1204    throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
1205  }
1206  if (%IsJSProxy(obj)) {
1207    ProxyFix(obj);
1208  }
1209  %PreventExtensions(obj);
1210  return obj;
1211}
1212
1213
1214// ES5 section 15.2.3.11
1215function ObjectIsSealed(obj) {
1216  if (!IS_SPEC_OBJECT(obj)) {
1217    throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
1218  }
1219  if (%IsJSProxy(obj)) {
1220    return false;
1221  }
1222  var names = ObjectGetOwnPropertyNames(obj);
1223  for (var i = 0; i < names.length; i++) {
1224    var name = names[i];
1225    var desc = GetOwnProperty(obj, name);
1226    if (desc.isConfigurable()) return false;
1227  }
1228  if (!ObjectIsExtensible(obj)) {
1229    return true;
1230  }
1231  return false;
1232}
1233
1234
1235// ES5 section 15.2.3.12
1236function ObjectIsFrozen(obj) {
1237  if (!IS_SPEC_OBJECT(obj)) {
1238    throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
1239  }
1240  if (%IsJSProxy(obj)) {
1241    return false;
1242  }
1243  var names = ObjectGetOwnPropertyNames(obj);
1244  for (var i = 0; i < names.length; i++) {
1245    var name = names[i];
1246    var desc = GetOwnProperty(obj, name);
1247    if (IsDataDescriptor(desc) && desc.isWritable()) return false;
1248    if (desc.isConfigurable()) return false;
1249  }
1250  if (!ObjectIsExtensible(obj)) {
1251    return true;
1252  }
1253  return false;
1254}
1255
1256
1257// ES5 section 15.2.3.13
1258function ObjectIsExtensible(obj) {
1259  if (!IS_SPEC_OBJECT(obj)) {
1260    throw MakeTypeError("obj_ctor_property_non_object", ["isExtensible"]);
1261  }
1262  if (%IsJSProxy(obj)) {
1263    return true;
1264  }
1265  return %IsExtensible(obj);
1266}
1267
1268
1269%SetCode($Object, function(x) {
1270  if (%_IsConstructCall()) {
1271    if (x == null) return this;
1272    return ToObject(x);
1273  } else {
1274    if (x == null) return { };
1275    return ToObject(x);
1276  }
1277});
1278
1279%SetExpectedNumberOfProperties($Object, 4);
1280
1281// ----------------------------------------------------------------------------
1282// Object
1283
1284function SetUpObject() {
1285  %CheckIsBootstrapping();
1286  // Set Up non-enumerable functions on the Object.prototype object.
1287  InstallFunctions($Object.prototype, DONT_ENUM, $Array(
1288    "toString", ObjectToString,
1289    "toLocaleString", ObjectToLocaleString,
1290    "valueOf", ObjectValueOf,
1291    "hasOwnProperty", ObjectHasOwnProperty,
1292    "isPrototypeOf", ObjectIsPrototypeOf,
1293    "propertyIsEnumerable", ObjectPropertyIsEnumerable,
1294    "__defineGetter__", ObjectDefineGetter,
1295    "__lookupGetter__", ObjectLookupGetter,
1296    "__defineSetter__", ObjectDefineSetter,
1297    "__lookupSetter__", ObjectLookupSetter
1298  ));
1299  InstallFunctions($Object, DONT_ENUM, $Array(
1300    "keys", ObjectKeys,
1301    "create", ObjectCreate,
1302    "defineProperty", ObjectDefineProperty,
1303    "defineProperties", ObjectDefineProperties,
1304    "freeze", ObjectFreeze,
1305    "getPrototypeOf", ObjectGetPrototypeOf,
1306    "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
1307    "getOwnPropertyNames", ObjectGetOwnPropertyNames,
1308    "isExtensible", ObjectIsExtensible,
1309    "isFrozen", ObjectIsFrozen,
1310    "isSealed", ObjectIsSealed,
1311    "preventExtensions", ObjectPreventExtension,
1312    "seal", ObjectSeal
1313  ));
1314}
1315
1316SetUpObject();
1317
1318// ----------------------------------------------------------------------------
1319// Boolean
1320
1321function BooleanToString() {
1322  // NOTE: Both Boolean objects and values can enter here as
1323  // 'this'. This is not as dictated by ECMA-262.
1324  var b = this;
1325  if (!IS_BOOLEAN(b)) {
1326    if (!IS_BOOLEAN_WRAPPER(b)) {
1327      throw new $TypeError('Boolean.prototype.toString is not generic');
1328    }
1329    b = %_ValueOf(b);
1330  }
1331  return b ? 'true' : 'false';
1332}
1333
1334
1335function BooleanValueOf() {
1336  // NOTE: Both Boolean objects and values can enter here as
1337  // 'this'. This is not as dictated by ECMA-262.
1338  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) {
1339    throw new $TypeError('Boolean.prototype.valueOf is not generic');
1340  }
1341  return %_ValueOf(this);
1342}
1343
1344
1345// ----------------------------------------------------------------------------
1346
1347
1348function SetUpBoolean () {
1349  %CheckIsBootstrapping();
1350  InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
1351    "toString", BooleanToString,
1352    "valueOf", BooleanValueOf
1353  ));
1354}
1355
1356SetUpBoolean();
1357
1358
1359// ----------------------------------------------------------------------------
1360// Number
1361
1362// Set the Number function and constructor.
1363%SetCode($Number, function(x) {
1364  var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
1365  if (%_IsConstructCall()) {
1366    %_SetValueOf(this, value);
1367  } else {
1368    return value;
1369  }
1370});
1371
1372%FunctionSetPrototype($Number, new $Number(0));
1373
1374// ECMA-262 section 15.7.4.2.
1375function NumberToString(radix) {
1376  // NOTE: Both Number objects and values can enter here as
1377  // 'this'. This is not as dictated by ECMA-262.
1378  var number = this;
1379  if (!IS_NUMBER(this)) {
1380    if (!IS_NUMBER_WRAPPER(this)) {
1381      throw new $TypeError('Number.prototype.toString is not generic');
1382    }
1383    // Get the value of this number in case it's an object.
1384    number = %_ValueOf(this);
1385  }
1386  // Fast case: Convert number in radix 10.
1387  if (IS_UNDEFINED(radix) || radix === 10) {
1388    return %_NumberToString(number);
1389  }
1390
1391  // Convert the radix to an integer and check the range.
1392  radix = TO_INTEGER(radix);
1393  if (radix < 2 || radix > 36) {
1394    throw new $RangeError('toString() radix argument must be between 2 and 36');
1395  }
1396  // Convert the number to a string in the given radix.
1397  return %NumberToRadixString(number, radix);
1398}
1399
1400
1401// ECMA-262 section 15.7.4.3
1402function NumberToLocaleString() {
1403  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1404    throw MakeTypeError("called_on_null_or_undefined",
1405                        ["Number.prototype.toLocaleString"]);
1406  }
1407  return this.toString();
1408}
1409
1410
1411// ECMA-262 section 15.7.4.4
1412function NumberValueOf() {
1413  // NOTE: Both Number objects and values can enter here as
1414  // 'this'. This is not as dictated by ECMA-262.
1415  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) {
1416    throw new $TypeError('Number.prototype.valueOf is not generic');
1417  }
1418  return %_ValueOf(this);
1419}
1420
1421
1422// ECMA-262 section 15.7.4.5
1423function NumberToFixed(fractionDigits) {
1424  var f = TO_INTEGER(fractionDigits);
1425  if (f < 0 || f > 20) {
1426    throw new $RangeError("toFixed() digits argument must be between 0 and 20");
1427  }
1428  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1429    throw MakeTypeError("called_on_null_or_undefined",
1430                        ["Number.prototype.toFixed"]);
1431  }
1432  var x = ToNumber(this);
1433  return %NumberToFixed(x, f);
1434}
1435
1436
1437// ECMA-262 section 15.7.4.6
1438function NumberToExponential(fractionDigits) {
1439  var f = -1;
1440  if (!IS_UNDEFINED(fractionDigits)) {
1441    f = TO_INTEGER(fractionDigits);
1442    if (f < 0 || f > 20) {
1443      throw new $RangeError(
1444          "toExponential() argument must be between 0 and 20");
1445    }
1446  }
1447  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1448    throw MakeTypeError("called_on_null_or_undefined",
1449                        ["Number.prototype.toExponential"]);
1450  }
1451  var x = ToNumber(this);
1452  return %NumberToExponential(x, f);
1453}
1454
1455
1456// ECMA-262 section 15.7.4.7
1457function NumberToPrecision(precision) {
1458  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1459    throw MakeTypeError("called_on_null_or_undefined",
1460                        ["Number.prototype.toPrecision"]);
1461  }
1462  if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
1463  var p = TO_INTEGER(precision);
1464  if (p < 1 || p > 21) {
1465    throw new $RangeError("toPrecision() argument must be between 1 and 21");
1466  }
1467  var x = ToNumber(this);
1468  return %NumberToPrecision(x, p);
1469}
1470
1471
1472// ----------------------------------------------------------------------------
1473
1474function SetUpNumber() {
1475  %CheckIsBootstrapping();
1476  %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
1477  // Set up the constructor property on the Number prototype object.
1478  %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);
1479
1480  %OptimizeObjectForAddingMultipleProperties($Number, 5);
1481  // ECMA-262 section 15.7.3.1.
1482  %SetProperty($Number,
1483               "MAX_VALUE",
1484               1.7976931348623157e+308,
1485               DONT_ENUM | DONT_DELETE | READ_ONLY);
1486
1487  // ECMA-262 section 15.7.3.2.
1488  %SetProperty($Number, "MIN_VALUE", 5e-324,
1489               DONT_ENUM | DONT_DELETE | READ_ONLY);
1490
1491  // ECMA-262 section 15.7.3.3.
1492  %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
1493
1494  // ECMA-262 section 15.7.3.4.
1495  %SetProperty($Number,
1496               "NEGATIVE_INFINITY",
1497               -1/0,
1498               DONT_ENUM | DONT_DELETE | READ_ONLY);
1499
1500  // ECMA-262 section 15.7.3.5.
1501  %SetProperty($Number,
1502               "POSITIVE_INFINITY",
1503               1/0,
1504               DONT_ENUM | DONT_DELETE | READ_ONLY);
1505  %ToFastProperties($Number);
1506
1507  // Set up non-enumerable functions on the Number prototype object.
1508  InstallFunctions($Number.prototype, DONT_ENUM, $Array(
1509    "toString", NumberToString,
1510    "toLocaleString", NumberToLocaleString,
1511    "valueOf", NumberValueOf,
1512    "toFixed", NumberToFixed,
1513    "toExponential", NumberToExponential,
1514    "toPrecision", NumberToPrecision
1515  ));
1516}
1517
1518SetUpNumber();
1519
1520
1521// ----------------------------------------------------------------------------
1522// Function
1523
1524$Function.prototype.constructor = $Function;
1525
1526function FunctionSourceString(func) {
1527  while (%IsJSFunctionProxy(func)) {
1528    func = %GetCallTrap(func);
1529  }
1530
1531  if (!IS_FUNCTION(func)) {
1532    throw new $TypeError('Function.prototype.toString is not generic');
1533  }
1534
1535  var source = %FunctionGetSourceCode(func);
1536  if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
1537    var name = %FunctionGetName(func);
1538    if (name) {
1539      // Mimic what KJS does.
1540      return 'function ' + name + '() { [native code] }';
1541    } else {
1542      return 'function () { [native code] }';
1543    }
1544  }
1545
1546  var name = %FunctionNameShouldPrintAsAnonymous(func)
1547      ? 'anonymous'
1548      : %FunctionGetName(func);
1549  return 'function ' + name + source;
1550}
1551
1552
1553function FunctionToString() {
1554  return FunctionSourceString(this);
1555}
1556
1557
1558// ES5 15.3.4.5
1559function FunctionBind(this_arg) { // Length is 1.
1560  if (!IS_SPEC_FUNCTION(this)) {
1561    throw new $TypeError('Bind must be called on a function');
1562  }
1563  var boundFunction = function () {
1564    // Poison .arguments and .caller, but is otherwise not detectable.
1565    "use strict";
1566    // This function must not use any object literals (Object, Array, RegExp),
1567    // since the literals-array is being used to store the bound data.
1568    if (%_IsConstructCall()) {
1569      return %NewObjectFromBound(boundFunction);
1570    }
1571    var bindings = %BoundFunctionGetBindings(boundFunction);
1572
1573    var argc = %_ArgumentsLength();
1574    if (argc == 0) {
1575      return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
1576    }
1577    if (bindings.length === 2) {
1578      return %Apply(bindings[0], bindings[1], arguments, 0, argc);
1579    }
1580    var bound_argc = bindings.length - 2;
1581    var argv = new InternalArray(bound_argc + argc);
1582    for (var i = 0; i < bound_argc; i++) {
1583      argv[i] = bindings[i + 2];
1584    }
1585    for (var j = 0; j < argc; j++) {
1586      argv[i++] = %_Arguments(j);
1587    }
1588    return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
1589  };
1590
1591  %FunctionRemovePrototype(boundFunction);
1592  var new_length = 0;
1593  if (%_ClassOf(this) == "Function") {
1594    // Function or FunctionProxy.
1595    var old_length = this.length;
1596    // FunctionProxies might provide a non-UInt32 value. If so, ignore it.
1597    if ((typeof old_length === "number") &&
1598        ((old_length >>> 0) === old_length)) {
1599      var argc = %_ArgumentsLength();
1600      if (argc > 0) argc--;  // Don't count the thisArg as parameter.
1601      new_length = old_length - argc;
1602      if (new_length < 0) new_length = 0;
1603    }
1604  }
1605  // This runtime function finds any remaining arguments on the stack,
1606  // so we don't pass the arguments object.
1607  var result = %FunctionBindArguments(boundFunction, this,
1608                                      this_arg, new_length);
1609
1610  // We already have caller and arguments properties on functions,
1611  // which are non-configurable. It therefore makes no sence to
1612  // try to redefine these as defined by the spec. The spec says
1613  // that bind should make these throw a TypeError if get or set
1614  // is called and make them non-enumerable and non-configurable.
1615  // To be consistent with our normal functions we leave this as it is.
1616  // TODO(lrn): Do set these to be thrower.
1617  return result;
1618}
1619
1620
1621function NewFunction(arg1) {  // length == 1
1622  var n = %_ArgumentsLength();
1623  var p = '';
1624  if (n > 1) {
1625    p = new InternalArray(n - 1);
1626    for (var i = 0; i < n - 1; i++) p[i] = %_Arguments(i);
1627    p = Join(p, n - 1, ',', NonStringToString);
1628    // If the formal parameters string include ) - an illegal
1629    // character - it may make the combined function expression
1630    // compile. We avoid this problem by checking for this early on.
1631    if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]);
1632  }
1633  var body = (n > 0) ? ToString(%_Arguments(n - 1)) : '';
1634  var source = '(function(' + p + ') {\n' + body + '\n})';
1635
1636  // The call to SetNewFunctionAttributes will ensure the prototype
1637  // property of the resulting function is enumerable (ECMA262, 15.3.5.2).
1638  var f = %CompileString(source)();
1639  %FunctionMarkNameShouldPrintAsAnonymous(f);
1640  return %SetNewFunctionAttributes(f);
1641}
1642
1643%SetCode($Function, NewFunction);
1644
1645// ----------------------------------------------------------------------------
1646
1647function SetUpFunction() {
1648  %CheckIsBootstrapping();
1649  InstallFunctions($Function.prototype, DONT_ENUM, $Array(
1650    "bind", FunctionBind,
1651    "toString", FunctionToString
1652  ));
1653}
1654
1655SetUpFunction();
1656