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