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