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