v8natives.js revision 086aeeaae12517475c22695a200be45495516549
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)) {
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    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 f.call(this);
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    f.call(%GlobalReceiver(global));
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
495// ES5 section 8.12.1.
496function GetOwnProperty(obj, p) {
497  var desc = new PropertyDescriptor();
498
499  // GetOwnProperty returns an array indexed by the constants
500  // defined in macros.py.
501  // If p is not a property on obj undefined is returned.
502  var props = %GetOwnProperty(ToObject(obj), ToString(p));
503
504  if (IS_UNDEFINED(props)) return void 0;
505
506  // This is an accessor
507  if (props[IS_ACCESSOR_INDEX]) {
508    desc.setGet(props[GETTER_INDEX]);
509    desc.setSet(props[SETTER_INDEX]);
510  } else {
511    desc.setValue(props[VALUE_INDEX]);
512    desc.setWritable(props[WRITABLE_INDEX]);
513  }
514  desc.setEnumerable(props[ENUMERABLE_INDEX]);
515  desc.setConfigurable(props[CONFIGURABLE_INDEX]);
516
517  return desc;
518}
519
520
521// ES5 section 8.12.2.
522function GetProperty(obj, p) {
523  var prop = GetOwnProperty(obj);
524  if (!IS_UNDEFINED(prop)) return prop;
525  var proto = obj.__proto__;
526  if (IS_NULL(proto)) return void 0;
527  return GetProperty(proto, p);
528}
529
530
531// ES5 section 8.12.6
532function HasProperty(obj, p) {
533  var desc = GetProperty(obj, p);
534  return IS_UNDEFINED(desc) ? false : true;
535}
536
537
538// ES5 8.12.9.
539function DefineOwnProperty(obj, p, desc, should_throw) {
540  var current = GetOwnProperty(obj, p);
541  var extensible = %IsExtensible(ToObject(obj));
542
543  // Error handling according to spec.
544  // Step 3
545  if (IS_UNDEFINED(current) && !extensible)
546    throw MakeTypeError("define_disallowed", ["defineProperty"]);
547
548  if (!IS_UNDEFINED(current) && !current.isConfigurable()) {
549    // Step 5 and 6
550    if ((!desc.hasEnumerable() ||
551         SameValue(desc.isEnumerable() && current.isEnumerable())) &&
552        (!desc.hasConfigurable() ||
553         SameValue(desc.isConfigurable(), current.isConfigurable())) &&
554        (!desc.hasWritable() ||
555         SameValue(desc.isWritable(), current.isWritable())) &&
556        (!desc.hasValue() ||
557         SameValue(desc.getValue(), current.getValue())) &&
558        (!desc.hasGetter() ||
559         SameValue(desc.getGet(), current.getGet())) &&
560        (!desc.hasSetter() ||
561         SameValue(desc.getSet(), current.getSet()))) {
562      return true;
563    }
564
565    // Step 7
566    if (desc.isConfigurable() || desc.isEnumerable() != current.isEnumerable())
567      throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
568    // Step 9
569    if (IsDataDescriptor(current) != IsDataDescriptor(desc))
570      throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
571    // Step 10
572    if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
573      if (!current.isWritable() && desc.isWritable())
574        throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
575      if (!current.isWritable() && desc.hasValue() &&
576          !SameValue(desc.getValue(), current.getValue())) {
577        throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
578      }
579    }
580    // Step 11
581    if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
582      if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())){
583        throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
584      }
585      if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet()))
586        throw MakeTypeError("redefine_disallowed", ["defineProperty"]);
587    }
588  }
589
590  // Send flags - enumerable and configurable are common - writable is
591  // only send to the data descriptor.
592  // Take special care if enumerable and configurable is not defined on
593  // desc (we need to preserve the existing values from current).
594  var flag = NONE;
595  if (desc.hasEnumerable()) {
596    flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
597  } else if (!IS_UNDEFINED(current)) {
598    flag |= current.isEnumerable() ? 0 : DONT_ENUM;
599  } else {
600    flag |= DONT_ENUM;
601  }
602
603  if (desc.hasConfigurable()) {
604    flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
605  } else if (!IS_UNDEFINED(current)) {
606    flag |= current.isConfigurable() ? 0 : DONT_DELETE;
607  } else
608    flag |= DONT_DELETE;
609
610  if (IsDataDescriptor(desc) || IsGenericDescriptor(desc)) {
611    if (desc.hasWritable()) {
612      flag |= desc.isWritable() ? 0 : READ_ONLY;
613    } else if (!IS_UNDEFINED(current)) {
614      flag |= current.isWritable() ? 0 : READ_ONLY;
615    } else {
616      flag |= READ_ONLY;
617    }
618    var value = void 0;  // Default value is undefined.
619    if (desc.hasValue()) {
620      value = desc.getValue();
621    } else if (!IS_UNDEFINED(current)) {
622      value = current.getValue();
623    }
624    %DefineOrRedefineDataProperty(obj, p, value, flag);
625  } else {
626    if (desc.hasGetter() &&
627        (IS_FUNCTION(desc.getGet()) || IS_UNDEFINED(desc.getGet()))) {
628       %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag);
629    }
630    if (desc.hasSetter() &&
631        (IS_FUNCTION(desc.getSet()) || IS_UNDEFINED(desc.getSet()))) {
632      %DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag);
633    }
634  }
635  return true;
636}
637
638
639// ES5 section 15.2.3.2.
640function ObjectGetPrototypeOf(obj) {
641  if (!IS_SPEC_OBJECT(obj))
642    throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]);
643  return obj.__proto__;
644}
645
646
647// ES5 section 15.2.3.3
648function ObjectGetOwnPropertyDescriptor(obj, p) {
649  if (!IS_SPEC_OBJECT(obj))
650    throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]);
651  var desc = GetOwnProperty(obj, p);
652  return FromPropertyDescriptor(desc);
653}
654
655
656// ES5 section 15.2.3.4.
657function ObjectGetOwnPropertyNames(obj) {
658  if (!IS_SPEC_OBJECT(obj))
659    throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]);
660
661  // Find all the indexed properties.
662
663  // Get the local element names.
664  var propertyNames = %GetLocalElementNames(obj);
665
666  // Get names for indexed interceptor properties.
667  if (%GetInterceptorInfo(obj) & 1) {
668    var indexedInterceptorNames =
669        %GetIndexedInterceptorElementNames(obj);
670    if (indexedInterceptorNames)
671      propertyNames = propertyNames.concat(indexedInterceptorNames);
672  }
673
674  // Find all the named properties.
675
676  // Get the local property names.
677  propertyNames = propertyNames.concat(%GetLocalPropertyNames(obj));
678
679  // Get names for named interceptor properties if any.
680
681  if (%GetInterceptorInfo(obj) & 2) {
682    var namedInterceptorNames =
683        %GetNamedInterceptorPropertyNames(obj);
684    if (namedInterceptorNames) {
685      propertyNames = propertyNames.concat(namedInterceptorNames);
686    }
687  }
688
689  // Property names are expected to be unique strings.
690  var propertySet = {};
691  var j = 0;
692  for (var i = 0; i < propertyNames.length; ++i) {
693    var name = ToString(propertyNames[i]);
694    // We need to check for the exact property value since for intrinsic
695    // properties like toString if(propertySet["toString"]) will always
696    // succeed.
697    if (propertySet[name] === true)
698      continue;
699    propertySet[name] = true;
700    propertyNames[j++] = name;
701  }
702  propertyNames.length = j;
703
704  return propertyNames;
705}
706
707
708// ES5 section 15.2.3.5.
709function ObjectCreate(proto, properties) {
710  if (!IS_SPEC_OBJECT(proto) && proto !== null) {
711    throw MakeTypeError("proto_object_or_null", [proto]);
712  }
713  var obj = new $Object();
714  obj.__proto__ = proto;
715  if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
716  return obj;
717}
718
719
720// ES5 section 15.2.3.6.
721function ObjectDefineProperty(obj, p, attributes) {
722  if (!IS_SPEC_OBJECT(obj)) {
723    throw MakeTypeError("obj_ctor_property_non_object", ["defineProperty"]);
724  }
725  var name = ToString(p);
726  var desc = ToPropertyDescriptor(attributes);
727  DefineOwnProperty(obj, name, desc, true);
728  return obj;
729}
730
731
732// ES5 section 15.2.3.7.
733function ObjectDefineProperties(obj, properties) {
734  if (!IS_SPEC_OBJECT(obj))
735    throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]);
736  var props = ToObject(properties);
737  var key_values = [];
738  for (var key in props) {
739    if (%HasLocalProperty(props, key)) {
740      key_values.push(key);
741      var value = props[key];
742      var desc = ToPropertyDescriptor(value);
743      key_values.push(desc);
744    }
745  }
746  for (var i = 0; i < key_values.length; i += 2) {
747    var key = key_values[i];
748    var desc = key_values[i + 1];
749    DefineOwnProperty(obj, key, desc, true);
750  }
751  return obj;
752}
753
754
755// ES5 section 15.2.3.8.
756function ObjectSeal(obj) {
757  if (!IS_SPEC_OBJECT(obj)) {
758    throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
759  }
760  var names = ObjectGetOwnPropertyNames(obj);
761  for (var i = 0; i < names.length; i++) {
762    var name = names[i];
763    var desc = GetOwnProperty(obj, name);
764    if (desc.isConfigurable()) desc.setConfigurable(false);
765    DefineOwnProperty(obj, name, desc, true);
766  }
767  return ObjectPreventExtension(obj);
768}
769
770
771// ES5 section 15.2.3.9.
772function ObjectFreeze(obj) {
773  if (!IS_SPEC_OBJECT(obj)) {
774    throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
775  }
776  var names = ObjectGetOwnPropertyNames(obj);
777  for (var i = 0; i < names.length; i++) {
778    var name = names[i];
779    var desc = GetOwnProperty(obj, name);
780    if (IsDataDescriptor(desc)) desc.setWritable(false);
781    if (desc.isConfigurable()) desc.setConfigurable(false);
782    DefineOwnProperty(obj, name, desc, true);
783  }
784  return ObjectPreventExtension(obj);
785}
786
787
788// ES5 section 15.2.3.10
789function ObjectPreventExtension(obj) {
790  if (!IS_SPEC_OBJECT(obj)) {
791    throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
792  }
793  %PreventExtensions(obj);
794  return obj;
795}
796
797
798// ES5 section 15.2.3.11
799function ObjectIsSealed(obj) {
800  if (!IS_SPEC_OBJECT(obj)) {
801    throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
802  }
803  var names = ObjectGetOwnPropertyNames(obj);
804  for (var i = 0; i < names.length; i++) {
805    var name = names[i];
806    var desc = GetOwnProperty(obj, name);
807    if (desc.isConfigurable()) return false;
808  }
809  if (!ObjectIsExtensible(obj)) {
810    return true;
811  }
812  return false;
813}
814
815
816// ES5 section 15.2.3.12
817function ObjectIsFrozen(obj) {
818  if (!IS_SPEC_OBJECT(obj)) {
819    throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
820  }
821  var names = ObjectGetOwnPropertyNames(obj);
822  for (var i = 0; i < names.length; i++) {
823    var name = names[i];
824    var desc = GetOwnProperty(obj, name);
825    if (IsDataDescriptor(desc) && desc.isWritable()) return false;
826    if (desc.isConfigurable()) return false;
827  }
828  if (!ObjectIsExtensible(obj)) {
829    return true;
830  }
831  return false;
832}
833
834
835// ES5 section 15.2.3.13
836function ObjectIsExtensible(obj) {
837  if (!IS_SPEC_OBJECT(obj)) {
838    throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
839  }
840  return %IsExtensible(obj);
841}
842
843
844%SetCode($Object, function(x) {
845  if (%_IsConstructCall()) {
846    if (x == null) return this;
847    return ToObject(x);
848  } else {
849    if (x == null) return { };
850    return ToObject(x);
851  }
852});
853
854%SetExpectedNumberOfProperties($Object, 4);
855
856// ----------------------------------------------------------------------------
857
858
859function SetupObject() {
860  // Setup non-enumerable functions on the Object.prototype object.
861  InstallFunctions($Object.prototype, DONT_ENUM, $Array(
862    "toString", ObjectToString,
863    "toLocaleString", ObjectToLocaleString,
864    "valueOf", ObjectValueOf,
865    "hasOwnProperty", ObjectHasOwnProperty,
866    "isPrototypeOf", ObjectIsPrototypeOf,
867    "propertyIsEnumerable", ObjectPropertyIsEnumerable,
868    "__defineGetter__", ObjectDefineGetter,
869    "__lookupGetter__", ObjectLookupGetter,
870    "__defineSetter__", ObjectDefineSetter,
871    "__lookupSetter__", ObjectLookupSetter
872  ));
873  InstallFunctions($Object, DONT_ENUM, $Array(
874    "keys", ObjectKeys,
875    "create", ObjectCreate,
876    "defineProperty", ObjectDefineProperty,
877    "defineProperties", ObjectDefineProperties,
878    "freeze", ObjectFreeze,
879    "getPrototypeOf", ObjectGetPrototypeOf,
880    "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
881    "getOwnPropertyNames", ObjectGetOwnPropertyNames,
882    "isExtensible", ObjectIsExtensible,
883    "isFrozen", ObjectIsFrozen,
884    "isSealed", ObjectIsSealed,
885    "preventExtensions", ObjectPreventExtension,
886    "seal", ObjectSeal
887  ));
888}
889
890SetupObject();
891
892
893// ----------------------------------------------------------------------------
894// Boolean
895
896function BooleanToString() {
897  // NOTE: Both Boolean objects and values can enter here as
898  // 'this'. This is not as dictated by ECMA-262.
899  var b = this;
900  if (!IS_BOOLEAN(b)) {
901    if (!IS_BOOLEAN_WRAPPER(b)) {
902      throw new $TypeError('Boolean.prototype.toString is not generic');
903    }
904    b = %_ValueOf(b);
905  }
906  return b ? 'true' : 'false';
907}
908
909
910function BooleanValueOf() {
911  // NOTE: Both Boolean objects and values can enter here as
912  // 'this'. This is not as dictated by ECMA-262.
913  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this))
914    throw new $TypeError('Boolean.prototype.valueOf is not generic');
915  return %_ValueOf(this);
916}
917
918
919// ----------------------------------------------------------------------------
920
921
922function SetupBoolean() {
923  InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
924    "toString", BooleanToString,
925    "valueOf", BooleanValueOf
926  ));
927}
928
929SetupBoolean();
930
931// ----------------------------------------------------------------------------
932// Number
933
934// Set the Number function and constructor.
935%SetCode($Number, function(x) {
936  var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
937  if (%_IsConstructCall()) {
938    %_SetValueOf(this, value);
939  } else {
940    return value;
941  }
942});
943
944%FunctionSetPrototype($Number, new $Number(0));
945
946// ECMA-262 section 15.7.4.2.
947function NumberToString(radix) {
948  // NOTE: Both Number objects and values can enter here as
949  // 'this'. This is not as dictated by ECMA-262.
950  var number = this;
951  if (!IS_NUMBER(this)) {
952    if (!IS_NUMBER_WRAPPER(this))
953      throw new $TypeError('Number.prototype.toString is not generic');
954    // Get the value of this number in case it's an object.
955    number = %_ValueOf(this);
956  }
957  // Fast case: Convert number in radix 10.
958  if (IS_UNDEFINED(radix) || radix === 10) {
959    return %_NumberToString(number);
960  }
961
962  // Convert the radix to an integer and check the range.
963  radix = TO_INTEGER(radix);
964  if (radix < 2 || radix > 36) {
965    throw new $RangeError('toString() radix argument must be between 2 and 36');
966  }
967  // Convert the number to a string in the given radix.
968  return %NumberToRadixString(number, radix);
969}
970
971
972// ECMA-262 section 15.7.4.3
973function NumberToLocaleString() {
974  return this.toString();
975}
976
977
978// ECMA-262 section 15.7.4.4
979function NumberValueOf() {
980  // NOTE: Both Number objects and values can enter here as
981  // 'this'. This is not as dictated by ECMA-262.
982  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this))
983    throw new $TypeError('Number.prototype.valueOf is not generic');
984  return %_ValueOf(this);
985}
986
987
988// ECMA-262 section 15.7.4.5
989function NumberToFixed(fractionDigits) {
990  var f = TO_INTEGER(fractionDigits);
991  if (f < 0 || f > 20) {
992    throw new $RangeError("toFixed() digits argument must be between 0 and 20");
993  }
994  var x = ToNumber(this);
995  return %NumberToFixed(x, f);
996}
997
998
999// ECMA-262 section 15.7.4.6
1000function NumberToExponential(fractionDigits) {
1001  var f = -1;
1002  if (!IS_UNDEFINED(fractionDigits)) {
1003    f = TO_INTEGER(fractionDigits);
1004    if (f < 0 || f > 20) {
1005      throw new $RangeError("toExponential() argument must be between 0 and 20");
1006    }
1007  }
1008  var x = ToNumber(this);
1009  return %NumberToExponential(x, f);
1010}
1011
1012
1013// ECMA-262 section 15.7.4.7
1014function NumberToPrecision(precision) {
1015  if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
1016  var p = TO_INTEGER(precision);
1017  if (p < 1 || p > 21) {
1018    throw new $RangeError("toPrecision() argument must be between 1 and 21");
1019  }
1020  var x = ToNumber(this);
1021  return %NumberToPrecision(x, p);
1022}
1023
1024
1025// ----------------------------------------------------------------------------
1026
1027function SetupNumber() {
1028  %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
1029  // Setup the constructor property on the Number prototype object.
1030  %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);
1031
1032  %OptimizeObjectForAddingMultipleProperties($Number, 5);
1033  // ECMA-262 section 15.7.3.1.
1034  %SetProperty($Number,
1035               "MAX_VALUE",
1036               1.7976931348623157e+308,
1037               DONT_ENUM | DONT_DELETE | READ_ONLY);
1038
1039  // ECMA-262 section 15.7.3.2.
1040  %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY);
1041
1042  // ECMA-262 section 15.7.3.3.
1043  %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
1044
1045  // ECMA-262 section 15.7.3.4.
1046  %SetProperty($Number,
1047               "NEGATIVE_INFINITY",
1048               -1/0,
1049               DONT_ENUM | DONT_DELETE | READ_ONLY);
1050
1051  // ECMA-262 section 15.7.3.5.
1052  %SetProperty($Number,
1053               "POSITIVE_INFINITY",
1054               1/0,
1055               DONT_ENUM | DONT_DELETE | READ_ONLY);
1056  %ToFastProperties($Number);
1057
1058  // Setup non-enumerable functions on the Number prototype object.
1059  InstallFunctions($Number.prototype, DONT_ENUM, $Array(
1060    "toString", NumberToString,
1061    "toLocaleString", NumberToLocaleString,
1062    "valueOf", NumberValueOf,
1063    "toFixed", NumberToFixed,
1064    "toExponential", NumberToExponential,
1065    "toPrecision", NumberToPrecision
1066  ));
1067}
1068
1069SetupNumber();
1070
1071
1072// ----------------------------------------------------------------------------
1073// Function
1074
1075$Function.prototype.constructor = $Function;
1076
1077function FunctionSourceString(func) {
1078  if (!IS_FUNCTION(func)) {
1079    throw new $TypeError('Function.prototype.toString is not generic');
1080  }
1081
1082  var source = %FunctionGetSourceCode(func);
1083  if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
1084    var name = %FunctionGetName(func);
1085    if (name) {
1086      // Mimic what KJS does.
1087      return 'function ' + name + '() { [native code] }';
1088    } else {
1089      return 'function () { [native code] }';
1090    }
1091  }
1092
1093  var name = %FunctionGetName(func);
1094  return 'function ' + name + source;
1095}
1096
1097
1098function FunctionToString() {
1099  return FunctionSourceString(this);
1100}
1101
1102
1103// ES5 15.3.4.5
1104function FunctionBind(this_arg) { // Length is 1.
1105  if (!IS_FUNCTION(this)) {
1106      throw new $TypeError('Bind must be called on a function');
1107  }
1108  // this_arg is not an argument that should be bound.
1109  var argc_bound = (%_ArgumentsLength() || 1) - 1;
1110  if (argc_bound > 0) {
1111    var bound_args = new $Array(argc_bound);
1112    for(var i = 0; i < argc_bound; i++) {
1113      bound_args[i] = %_Arguments(i+1);
1114    }
1115  }
1116  var fn = this;
1117  var result = function() {
1118    // Combine the args we got from the bind call with the args
1119    // given as argument to the invocation.
1120    var argc = %_ArgumentsLength();
1121    var args = new $Array(argc + argc_bound);
1122    // Add bound arguments.
1123    for (var i = 0; i < argc_bound; i++) {
1124      args[i] = bound_args[i];
1125    }
1126    // Add arguments from call.
1127    for (var i = 0; i < argc; i++) {
1128      args[argc_bound + i] = %_Arguments(i);
1129    }
1130    // If this is a construct call we use a special runtime method
1131    // to generate the actual object using the bound function.
1132    if (%_IsConstructCall()) {
1133      return %NewObjectFromBound(fn, args);
1134    }
1135    return fn.apply(this_arg, args);
1136  };
1137
1138  // We already have caller and arguments properties on functions,
1139  // which are non-configurable. It therefore makes no sence to
1140  // try to redefine these as defined by the spec. The spec says
1141  // that bind should make these throw a TypeError if get or set
1142  // is called and make them non-enumerable and non-configurable.
1143  // To be consistent with our normal functions we leave this as it is.
1144
1145  // Set the correct length.
1146  var length = (this.length - argc_bound) > 0 ? this.length - argc_bound : 0;
1147  %FunctionSetLength(result, length);
1148
1149  return result;
1150}
1151
1152
1153function NewFunction(arg1) {  // length == 1
1154  var n = %_ArgumentsLength();
1155  var p = '';
1156  if (n > 1) {
1157    p = new $Array(n - 1);
1158    for (var i = 0; i < n - 1; i++) p[i] = %_Arguments(i);
1159    p = Join(p, n - 1, ',', NonStringToString);
1160    // If the formal parameters string include ) - an illegal
1161    // character - it may make the combined function expression
1162    // compile. We avoid this problem by checking for this early on.
1163    if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]);
1164  }
1165  var body = (n > 0) ? ToString(%_Arguments(n - 1)) : '';
1166  var source = '(function(' + p + ') {\n' + body + '\n})';
1167
1168  // The call to SetNewFunctionAttributes will ensure the prototype
1169  // property of the resulting function is enumerable (ECMA262, 15.3.5.2).
1170  var f = %CompileString(source)();
1171  %FunctionSetName(f, "anonymous");
1172  return %SetNewFunctionAttributes(f);
1173}
1174
1175%SetCode($Function, NewFunction);
1176
1177// ----------------------------------------------------------------------------
1178
1179function SetupFunction() {
1180  InstallFunctions($Function.prototype, DONT_ENUM, $Array(
1181    "bind", FunctionBind,
1182    "toString", FunctionToString
1183  ));
1184}
1185
1186SetupFunction();
1187