v8natives.js revision e0cee9b3ed82e2391fd85d118aeaa4ea361c687d
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  return %DefineAccessor(ToObject(this), ToString(name), GETTER, fun);
255}
256
257
258function ObjectLookupGetter(name) {
259  if (this == null && !IS_UNDETECTABLE(this)) {
260    throw new $TypeError('Object.prototype.__lookupGetter__: this is Null');
261  }
262  return %LookupAccessor(ToObject(this), ToString(name), GETTER);
263}
264
265
266function ObjectDefineSetter(name, fun) {
267  if (this == null && !IS_UNDETECTABLE(this)) {
268    throw new $TypeError('Object.prototype.__defineSetter__: this is Null');
269  }
270  if (!IS_FUNCTION(fun)) {
271    throw new $TypeError(
272        'Object.prototype.__defineSetter__: Expecting function');
273  }
274  return %DefineAccessor(ToObject(this), ToString(name), SETTER, fun);
275}
276
277
278function ObjectLookupSetter(name) {
279  if (this == null && !IS_UNDETECTABLE(this)) {
280    throw new $TypeError('Object.prototype.__lookupSetter__: this is Null');
281  }
282  return %LookupAccessor(ToObject(this), ToString(name), SETTER);
283}
284
285
286function ObjectKeys(obj) {
287  if (!IS_SPEC_OBJECT(obj))
288    throw MakeTypeError("obj_ctor_property_non_object", ["keys"]);
289  return %LocalKeys(obj);
290}
291
292
293// ES5 8.10.1.
294function IsAccessorDescriptor(desc) {
295  if (IS_UNDEFINED(desc)) return false;
296  return desc.hasGetter_ || desc.hasSetter_;
297}
298
299
300// ES5 8.10.2.
301function IsDataDescriptor(desc) {
302  if (IS_UNDEFINED(desc)) return false;
303  return desc.hasValue_ || desc.hasWritable_;
304}
305
306
307// ES5 8.10.3.
308function IsGenericDescriptor(desc) {
309  return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
310}
311
312
313function IsInconsistentDescriptor(desc) {
314  return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
315}
316
317// ES5 8.10.4
318function FromPropertyDescriptor(desc) {
319  if (IS_UNDEFINED(desc)) return desc;
320  var obj = new $Object();
321  if (IsDataDescriptor(desc)) {
322    obj.value = desc.getValue();
323    obj.writable = desc.isWritable();
324  }
325  if (IsAccessorDescriptor(desc)) {
326    obj.get = desc.getGet();
327    obj.set = desc.getSet();
328  }
329  obj.enumerable = desc.isEnumerable();
330  obj.configurable = desc.isConfigurable();
331  return obj;
332}
333
334// ES5 8.10.5.
335function ToPropertyDescriptor(obj) {
336  if (!IS_SPEC_OBJECT(obj)) {
337    throw MakeTypeError("property_desc_object", [obj]);
338  }
339  var desc = new PropertyDescriptor();
340
341  if ("enumerable" in obj) {
342    desc.setEnumerable(ToBoolean(obj.enumerable));
343  }
344
345  if ("configurable" in obj) {
346    desc.setConfigurable(ToBoolean(obj.configurable));
347  }
348
349  if ("value" in obj) {
350    desc.setValue(obj.value);
351  }
352
353  if ("writable" in obj) {
354    desc.setWritable(ToBoolean(obj.writable));
355  }
356
357  if ("get" in obj) {
358    var get = obj.get;
359    if (!IS_UNDEFINED(get) && !IS_FUNCTION(get)) {
360      throw MakeTypeError("getter_must_be_callable", [get]);
361    }
362    desc.setGet(get);
363  }
364
365  if ("set" in obj) {
366    var set = obj.set;
367    if (!IS_UNDEFINED(set) && !IS_FUNCTION(set)) {
368      throw MakeTypeError("setter_must_be_callable", [set]);
369    }
370    desc.setSet(set);
371  }
372
373  if (IsInconsistentDescriptor(desc)) {
374    throw MakeTypeError("value_and_accessor", [obj]);
375  }
376  return desc;
377}
378
379
380function PropertyDescriptor() {
381  // Initialize here so they are all in-object and have the same map.
382  // Default values from ES5 8.6.1.
383  this.value_ = void 0;
384  this.hasValue_ = false;
385  this.writable_ = false;
386  this.hasWritable_ = false;
387  this.enumerable_ = false;
388  this.hasEnumerable_ = false;
389  this.configurable_ = false;
390  this.hasConfigurable_ = false;
391  this.get_ = void 0;
392  this.hasGetter_ = false;
393  this.set_ = void 0;
394  this.hasSetter_ = false;
395}
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    throw MakeTypeError("define_disallowed", ["defineProperty"]);
566
567  if (!IS_UNDEFINED(current)) {
568    // Step 5 and 6
569    if ((IsGenericDescriptor(desc) ||
570         IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
571        (!desc.hasEnumerable() ||
572         SameValue(desc.isEnumerable(), current.isEnumerable())) &&
573        (!desc.hasConfigurable() ||
574         SameValue(desc.isConfigurable(), current.isConfigurable())) &&
575        (!desc.hasWritable() ||
576         SameValue(desc.isWritable(), current.isWritable())) &&
577        (!desc.hasValue() ||
578         SameValue(desc.getValue(), current.getValue())) &&
579        (!desc.hasGetter() ||
580         SameValue(desc.getGet(), current.getGet())) &&
581        (!desc.hasSetter() ||
582         SameValue(desc.getSet(), current.getSet()))) {
583      return true;
584    }
585    if (!current.isConfigurable()) {
586      // Step 7
587      if (desc.isConfigurable() ||
588          (desc.hasEnumerable() &&
589           desc.isEnumerable() != current.isEnumerable())) {
590        throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
591      }
592      // Step 8
593      if (!IsGenericDescriptor(desc)) {
594        // Step 9a
595        if (IsDataDescriptor(current) != IsDataDescriptor(desc)) {
596          throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
597        }
598        // Step 10a
599        if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
600          if (!current.isWritable() && desc.isWritable()) {
601            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
602          }
603          if (!current.isWritable() && desc.hasValue() &&
604              !SameValue(desc.getValue(), current.getValue())) {
605            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
606          }
607        }
608        // Step 11
609        if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
610          if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) {
611            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
612          }
613          if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) {
614            throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
615          }
616        }
617      }
618    }
619  }
620
621  // Send flags - enumerable and configurable are common - writable is
622  // only send to the data descriptor.
623  // Take special care if enumerable and configurable is not defined on
624  // desc (we need to preserve the existing values from current).
625  var flag = NONE;
626  if (desc.hasEnumerable()) {
627    flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
628  } else if (!IS_UNDEFINED(current)) {
629    flag |= current.isEnumerable() ? 0 : DONT_ENUM;
630  } else {
631    flag |= DONT_ENUM;
632  }
633
634  if (desc.hasConfigurable()) {
635    flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
636  } else if (!IS_UNDEFINED(current)) {
637    flag |= current.isConfigurable() ? 0 : DONT_DELETE;
638  } else
639    flag |= DONT_DELETE;
640
641  if (IsDataDescriptor(desc) ||
642      (IsGenericDescriptor(desc) &&
643       (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
644    // There are 3 cases that lead here:
645    // Step 4a - defining a new data property.
646    // Steps 9b & 12 - replacing an existing accessor property with a data
647    //                 property.
648    // Step 12 - updating an existing data property with a data or generic
649    //           descriptor.
650
651    if (desc.hasWritable()) {
652      flag |= desc.isWritable() ? 0 : READ_ONLY;
653    } else if (!IS_UNDEFINED(current)) {
654      flag |= current.isWritable() ? 0 : READ_ONLY;
655    } else {
656      flag |= READ_ONLY;
657    }
658
659    var value = void 0;  // Default value is undefined.
660    if (desc.hasValue()) {
661      value = desc.getValue();
662    } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
663      value = current.getValue();
664    }
665
666    %DefineOrRedefineDataProperty(obj, p, value, flag);
667  } else if (IsGenericDescriptor(desc)) {
668    // Step 12 - updating an existing accessor property with generic
669    //           descriptor. Changing flags only.
670    %DefineOrRedefineAccessorProperty(obj, p, GETTER, current.getGet(), flag);
671  } else {
672    // There are 3 cases that lead here:
673    // Step 4b - defining a new accessor property.
674    // Steps 9c & 12 - replacing an existing data property with an accessor
675    //                 property.
676    // Step 12 - updating an existing accessor property with an accessor
677    //           descriptor.
678    if (desc.hasGetter()) {
679      %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag);
680    }
681    if (desc.hasSetter()) {
682      %DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag);
683    }
684  }
685  return true;
686}
687
688
689// ES5 section 15.2.3.2.
690function ObjectGetPrototypeOf(obj) {
691  if (!IS_SPEC_OBJECT(obj))
692    throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]);
693  return obj.__proto__;
694}
695
696
697// ES5 section 15.2.3.3
698function ObjectGetOwnPropertyDescriptor(obj, p) {
699  if (!IS_SPEC_OBJECT(obj))
700    throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]);
701  var desc = GetOwnProperty(obj, p);
702  return FromPropertyDescriptor(desc);
703}
704
705
706// ES5 section 15.2.3.4.
707function ObjectGetOwnPropertyNames(obj) {
708  if (!IS_SPEC_OBJECT(obj))
709    throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]);
710
711  // Find all the indexed properties.
712
713  // Get the local element names.
714  var propertyNames = %GetLocalElementNames(obj);
715
716  // Get names for indexed interceptor properties.
717  if (%GetInterceptorInfo(obj) & 1) {
718    var indexedInterceptorNames =
719        %GetIndexedInterceptorElementNames(obj);
720    if (indexedInterceptorNames)
721      propertyNames = propertyNames.concat(indexedInterceptorNames);
722  }
723
724  // Find all the named properties.
725
726  // Get the local property names.
727  propertyNames = propertyNames.concat(%GetLocalPropertyNames(obj));
728
729  // Get names for named interceptor properties if any.
730
731  if (%GetInterceptorInfo(obj) & 2) {
732    var namedInterceptorNames =
733        %GetNamedInterceptorPropertyNames(obj);
734    if (namedInterceptorNames) {
735      propertyNames = propertyNames.concat(namedInterceptorNames);
736    }
737  }
738
739  // Property names are expected to be unique strings.
740  var propertySet = {};
741  var j = 0;
742  for (var i = 0; i < propertyNames.length; ++i) {
743    var name = ToString(propertyNames[i]);
744    // We need to check for the exact property value since for intrinsic
745    // properties like toString if(propertySet["toString"]) will always
746    // succeed.
747    if (propertySet[name] === true)
748      continue;
749    propertySet[name] = true;
750    propertyNames[j++] = name;
751  }
752  propertyNames.length = j;
753
754  return propertyNames;
755}
756
757
758// ES5 section 15.2.3.5.
759function ObjectCreate(proto, properties) {
760  if (!IS_SPEC_OBJECT(proto) && proto !== null) {
761    throw MakeTypeError("proto_object_or_null", [proto]);
762  }
763  var obj = new $Object();
764  obj.__proto__ = proto;
765  if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
766  return obj;
767}
768
769
770// ES5 section 15.2.3.6.
771function ObjectDefineProperty(obj, p, attributes) {
772  if (!IS_SPEC_OBJECT(obj)) {
773    throw MakeTypeError("obj_ctor_property_non_object", ["defineProperty"]);
774  }
775  var name = ToString(p);
776  var desc = ToPropertyDescriptor(attributes);
777  DefineOwnProperty(obj, name, desc, true);
778  return obj;
779}
780
781
782// ES5 section 15.2.3.7.
783function ObjectDefineProperties(obj, properties) {
784  if (!IS_SPEC_OBJECT(obj))
785    throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]);
786  var props = ToObject(properties);
787  var key_values = [];
788  for (var key in props) {
789    if (%HasLocalProperty(props, key)) {
790      key_values.push(key);
791      var value = props[key];
792      var desc = ToPropertyDescriptor(value);
793      key_values.push(desc);
794    }
795  }
796  for (var i = 0; i < key_values.length; i += 2) {
797    var key = key_values[i];
798    var desc = key_values[i + 1];
799    DefineOwnProperty(obj, key, desc, true);
800  }
801  return obj;
802}
803
804
805// ES5 section 15.2.3.8.
806function ObjectSeal(obj) {
807  if (!IS_SPEC_OBJECT(obj)) {
808    throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
809  }
810  var names = ObjectGetOwnPropertyNames(obj);
811  for (var i = 0; i < names.length; i++) {
812    var name = names[i];
813    var desc = GetOwnProperty(obj, name);
814    if (desc.isConfigurable()) desc.setConfigurable(false);
815    DefineOwnProperty(obj, name, desc, true);
816  }
817  return ObjectPreventExtension(obj);
818}
819
820
821// ES5 section 15.2.3.9.
822function ObjectFreeze(obj) {
823  if (!IS_SPEC_OBJECT(obj)) {
824    throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
825  }
826  var names = ObjectGetOwnPropertyNames(obj);
827  for (var i = 0; i < names.length; i++) {
828    var name = names[i];
829    var desc = GetOwnProperty(obj, name);
830    if (IsDataDescriptor(desc)) desc.setWritable(false);
831    if (desc.isConfigurable()) desc.setConfigurable(false);
832    DefineOwnProperty(obj, name, desc, true);
833  }
834  return ObjectPreventExtension(obj);
835}
836
837
838// ES5 section 15.2.3.10
839function ObjectPreventExtension(obj) {
840  if (!IS_SPEC_OBJECT(obj)) {
841    throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
842  }
843  %PreventExtensions(obj);
844  return obj;
845}
846
847
848// ES5 section 15.2.3.11
849function ObjectIsSealed(obj) {
850  if (!IS_SPEC_OBJECT(obj)) {
851    throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
852  }
853  var names = ObjectGetOwnPropertyNames(obj);
854  for (var i = 0; i < names.length; i++) {
855    var name = names[i];
856    var desc = GetOwnProperty(obj, name);
857    if (desc.isConfigurable()) return false;
858  }
859  if (!ObjectIsExtensible(obj)) {
860    return true;
861  }
862  return false;
863}
864
865
866// ES5 section 15.2.3.12
867function ObjectIsFrozen(obj) {
868  if (!IS_SPEC_OBJECT(obj)) {
869    throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
870  }
871  var names = ObjectGetOwnPropertyNames(obj);
872  for (var i = 0; i < names.length; i++) {
873    var name = names[i];
874    var desc = GetOwnProperty(obj, name);
875    if (IsDataDescriptor(desc) && desc.isWritable()) return false;
876    if (desc.isConfigurable()) return false;
877  }
878  if (!ObjectIsExtensible(obj)) {
879    return true;
880  }
881  return false;
882}
883
884
885// ES5 section 15.2.3.13
886function ObjectIsExtensible(obj) {
887  if (!IS_SPEC_OBJECT(obj)) {
888    throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
889  }
890  return %IsExtensible(obj);
891}
892
893
894%SetCode($Object, function(x) {
895  if (%_IsConstructCall()) {
896    if (x == null) return this;
897    return ToObject(x);
898  } else {
899    if (x == null) return { };
900    return ToObject(x);
901  }
902});
903
904%SetExpectedNumberOfProperties($Object, 4);
905
906// ----------------------------------------------------------------------------
907
908
909function SetupObject() {
910  // Setup non-enumerable functions on the Object.prototype object.
911  InstallFunctions($Object.prototype, DONT_ENUM, $Array(
912    "toString", ObjectToString,
913    "toLocaleString", ObjectToLocaleString,
914    "valueOf", ObjectValueOf,
915    "hasOwnProperty", ObjectHasOwnProperty,
916    "isPrototypeOf", ObjectIsPrototypeOf,
917    "propertyIsEnumerable", ObjectPropertyIsEnumerable,
918    "__defineGetter__", ObjectDefineGetter,
919    "__lookupGetter__", ObjectLookupGetter,
920    "__defineSetter__", ObjectDefineSetter,
921    "__lookupSetter__", ObjectLookupSetter
922  ));
923  InstallFunctions($Object, DONT_ENUM, $Array(
924    "keys", ObjectKeys,
925    "create", ObjectCreate,
926    "defineProperty", ObjectDefineProperty,
927    "defineProperties", ObjectDefineProperties,
928    "freeze", ObjectFreeze,
929    "getPrototypeOf", ObjectGetPrototypeOf,
930    "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
931    "getOwnPropertyNames", ObjectGetOwnPropertyNames,
932    "isExtensible", ObjectIsExtensible,
933    "isFrozen", ObjectIsFrozen,
934    "isSealed", ObjectIsSealed,
935    "preventExtensions", ObjectPreventExtension,
936    "seal", ObjectSeal
937  ));
938}
939
940SetupObject();
941
942
943// ----------------------------------------------------------------------------
944// Boolean
945
946function BooleanToString() {
947  // NOTE: Both Boolean objects and values can enter here as
948  // 'this'. This is not as dictated by ECMA-262.
949  var b = this;
950  if (!IS_BOOLEAN(b)) {
951    if (!IS_BOOLEAN_WRAPPER(b)) {
952      throw new $TypeError('Boolean.prototype.toString is not generic');
953    }
954    b = %_ValueOf(b);
955  }
956  return b ? 'true' : 'false';
957}
958
959
960function BooleanValueOf() {
961  // NOTE: Both Boolean objects and values can enter here as
962  // 'this'. This is not as dictated by ECMA-262.
963  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this))
964    throw new $TypeError('Boolean.prototype.valueOf is not generic');
965  return %_ValueOf(this);
966}
967
968
969// ----------------------------------------------------------------------------
970
971
972function SetupBoolean() {
973  InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
974    "toString", BooleanToString,
975    "valueOf", BooleanValueOf
976  ));
977}
978
979SetupBoolean();
980
981// ----------------------------------------------------------------------------
982// Number
983
984// Set the Number function and constructor.
985%SetCode($Number, function(x) {
986  var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
987  if (%_IsConstructCall()) {
988    %_SetValueOf(this, value);
989  } else {
990    return value;
991  }
992});
993
994%FunctionSetPrototype($Number, new $Number(0));
995
996// ECMA-262 section 15.7.4.2.
997function NumberToString(radix) {
998  // NOTE: Both Number objects and values can enter here as
999  // 'this'. This is not as dictated by ECMA-262.
1000  var number = this;
1001  if (!IS_NUMBER(this)) {
1002    if (!IS_NUMBER_WRAPPER(this))
1003      throw new $TypeError('Number.prototype.toString is not generic');
1004    // Get the value of this number in case it's an object.
1005    number = %_ValueOf(this);
1006  }
1007  // Fast case: Convert number in radix 10.
1008  if (IS_UNDEFINED(radix) || radix === 10) {
1009    return %_NumberToString(number);
1010  }
1011
1012  // Convert the radix to an integer and check the range.
1013  radix = TO_INTEGER(radix);
1014  if (radix < 2 || radix > 36) {
1015    throw new $RangeError('toString() radix argument must be between 2 and 36');
1016  }
1017  // Convert the number to a string in the given radix.
1018  return %NumberToRadixString(number, radix);
1019}
1020
1021
1022// ECMA-262 section 15.7.4.3
1023function NumberToLocaleString() {
1024  return this.toString();
1025}
1026
1027
1028// ECMA-262 section 15.7.4.4
1029function NumberValueOf() {
1030  // NOTE: Both Number objects and values can enter here as
1031  // 'this'. This is not as dictated by ECMA-262.
1032  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this))
1033    throw new $TypeError('Number.prototype.valueOf is not generic');
1034  return %_ValueOf(this);
1035}
1036
1037
1038// ECMA-262 section 15.7.4.5
1039function NumberToFixed(fractionDigits) {
1040  var f = TO_INTEGER(fractionDigits);
1041  if (f < 0 || f > 20) {
1042    throw new $RangeError("toFixed() digits argument must be between 0 and 20");
1043  }
1044  var x = ToNumber(this);
1045  return %NumberToFixed(x, f);
1046}
1047
1048
1049// ECMA-262 section 15.7.4.6
1050function NumberToExponential(fractionDigits) {
1051  var f = -1;
1052  if (!IS_UNDEFINED(fractionDigits)) {
1053    f = TO_INTEGER(fractionDigits);
1054    if (f < 0 || f > 20) {
1055      throw new $RangeError("toExponential() argument must be between 0 and 20");
1056    }
1057  }
1058  var x = ToNumber(this);
1059  return %NumberToExponential(x, f);
1060}
1061
1062
1063// ECMA-262 section 15.7.4.7
1064function NumberToPrecision(precision) {
1065  if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
1066  var p = TO_INTEGER(precision);
1067  if (p < 1 || p > 21) {
1068    throw new $RangeError("toPrecision() argument must be between 1 and 21");
1069  }
1070  var x = ToNumber(this);
1071  return %NumberToPrecision(x, p);
1072}
1073
1074
1075// ----------------------------------------------------------------------------
1076
1077function SetupNumber() {
1078  %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
1079  // Setup the constructor property on the Number prototype object.
1080  %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);
1081
1082  %OptimizeObjectForAddingMultipleProperties($Number, 5);
1083  // ECMA-262 section 15.7.3.1.
1084  %SetProperty($Number,
1085               "MAX_VALUE",
1086               1.7976931348623157e+308,
1087               DONT_ENUM | DONT_DELETE | READ_ONLY);
1088
1089  // ECMA-262 section 15.7.3.2.
1090  %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY);
1091
1092  // ECMA-262 section 15.7.3.3.
1093  %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
1094
1095  // ECMA-262 section 15.7.3.4.
1096  %SetProperty($Number,
1097               "NEGATIVE_INFINITY",
1098               -1/0,
1099               DONT_ENUM | DONT_DELETE | READ_ONLY);
1100
1101  // ECMA-262 section 15.7.3.5.
1102  %SetProperty($Number,
1103               "POSITIVE_INFINITY",
1104               1/0,
1105               DONT_ENUM | DONT_DELETE | READ_ONLY);
1106  %ToFastProperties($Number);
1107
1108  // Setup non-enumerable functions on the Number prototype object.
1109  InstallFunctions($Number.prototype, DONT_ENUM, $Array(
1110    "toString", NumberToString,
1111    "toLocaleString", NumberToLocaleString,
1112    "valueOf", NumberValueOf,
1113    "toFixed", NumberToFixed,
1114    "toExponential", NumberToExponential,
1115    "toPrecision", NumberToPrecision
1116  ));
1117}
1118
1119SetupNumber();
1120
1121
1122// ----------------------------------------------------------------------------
1123// Function
1124
1125$Function.prototype.constructor = $Function;
1126
1127function FunctionSourceString(func) {
1128  if (!IS_FUNCTION(func)) {
1129    throw new $TypeError('Function.prototype.toString is not generic');
1130  }
1131
1132  var source = %FunctionGetSourceCode(func);
1133  if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
1134    var name = %FunctionGetName(func);
1135    if (name) {
1136      // Mimic what KJS does.
1137      return 'function ' + name + '() { [native code] }';
1138    } else {
1139      return 'function () { [native code] }';
1140    }
1141  }
1142
1143  var name = %FunctionGetName(func);
1144  return 'function ' + name + source;
1145}
1146
1147
1148function FunctionToString() {
1149  return FunctionSourceString(this);
1150}
1151
1152
1153// ES5 15.3.4.5
1154function FunctionBind(this_arg) { // Length is 1.
1155  if (!IS_FUNCTION(this)) {
1156      throw new $TypeError('Bind must be called on a function');
1157  }
1158  // this_arg is not an argument that should be bound.
1159  var argc_bound = (%_ArgumentsLength() || 1) - 1;
1160  var fn = this;
1161  if (argc_bound == 0) {
1162    var result = function() {
1163      if (%_IsConstructCall()) {
1164        // %NewObjectFromBound implicitly uses arguments passed to this
1165        // function. We do not pass the arguments object explicitly to avoid
1166        // materializing it and guarantee that this function will be optimized.
1167        return %NewObjectFromBound(fn, null);
1168      }
1169
1170      return fn.apply(this_arg, arguments);
1171    };
1172  } else {
1173    var bound_args = new InternalArray(argc_bound);
1174    for(var i = 0; i < argc_bound; i++) {
1175      bound_args[i] = %_Arguments(i+1);
1176    }
1177
1178    var result = function() {
1179      // If this is a construct call we use a special runtime method
1180      // to generate the actual object using the bound function.
1181      if (%_IsConstructCall()) {
1182        // %NewObjectFromBound implicitly uses arguments passed to this
1183        // function. We do not pass the arguments object explicitly to avoid
1184        // materializing it and guarantee that this function will be optimized.
1185        return %NewObjectFromBound(fn, bound_args);
1186      }
1187
1188      // Combine the args we got from the bind call with the args
1189      // given as argument to the invocation.
1190      var argc = %_ArgumentsLength();
1191      var args = new InternalArray(argc + argc_bound);
1192      // Add bound arguments.
1193      for (var i = 0; i < argc_bound; i++) {
1194        args[i] = bound_args[i];
1195      }
1196      // Add arguments from call.
1197      for (var i = 0; i < argc; i++) {
1198        args[argc_bound + i] = %_Arguments(i);
1199      }
1200      return fn.apply(this_arg, args);
1201    };
1202  }
1203
1204  // We already have caller and arguments properties on functions,
1205  // which are non-configurable. It therefore makes no sence to
1206  // try to redefine these as defined by the spec. The spec says
1207  // that bind should make these throw a TypeError if get or set
1208  // is called and make them non-enumerable and non-configurable.
1209  // To be consistent with our normal functions we leave this as it is.
1210
1211  // Set the correct length.
1212  var length = (this.length - argc_bound) > 0 ? this.length - argc_bound : 0;
1213  %FunctionSetLength(result, length);
1214
1215  return result;
1216}
1217
1218
1219function NewFunction(arg1) {  // length == 1
1220  var n = %_ArgumentsLength();
1221  var p = '';
1222  if (n > 1) {
1223    p = new InternalArray(n - 1);
1224    for (var i = 0; i < n - 1; i++) p[i] = %_Arguments(i);
1225    p = Join(p, n - 1, ',', NonStringToString);
1226    // If the formal parameters string include ) - an illegal
1227    // character - it may make the combined function expression
1228    // compile. We avoid this problem by checking for this early on.
1229    if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]);
1230  }
1231  var body = (n > 0) ? ToString(%_Arguments(n - 1)) : '';
1232  var source = '(function(' + p + ') {\n' + body + '\n})';
1233
1234  // The call to SetNewFunctionAttributes will ensure the prototype
1235  // property of the resulting function is enumerable (ECMA262, 15.3.5.2).
1236  var f = %CompileString(source)();
1237  %FunctionSetName(f, "anonymous");
1238  return %SetNewFunctionAttributes(f);
1239}
1240
1241%SetCode($Function, NewFunction);
1242
1243// ----------------------------------------------------------------------------
1244
1245function SetupFunction() {
1246  InstallFunctions($Function.prototype, DONT_ENUM, $Array(
1247    "bind", FunctionBind,
1248    "toString", FunctionToString
1249  ));
1250}
1251
1252SetupFunction();
1253