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