• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    %FunctionRemovePrototype(f);
58    %SetProperty(object, key, f, attributes);
59  }
60  %ToFastProperties(object);
61}
62
63// Emulates JSC by installing functions on a hidden prototype that
64// lies above the current object/prototype.  This lets you override
65// functions on String.prototype etc. and then restore the old function
66// with delete.  See http://code.google.com/p/chromium/issues/detail?id=1717
67function InstallFunctionsOnHiddenPrototype(object, attributes, functions) {
68  var hidden_prototype = new $Object();
69  %SetHiddenPrototype(object, hidden_prototype);
70  InstallFunctions(hidden_prototype, attributes, functions);
71}
72
73
74// ----------------------------------------------------------------------------
75
76
77// ECMA 262 - 15.1.4
78function GlobalIsNaN(number) {
79  var n = ToNumber(number);
80  return NUMBER_IS_NAN(n);
81}
82
83
84// ECMA 262 - 15.1.5
85function GlobalIsFinite(number) {
86  if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
87
88  // NaN - NaN == NaN, Infinity - Infinity == NaN, -Infinity - -Infinity == NaN.
89  return %_IsSmi(number) || number - number == 0;
90}
91
92
93// ECMA-262 - 15.1.2.2
94function GlobalParseInt(string, radix) {
95  if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
96    // Some people use parseInt instead of Math.floor.  This
97    // optimization makes parseInt on a Smi 12 times faster (60ns
98    // vs 800ns).  The following optimization makes parseInt on a
99    // non-Smi number 9 times faster (230ns vs 2070ns).  Together
100    // they make parseInt on a string 1.4% slower (274ns vs 270ns).
101    if (%_IsSmi(string)) return string;
102    if (IS_NUMBER(string) &&
103        ((0.01 < string && string < 1e9) ||
104            (-1e9 < string && string < -0.01))) {
105      // Truncate number.
106      return string | 0;
107    }
108    if (IS_UNDEFINED(radix)) radix = 0;
109  } else {
110    radix = TO_INT32(radix);
111    if (!(radix == 0 || (2 <= radix && radix <= 36)))
112      return $NaN;
113  }
114  string = TO_STRING_INLINE(string);
115  if (%_HasCachedArrayIndex(string) &&
116      (radix == 0 || radix == 10)) {
117    return %_GetCachedArrayIndex(string);
118  }
119  return %StringParseInt(string, radix);
120}
121
122
123// ECMA-262 - 15.1.2.3
124function GlobalParseFloat(string) {
125  string = TO_STRING_INLINE(string);
126  if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
127  return %StringParseFloat(string);
128}
129
130
131function GlobalEval(x) {
132  if (!IS_STRING(x)) return x;
133
134  var global_receiver = %GlobalReceiver(global);
135  var this_is_global_receiver = (this === global_receiver);
136  var global_is_detached = (global === global_receiver);
137
138  if (!this_is_global_receiver || global_is_detached) {
139    throw new $EvalError('The "this" object passed to eval must ' +
140                         'be the global object from which eval originated');
141  }
142
143  var f = %CompileString(x);
144  if (!IS_FUNCTION(f)) return f;
145
146  return %_CallFunction(this, f);
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  ));
171}
172
173SetupGlobal();
174
175
176// ----------------------------------------------------------------------------
177// Boolean (first part of definition)
178
179
180%SetCode($Boolean, function(x) {
181  if (%_IsConstructCall()) {
182    %_SetValueOf(this, ToBoolean(x));
183  } else {
184    return ToBoolean(x);
185  }
186});
187
188%FunctionSetPrototype($Boolean, new $Boolean(false));
189
190%SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM);
191
192// ----------------------------------------------------------------------------
193// Object
194
195$Object.prototype.constructor = $Object;
196
197// ECMA-262 - 15.2.4.2
198function ObjectToString() {
199  return "[object " + %_ClassOf(ToObject(this)) + "]";
200}
201
202
203// ECMA-262 - 15.2.4.3
204function ObjectToLocaleString() {
205  return this.toString();
206}
207
208
209// ECMA-262 - 15.2.4.4
210function ObjectValueOf() {
211  return ToObject(this);
212}
213
214
215// ECMA-262 - 15.2.4.5
216function ObjectHasOwnProperty(V) {
217  return %HasLocalProperty(ToObject(this), ToString(V));
218}
219
220
221// ECMA-262 - 15.2.4.6
222function ObjectIsPrototypeOf(V) {
223  if (!IS_SPEC_OBJECT(V)) return false;
224  return %IsInPrototypeChain(this, V);
225}
226
227
228// ECMA-262 - 15.2.4.6
229function ObjectPropertyIsEnumerable(V) {
230  return %IsPropertyEnumerable(ToObject(this), ToString(V));
231}
232
233
234// Extensions for providing property getters and setters.
235function ObjectDefineGetter(name, fun) {
236  if (this == null && !IS_UNDETECTABLE(this)) {
237    throw new $TypeError('Object.prototype.__defineGetter__: this is Null');
238  }
239  if (!IS_FUNCTION(fun)) {
240    throw new $TypeError('Object.prototype.__defineGetter__: Expecting function');
241  }
242  var desc = new PropertyDescriptor();
243  desc.setGet(fun);
244  desc.setEnumerable(true);
245  desc.setConfigurable(true);
246  DefineOwnProperty(ToObject(this), ToString(name), desc, false);
247}
248
249
250function ObjectLookupGetter(name) {
251  if (this == null && !IS_UNDETECTABLE(this)) {
252    throw new $TypeError('Object.prototype.__lookupGetter__: this is Null');
253  }
254  return %LookupAccessor(ToObject(this), ToString(name), GETTER);
255}
256
257
258function ObjectDefineSetter(name, fun) {
259  if (this == null && !IS_UNDETECTABLE(this)) {
260    throw new $TypeError('Object.prototype.__defineSetter__: this is Null');
261  }
262  if (!IS_FUNCTION(fun)) {
263    throw new $TypeError(
264        'Object.prototype.__defineSetter__: Expecting function');
265  }
266  var desc = new PropertyDescriptor();
267  desc.setSet(fun);
268  desc.setEnumerable(true);
269  desc.setConfigurable(true);
270  DefineOwnProperty(ToObject(this), ToString(name), desc, false);
271}
272
273
274function ObjectLookupSetter(name) {
275  if (this == null && !IS_UNDETECTABLE(this)) {
276    throw new $TypeError('Object.prototype.__lookupSetter__: this is Null');
277  }
278  return %LookupAccessor(ToObject(this), ToString(name), SETTER);
279}
280
281
282function ObjectKeys(obj) {
283  if (!IS_SPEC_OBJECT(obj))
284    throw MakeTypeError("obj_ctor_property_non_object", ["keys"]);
285  return %LocalKeys(obj);
286}
287
288
289// ES5 8.10.1.
290function IsAccessorDescriptor(desc) {
291  if (IS_UNDEFINED(desc)) return false;
292  return desc.hasGetter_ || desc.hasSetter_;
293}
294
295
296// ES5 8.10.2.
297function IsDataDescriptor(desc) {
298  if (IS_UNDEFINED(desc)) return false;
299  return desc.hasValue_ || desc.hasWritable_;
300}
301
302
303// ES5 8.10.3.
304function IsGenericDescriptor(desc) {
305  return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
306}
307
308
309function IsInconsistentDescriptor(desc) {
310  return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
311}
312
313// ES5 8.10.4
314function FromPropertyDescriptor(desc) {
315  if (IS_UNDEFINED(desc)) return desc;
316  var obj = new $Object();
317  if (IsDataDescriptor(desc)) {
318    obj.value = desc.getValue();
319    obj.writable = desc.isWritable();
320  }
321  if (IsAccessorDescriptor(desc)) {
322    obj.get = desc.getGet();
323    obj.set = desc.getSet();
324  }
325  obj.enumerable = desc.isEnumerable();
326  obj.configurable = desc.isConfigurable();
327  return obj;
328}
329
330// ES5 8.10.5.
331function ToPropertyDescriptor(obj) {
332  if (!IS_SPEC_OBJECT(obj)) {
333    throw MakeTypeError("property_desc_object", [obj]);
334  }
335  var desc = new PropertyDescriptor();
336
337  if ("enumerable" in obj) {
338    desc.setEnumerable(ToBoolean(obj.enumerable));
339  }
340
341  if ("configurable" in obj) {
342    desc.setConfigurable(ToBoolean(obj.configurable));
343  }
344
345  if ("value" in obj) {
346    desc.setValue(obj.value);
347  }
348
349  if ("writable" in obj) {
350    desc.setWritable(ToBoolean(obj.writable));
351  }
352
353  if ("get" in obj) {
354    var get = obj.get;
355    if (!IS_UNDEFINED(get) && !IS_FUNCTION(get)) {
356      throw MakeTypeError("getter_must_be_callable", [get]);
357    }
358    desc.setGet(get);
359  }
360
361  if ("set" in obj) {
362    var set = obj.set;
363    if (!IS_UNDEFINED(set) && !IS_FUNCTION(set)) {
364      throw MakeTypeError("setter_must_be_callable", [set]);
365    }
366    desc.setSet(set);
367  }
368
369  if (IsInconsistentDescriptor(desc)) {
370    throw MakeTypeError("value_and_accessor", [obj]);
371  }
372  return desc;
373}
374
375
376function PropertyDescriptor() {
377  // Initialize here so they are all in-object and have the same map.
378  // Default values from ES5 8.6.1.
379  this.value_ = void 0;
380  this.hasValue_ = false;
381  this.writable_ = false;
382  this.hasWritable_ = false;
383  this.enumerable_ = false;
384  this.hasEnumerable_ = false;
385  this.configurable_ = false;
386  this.hasConfigurable_ = false;
387  this.get_ = void 0;
388  this.hasGetter_ = false;
389  this.set_ = void 0;
390  this.hasSetter_ = false;
391}
392
393PropertyDescriptor.prototype.__proto__ = null;
394PropertyDescriptor.prototype.toString = function() {
395  return "[object PropertyDescriptor]";
396};
397
398PropertyDescriptor.prototype.setValue = function(value) {
399  this.value_ = value;
400  this.hasValue_ = true;
401}
402
403
404PropertyDescriptor.prototype.getValue = function() {
405  return this.value_;
406}
407
408
409PropertyDescriptor.prototype.hasValue = function() {
410  return this.hasValue_;
411}
412
413
414PropertyDescriptor.prototype.setEnumerable = function(enumerable) {
415  this.enumerable_ = enumerable;
416  this.hasEnumerable_ = true;
417}
418
419
420PropertyDescriptor.prototype.isEnumerable = function () {
421  return this.enumerable_;
422}
423
424
425PropertyDescriptor.prototype.hasEnumerable = function() {
426  return this.hasEnumerable_;
427}
428
429
430PropertyDescriptor.prototype.setWritable = function(writable) {
431  this.writable_ = writable;
432  this.hasWritable_ = true;
433}
434
435
436PropertyDescriptor.prototype.isWritable = function() {
437  return this.writable_;
438}
439
440
441PropertyDescriptor.prototype.hasWritable = function() {
442  return this.hasWritable_;
443}
444
445
446PropertyDescriptor.prototype.setConfigurable = function(configurable) {
447  this.configurable_ = configurable;
448  this.hasConfigurable_ = true;
449}
450
451
452PropertyDescriptor.prototype.hasConfigurable = function() {
453  return this.hasConfigurable_;
454}
455
456
457PropertyDescriptor.prototype.isConfigurable = function() {
458  return this.configurable_;
459}
460
461
462PropertyDescriptor.prototype.setGet = function(get) {
463  this.get_ = get;
464  this.hasGetter_ = true;
465}
466
467
468PropertyDescriptor.prototype.getGet = function() {
469  return this.get_;
470}
471
472
473PropertyDescriptor.prototype.hasGetter = function() {
474  return this.hasGetter_;
475}
476
477
478PropertyDescriptor.prototype.setSet = function(set) {
479  this.set_ = set;
480  this.hasSetter_ = true;
481}
482
483
484PropertyDescriptor.prototype.getSet = function() {
485  return this.set_;
486}
487
488
489PropertyDescriptor.prototype.hasSetter = function() {
490  return this.hasSetter_;
491}
492
493
494// Converts an array returned from Runtime_GetOwnProperty to an actual
495// property descriptor. For a description of the array layout please
496// see the runtime.cc file.
497function ConvertDescriptorArrayToDescriptor(desc_array) {
498  if (desc_array === false) {
499    throw 'Internal error: invalid desc_array';
500  }
501
502  if (IS_UNDEFINED(desc_array)) {
503    return void 0;
504  }
505
506  var desc = new PropertyDescriptor();
507  // This is an accessor.
508  if (desc_array[IS_ACCESSOR_INDEX]) {
509    desc.setGet(desc_array[GETTER_INDEX]);
510    desc.setSet(desc_array[SETTER_INDEX]);
511  } else {
512    desc.setValue(desc_array[VALUE_INDEX]);
513    desc.setWritable(desc_array[WRITABLE_INDEX]);
514  }
515  desc.setEnumerable(desc_array[ENUMERABLE_INDEX]);
516  desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]);
517
518  return desc;
519}
520
521
522// ES5 section 8.12.2.
523function GetProperty(obj, p) {
524  var prop = GetOwnProperty(obj);
525  if (!IS_UNDEFINED(prop)) return prop;
526  var proto = obj.__proto__;
527  if (IS_NULL(proto)) return void 0;
528  return GetProperty(proto, p);
529}
530
531
532// ES5 section 8.12.6
533function HasProperty(obj, p) {
534  var desc = GetProperty(obj, p);
535  return IS_UNDEFINED(desc) ? false : true;
536}
537
538
539// ES5 section 8.12.1.
540function GetOwnProperty(obj, p) {
541  // GetOwnProperty returns an array indexed by the constants
542  // defined in macros.py.
543  // If p is not a property on obj undefined is returned.
544  var props = %GetOwnProperty(ToObject(obj), ToString(p));
545
546  // A false value here means that access checks failed.
547  if (props === false) return void 0;
548
549  return ConvertDescriptorArrayToDescriptor(props);
550}
551
552
553// ES5 8.12.9.
554function DefineOwnProperty(obj, p, desc, should_throw) {
555  var current_or_access = %GetOwnProperty(ToObject(obj), ToString(p));
556  // A false value here means that access checks failed.
557  if (current_or_access === false) return void 0;
558
559  var current = ConvertDescriptorArrayToDescriptor(current_or_access);
560  var extensible = %IsExtensible(ToObject(obj));
561
562  // Error handling according to spec.
563  // Step 3
564  if (IS_UNDEFINED(current) && !extensible) {
565    if (should_throw) {
566      throw MakeTypeError("define_disallowed", ["defineProperty"]);
567    } else {
568      return;
569    }
570  }
571
572  if (!IS_UNDEFINED(current)) {
573    // Step 5 and 6
574    if ((IsGenericDescriptor(desc) ||
575         IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
576        (!desc.hasEnumerable() ||
577         SameValue(desc.isEnumerable(), current.isEnumerable())) &&
578        (!desc.hasConfigurable() ||
579         SameValue(desc.isConfigurable(), current.isConfigurable())) &&
580        (!desc.hasWritable() ||
581         SameValue(desc.isWritable(), current.isWritable())) &&
582        (!desc.hasValue() ||
583         SameValue(desc.getValue(), current.getValue())) &&
584        (!desc.hasGetter() ||
585         SameValue(desc.getGet(), current.getGet())) &&
586        (!desc.hasSetter() ||
587         SameValue(desc.getSet(), current.getSet()))) {
588      return true;
589    }
590    if (!current.isConfigurable()) {
591      // Step 7
592      if (desc.isConfigurable() ||
593          (desc.hasEnumerable() &&
594           desc.isEnumerable() != current.isEnumerable())) {
595        if (should_throw) {
596          throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
597        } else {
598          return;
599        }
600      }
601      // Step 8
602      if (!IsGenericDescriptor(desc)) {
603        // Step 9a
604        if (IsDataDescriptor(current) != IsDataDescriptor(desc)) {
605          if (should_throw) {
606            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
607          } else {
608            return;
609          }
610        }
611        // Step 10a
612        if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
613          if (!current.isWritable() && desc.isWritable()) {
614            if (should_throw) {
615              throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
616            } else {
617              return;
618            }
619          }
620          if (!current.isWritable() && desc.hasValue() &&
621              !SameValue(desc.getValue(), current.getValue())) {
622            if (should_throw) {
623              throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
624            } else {
625              return;
626            }
627          }
628        }
629        // Step 11
630        if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
631          if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) {
632            if (should_throw) {
633              throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
634            } else {
635              return;
636            }
637          }
638          if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) {
639            if (should_throw) {
640              throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
641            } else {
642              return;
643            }
644          }
645        }
646      }
647    }
648  }
649
650  // Send flags - enumerable and configurable are common - writable is
651  // only send to the data descriptor.
652  // Take special care if enumerable and configurable is not defined on
653  // desc (we need to preserve the existing values from current).
654  var flag = NONE;
655  if (desc.hasEnumerable()) {
656    flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
657  } else if (!IS_UNDEFINED(current)) {
658    flag |= current.isEnumerable() ? 0 : DONT_ENUM;
659  } else {
660    flag |= DONT_ENUM;
661  }
662
663  if (desc.hasConfigurable()) {
664    flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
665  } else if (!IS_UNDEFINED(current)) {
666    flag |= current.isConfigurable() ? 0 : DONT_DELETE;
667  } else
668    flag |= DONT_DELETE;
669
670  if (IsDataDescriptor(desc) ||
671      (IsGenericDescriptor(desc) &&
672       (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
673    // There are 3 cases that lead here:
674    // Step 4a - defining a new data property.
675    // Steps 9b & 12 - replacing an existing accessor property with a data
676    //                 property.
677    // Step 12 - updating an existing data property with a data or generic
678    //           descriptor.
679
680    if (desc.hasWritable()) {
681      flag |= desc.isWritable() ? 0 : READ_ONLY;
682    } else if (!IS_UNDEFINED(current)) {
683      flag |= current.isWritable() ? 0 : READ_ONLY;
684    } else {
685      flag |= READ_ONLY;
686    }
687
688    var value = void 0;  // Default value is undefined.
689    if (desc.hasValue()) {
690      value = desc.getValue();
691    } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
692      value = current.getValue();
693    }
694
695    %DefineOrRedefineDataProperty(obj, p, value, flag);
696  } else if (IsGenericDescriptor(desc)) {
697    // Step 12 - updating an existing accessor property with generic
698    //           descriptor. Changing flags only.
699    %DefineOrRedefineAccessorProperty(obj, p, GETTER, current.getGet(), flag);
700  } else {
701    // There are 3 cases that lead here:
702    // Step 4b - defining a new accessor property.
703    // Steps 9c & 12 - replacing an existing data property with an accessor
704    //                 property.
705    // Step 12 - updating an existing accessor property with an accessor
706    //           descriptor.
707    if (desc.hasGetter()) {
708      %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag);
709    }
710    if (desc.hasSetter()) {
711      %DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag);
712    }
713  }
714  return true;
715}
716
717
718// ES5 section 15.2.3.2.
719function ObjectGetPrototypeOf(obj) {
720  if (!IS_SPEC_OBJECT(obj))
721    throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]);
722  return obj.__proto__;
723}
724
725
726// ES5 section 15.2.3.3
727function ObjectGetOwnPropertyDescriptor(obj, p) {
728  if (!IS_SPEC_OBJECT(obj))
729    throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]);
730  var desc = GetOwnProperty(obj, p);
731  return FromPropertyDescriptor(desc);
732}
733
734
735// ES5 section 15.2.3.4.
736function ObjectGetOwnPropertyNames(obj) {
737  if (!IS_SPEC_OBJECT(obj))
738    throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]);
739
740  // Find all the indexed properties.
741
742  // Get the local element names.
743  var propertyNames = %GetLocalElementNames(obj);
744
745  // Get names for indexed interceptor properties.
746  if (%GetInterceptorInfo(obj) & 1) {
747    var indexedInterceptorNames =
748        %GetIndexedInterceptorElementNames(obj);
749    if (indexedInterceptorNames)
750      propertyNames = propertyNames.concat(indexedInterceptorNames);
751  }
752
753  // Find all the named properties.
754
755  // Get the local property names.
756  propertyNames = propertyNames.concat(%GetLocalPropertyNames(obj));
757
758  // Get names for named interceptor properties if any.
759
760  if (%GetInterceptorInfo(obj) & 2) {
761    var namedInterceptorNames =
762        %GetNamedInterceptorPropertyNames(obj);
763    if (namedInterceptorNames) {
764      propertyNames = propertyNames.concat(namedInterceptorNames);
765    }
766  }
767
768  // Property names are expected to be unique strings.
769  var propertySet = {};
770  var j = 0;
771  for (var i = 0; i < propertyNames.length; ++i) {
772    var name = ToString(propertyNames[i]);
773    // We need to check for the exact property value since for intrinsic
774    // properties like toString if(propertySet["toString"]) will always
775    // succeed.
776    if (propertySet[name] === true)
777      continue;
778    propertySet[name] = true;
779    propertyNames[j++] = name;
780  }
781  propertyNames.length = j;
782
783  return propertyNames;
784}
785
786
787// ES5 section 15.2.3.5.
788function ObjectCreate(proto, properties) {
789  if (!IS_SPEC_OBJECT(proto) && proto !== null) {
790    throw MakeTypeError("proto_object_or_null", [proto]);
791  }
792  var obj = new $Object();
793  obj.__proto__ = proto;
794  if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
795  return obj;
796}
797
798
799// ES5 section 15.2.3.6.
800function ObjectDefineProperty(obj, p, attributes) {
801  if (!IS_SPEC_OBJECT(obj)) {
802    throw MakeTypeError("obj_ctor_property_non_object", ["defineProperty"]);
803  }
804  var name = ToString(p);
805  var desc = ToPropertyDescriptor(attributes);
806  DefineOwnProperty(obj, name, desc, true);
807  return obj;
808}
809
810
811// ES5 section 15.2.3.7.
812function ObjectDefineProperties(obj, properties) {
813  if (!IS_SPEC_OBJECT(obj))
814    throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]);
815  var props = ToObject(properties);
816  var key_values = [];
817  for (var key in props) {
818    if (%HasLocalProperty(props, key)) {
819      key_values.push(key);
820      var value = props[key];
821      var desc = ToPropertyDescriptor(value);
822      key_values.push(desc);
823    }
824  }
825  for (var i = 0; i < key_values.length; i += 2) {
826    var key = key_values[i];
827    var desc = key_values[i + 1];
828    DefineOwnProperty(obj, key, desc, true);
829  }
830  return obj;
831}
832
833
834// ES5 section 15.2.3.8.
835function ObjectSeal(obj) {
836  if (!IS_SPEC_OBJECT(obj)) {
837    throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
838  }
839  var names = ObjectGetOwnPropertyNames(obj);
840  for (var i = 0; i < names.length; i++) {
841    var name = names[i];
842    var desc = GetOwnProperty(obj, name);
843    if (desc.isConfigurable()) desc.setConfigurable(false);
844    DefineOwnProperty(obj, name, desc, true);
845  }
846  return ObjectPreventExtension(obj);
847}
848
849
850// ES5 section 15.2.3.9.
851function ObjectFreeze(obj) {
852  if (!IS_SPEC_OBJECT(obj)) {
853    throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
854  }
855  var names = ObjectGetOwnPropertyNames(obj);
856  for (var i = 0; i < names.length; i++) {
857    var name = names[i];
858    var desc = GetOwnProperty(obj, name);
859    if (IsDataDescriptor(desc)) desc.setWritable(false);
860    if (desc.isConfigurable()) desc.setConfigurable(false);
861    DefineOwnProperty(obj, name, desc, true);
862  }
863  return ObjectPreventExtension(obj);
864}
865
866
867// ES5 section 15.2.3.10
868function ObjectPreventExtension(obj) {
869  if (!IS_SPEC_OBJECT(obj)) {
870    throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
871  }
872  %PreventExtensions(obj);
873  return obj;
874}
875
876
877// ES5 section 15.2.3.11
878function ObjectIsSealed(obj) {
879  if (!IS_SPEC_OBJECT(obj)) {
880    throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
881  }
882  var names = ObjectGetOwnPropertyNames(obj);
883  for (var i = 0; i < names.length; i++) {
884    var name = names[i];
885    var desc = GetOwnProperty(obj, name);
886    if (desc.isConfigurable()) return false;
887  }
888  if (!ObjectIsExtensible(obj)) {
889    return true;
890  }
891  return false;
892}
893
894
895// ES5 section 15.2.3.12
896function ObjectIsFrozen(obj) {
897  if (!IS_SPEC_OBJECT(obj)) {
898    throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
899  }
900  var names = ObjectGetOwnPropertyNames(obj);
901  for (var i = 0; i < names.length; i++) {
902    var name = names[i];
903    var desc = GetOwnProperty(obj, name);
904    if (IsDataDescriptor(desc) && desc.isWritable()) return false;
905    if (desc.isConfigurable()) return false;
906  }
907  if (!ObjectIsExtensible(obj)) {
908    return true;
909  }
910  return false;
911}
912
913
914// ES5 section 15.2.3.13
915function ObjectIsExtensible(obj) {
916  if (!IS_SPEC_OBJECT(obj)) {
917    throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
918  }
919  return %IsExtensible(obj);
920}
921
922
923%SetCode($Object, function(x) {
924  if (%_IsConstructCall()) {
925    if (x == null) return this;
926    return ToObject(x);
927  } else {
928    if (x == null) return { };
929    return ToObject(x);
930  }
931});
932
933%SetExpectedNumberOfProperties($Object, 4);
934
935// ----------------------------------------------------------------------------
936
937
938function SetupObject() {
939  // Setup non-enumerable functions on the Object.prototype object.
940  InstallFunctions($Object.prototype, DONT_ENUM, $Array(
941    "toString", ObjectToString,
942    "toLocaleString", ObjectToLocaleString,
943    "valueOf", ObjectValueOf,
944    "hasOwnProperty", ObjectHasOwnProperty,
945    "isPrototypeOf", ObjectIsPrototypeOf,
946    "propertyIsEnumerable", ObjectPropertyIsEnumerable,
947    "__defineGetter__", ObjectDefineGetter,
948    "__lookupGetter__", ObjectLookupGetter,
949    "__defineSetter__", ObjectDefineSetter,
950    "__lookupSetter__", ObjectLookupSetter
951  ));
952  InstallFunctions($Object, DONT_ENUM, $Array(
953    "keys", ObjectKeys,
954    "create", ObjectCreate,
955    "defineProperty", ObjectDefineProperty,
956    "defineProperties", ObjectDefineProperties,
957    "freeze", ObjectFreeze,
958    "getPrototypeOf", ObjectGetPrototypeOf,
959    "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
960    "getOwnPropertyNames", ObjectGetOwnPropertyNames,
961    "isExtensible", ObjectIsExtensible,
962    "isFrozen", ObjectIsFrozen,
963    "isSealed", ObjectIsSealed,
964    "preventExtensions", ObjectPreventExtension,
965    "seal", ObjectSeal
966  ));
967}
968
969SetupObject();
970
971
972// ----------------------------------------------------------------------------
973// Boolean
974
975function BooleanToString() {
976  // NOTE: Both Boolean objects and values can enter here as
977  // 'this'. This is not as dictated by ECMA-262.
978  var b = this;
979  if (!IS_BOOLEAN(b)) {
980    if (!IS_BOOLEAN_WRAPPER(b)) {
981      throw new $TypeError('Boolean.prototype.toString is not generic');
982    }
983    b = %_ValueOf(b);
984  }
985  return b ? 'true' : 'false';
986}
987
988
989function BooleanValueOf() {
990  // NOTE: Both Boolean objects and values can enter here as
991  // 'this'. This is not as dictated by ECMA-262.
992  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this))
993    throw new $TypeError('Boolean.prototype.valueOf is not generic');
994  return %_ValueOf(this);
995}
996
997
998// ----------------------------------------------------------------------------
999
1000
1001function SetupBoolean() {
1002  InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
1003    "toString", BooleanToString,
1004    "valueOf", BooleanValueOf
1005  ));
1006}
1007
1008SetupBoolean();
1009
1010// ----------------------------------------------------------------------------
1011// Number
1012
1013// Set the Number function and constructor.
1014%SetCode($Number, function(x) {
1015  var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
1016  if (%_IsConstructCall()) {
1017    %_SetValueOf(this, value);
1018  } else {
1019    return value;
1020  }
1021});
1022
1023%FunctionSetPrototype($Number, new $Number(0));
1024
1025// ECMA-262 section 15.7.4.2.
1026function NumberToString(radix) {
1027  // NOTE: Both Number objects and values can enter here as
1028  // 'this'. This is not as dictated by ECMA-262.
1029  var number = this;
1030  if (!IS_NUMBER(this)) {
1031    if (!IS_NUMBER_WRAPPER(this))
1032      throw new $TypeError('Number.prototype.toString is not generic');
1033    // Get the value of this number in case it's an object.
1034    number = %_ValueOf(this);
1035  }
1036  // Fast case: Convert number in radix 10.
1037  if (IS_UNDEFINED(radix) || radix === 10) {
1038    return %_NumberToString(number);
1039  }
1040
1041  // Convert the radix to an integer and check the range.
1042  radix = TO_INTEGER(radix);
1043  if (radix < 2 || radix > 36) {
1044    throw new $RangeError('toString() radix argument must be between 2 and 36');
1045  }
1046  // Convert the number to a string in the given radix.
1047  return %NumberToRadixString(number, radix);
1048}
1049
1050
1051// ECMA-262 section 15.7.4.3
1052function NumberToLocaleString() {
1053  return this.toString();
1054}
1055
1056
1057// ECMA-262 section 15.7.4.4
1058function NumberValueOf() {
1059  // NOTE: Both Number objects and values can enter here as
1060  // 'this'. This is not as dictated by ECMA-262.
1061  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this))
1062    throw new $TypeError('Number.prototype.valueOf is not generic');
1063  return %_ValueOf(this);
1064}
1065
1066
1067// ECMA-262 section 15.7.4.5
1068function NumberToFixed(fractionDigits) {
1069  var f = TO_INTEGER(fractionDigits);
1070  if (f < 0 || f > 20) {
1071    throw new $RangeError("toFixed() digits argument must be between 0 and 20");
1072  }
1073  var x = ToNumber(this);
1074  return %NumberToFixed(x, f);
1075}
1076
1077
1078// ECMA-262 section 15.7.4.6
1079function NumberToExponential(fractionDigits) {
1080  var f = -1;
1081  if (!IS_UNDEFINED(fractionDigits)) {
1082    f = TO_INTEGER(fractionDigits);
1083    if (f < 0 || f > 20) {
1084      throw new $RangeError("toExponential() argument must be between 0 and 20");
1085    }
1086  }
1087  var x = ToNumber(this);
1088  return %NumberToExponential(x, f);
1089}
1090
1091
1092// ECMA-262 section 15.7.4.7
1093function NumberToPrecision(precision) {
1094  if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
1095  var p = TO_INTEGER(precision);
1096  if (p < 1 || p > 21) {
1097    throw new $RangeError("toPrecision() argument must be between 1 and 21");
1098  }
1099  var x = ToNumber(this);
1100  return %NumberToPrecision(x, p);
1101}
1102
1103
1104// ----------------------------------------------------------------------------
1105
1106function SetupNumber() {
1107  %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
1108  // Setup the constructor property on the Number prototype object.
1109  %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);
1110
1111  %OptimizeObjectForAddingMultipleProperties($Number, 5);
1112  // ECMA-262 section 15.7.3.1.
1113  %SetProperty($Number,
1114               "MAX_VALUE",
1115               1.7976931348623157e+308,
1116               DONT_ENUM | DONT_DELETE | READ_ONLY);
1117
1118  // ECMA-262 section 15.7.3.2.
1119  %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY);
1120
1121  // ECMA-262 section 15.7.3.3.
1122  %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
1123
1124  // ECMA-262 section 15.7.3.4.
1125  %SetProperty($Number,
1126               "NEGATIVE_INFINITY",
1127               -1/0,
1128               DONT_ENUM | DONT_DELETE | READ_ONLY);
1129
1130  // ECMA-262 section 15.7.3.5.
1131  %SetProperty($Number,
1132               "POSITIVE_INFINITY",
1133               1/0,
1134               DONT_ENUM | DONT_DELETE | READ_ONLY);
1135  %ToFastProperties($Number);
1136
1137  // Setup non-enumerable functions on the Number prototype object.
1138  InstallFunctions($Number.prototype, DONT_ENUM, $Array(
1139    "toString", NumberToString,
1140    "toLocaleString", NumberToLocaleString,
1141    "valueOf", NumberValueOf,
1142    "toFixed", NumberToFixed,
1143    "toExponential", NumberToExponential,
1144    "toPrecision", NumberToPrecision
1145  ));
1146}
1147
1148SetupNumber();
1149
1150
1151// ----------------------------------------------------------------------------
1152// Function
1153
1154$Function.prototype.constructor = $Function;
1155
1156function FunctionSourceString(func) {
1157  if (!IS_FUNCTION(func)) {
1158    throw new $TypeError('Function.prototype.toString is not generic');
1159  }
1160
1161  var source = %FunctionGetSourceCode(func);
1162  if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
1163    var name = %FunctionGetName(func);
1164    if (name) {
1165      // Mimic what KJS does.
1166      return 'function ' + name + '() { [native code] }';
1167    } else {
1168      return 'function () { [native code] }';
1169    }
1170  }
1171
1172  var name = %FunctionGetName(func);
1173  return 'function ' + name + source;
1174}
1175
1176
1177function FunctionToString() {
1178  return FunctionSourceString(this);
1179}
1180
1181
1182// ES5 15.3.4.5
1183function FunctionBind(this_arg) { // Length is 1.
1184  if (!IS_FUNCTION(this)) {
1185      throw new $TypeError('Bind must be called on a function');
1186  }
1187  // this_arg is not an argument that should be bound.
1188  var argc_bound = (%_ArgumentsLength() || 1) - 1;
1189  var fn = this;
1190  if (argc_bound == 0) {
1191    var result = function() {
1192      if (%_IsConstructCall()) {
1193        // %NewObjectFromBound implicitly uses arguments passed to this
1194        // function. We do not pass the arguments object explicitly to avoid
1195        // materializing it and guarantee that this function will be optimized.
1196        return %NewObjectFromBound(fn, null);
1197      }
1198
1199      return fn.apply(this_arg, arguments);
1200    };
1201  } else {
1202    var bound_args = new InternalArray(argc_bound);
1203    for(var i = 0; i < argc_bound; i++) {
1204      bound_args[i] = %_Arguments(i+1);
1205    }
1206
1207    var result = function() {
1208      // If this is a construct call we use a special runtime method
1209      // to generate the actual object using the bound function.
1210      if (%_IsConstructCall()) {
1211        // %NewObjectFromBound implicitly uses arguments passed to this
1212        // function. We do not pass the arguments object explicitly to avoid
1213        // materializing it and guarantee that this function will be optimized.
1214        return %NewObjectFromBound(fn, bound_args);
1215      }
1216
1217      // Combine the args we got from the bind call with the args
1218      // given as argument to the invocation.
1219      var argc = %_ArgumentsLength();
1220      var args = new InternalArray(argc + argc_bound);
1221      // Add bound arguments.
1222      for (var i = 0; i < argc_bound; i++) {
1223        args[i] = bound_args[i];
1224      }
1225      // Add arguments from call.
1226      for (var i = 0; i < argc; i++) {
1227        args[argc_bound + i] = %_Arguments(i);
1228      }
1229      return fn.apply(this_arg, args);
1230    };
1231  }
1232
1233  // We already have caller and arguments properties on functions,
1234  // which are non-configurable. It therefore makes no sence to
1235  // try to redefine these as defined by the spec. The spec says
1236  // that bind should make these throw a TypeError if get or set
1237  // is called and make them non-enumerable and non-configurable.
1238  // To be consistent with our normal functions we leave this as it is.
1239
1240  // Set the correct length.
1241  var length = (this.length - argc_bound) > 0 ? this.length - argc_bound : 0;
1242  %FunctionSetLength(result, length);
1243
1244  return result;
1245}
1246
1247
1248function NewFunction(arg1) {  // length == 1
1249  var n = %_ArgumentsLength();
1250  var p = '';
1251  if (n > 1) {
1252    p = new InternalArray(n - 1);
1253    for (var i = 0; i < n - 1; i++) p[i] = %_Arguments(i);
1254    p = Join(p, n - 1, ',', NonStringToString);
1255    // If the formal parameters string include ) - an illegal
1256    // character - it may make the combined function expression
1257    // compile. We avoid this problem by checking for this early on.
1258    if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]);
1259  }
1260  var body = (n > 0) ? ToString(%_Arguments(n - 1)) : '';
1261  var source = '(function(' + p + ') {\n' + body + '\n})';
1262
1263  // The call to SetNewFunctionAttributes will ensure the prototype
1264  // property of the resulting function is enumerable (ECMA262, 15.3.5.2).
1265  var f = %CompileString(source)();
1266  %FunctionSetName(f, "anonymous");
1267  return %SetNewFunctionAttributes(f);
1268}
1269
1270%SetCode($Function, NewFunction);
1271
1272// ----------------------------------------------------------------------------
1273
1274function SetupFunction() {
1275  InstallFunctions($Function.prototype, DONT_ENUM, $Array(
1276    "bind", FunctionBind,
1277    "toString", FunctionToString
1278  ));
1279}
1280
1281SetupFunction();
1282