1// Copyright 2012 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// in runtime.js:
30// var $Object = global.Object;
31// var $Boolean = global.Boolean;
32// var $Number = global.Number;
33// var $Function = global.Function;
34// var $Array = global.Array;
35//
36// in math.js:
37// var $floor = MathFloor
38
39var $isNaN = GlobalIsNaN;
40var $isFinite = GlobalIsFinite;
41
42// ----------------------------------------------------------------------------
43
44// Helper function used to install functions on objects.
45function InstallFunctions(object, attributes, functions) {
46  if (functions.length >= 8) {
47    %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
48  }
49  for (var i = 0; i < functions.length; i += 2) {
50    var key = functions[i];
51    var f = functions[i + 1];
52    %FunctionSetName(f, key);
53    %FunctionRemovePrototype(f);
54    %SetProperty(object, key, f, attributes);
55    %SetNativeFlag(f);
56  }
57  %ToFastProperties(object);
58}
59
60
61// Helper function to install a getter-only accessor property.
62function InstallGetter(object, name, getter) {
63  %FunctionSetName(getter, name);
64  %FunctionRemovePrototype(getter);
65  %DefineOrRedefineAccessorProperty(object, name, getter, null, DONT_ENUM);
66  %SetNativeFlag(getter);
67}
68
69
70// Helper function to install a getter/setter accessor property.
71function InstallGetterSetter(object, name, getter, setter) {
72  %FunctionSetName(getter, name);
73  %FunctionSetName(setter, name);
74  %FunctionRemovePrototype(getter);
75  %FunctionRemovePrototype(setter);
76  %DefineOrRedefineAccessorProperty(object, name, getter, setter, DONT_ENUM);
77  %SetNativeFlag(getter);
78  %SetNativeFlag(setter);
79}
80
81
82// Prevents changes to the prototype of a built-in function.
83// The "prototype" property of the function object is made non-configurable,
84// and the prototype object is made non-extensible. The latter prevents
85// changing the __proto__ property.
86function SetUpLockedPrototype(constructor, fields, methods) {
87  %CheckIsBootstrapping();
88  var prototype = constructor.prototype;
89  // Install functions first, because this function is used to initialize
90  // PropertyDescriptor itself.
91  var property_count = (methods.length >> 1) + (fields ? fields.length : 0);
92  if (property_count >= 4) {
93    %OptimizeObjectForAddingMultipleProperties(prototype, property_count);
94  }
95  if (fields) {
96    for (var i = 0; i < fields.length; i++) {
97      %SetProperty(prototype, fields[i], UNDEFINED, DONT_ENUM | DONT_DELETE);
98    }
99  }
100  for (var i = 0; i < methods.length; i += 2) {
101    var key = methods[i];
102    var f = methods[i + 1];
103    %SetProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY);
104    %SetNativeFlag(f);
105  }
106  %SetPrototype(prototype, null);
107  %ToFastProperties(prototype);
108}
109
110
111// ----------------------------------------------------------------------------
112
113
114// ECMA 262 - 15.1.4
115function GlobalIsNaN(number) {
116  if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
117  return NUMBER_IS_NAN(number);
118}
119
120
121// ECMA 262 - 15.1.5
122function GlobalIsFinite(number) {
123  if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
124  return NUMBER_IS_FINITE(number);
125}
126
127
128// ECMA-262 - 15.1.2.2
129function GlobalParseInt(string, radix) {
130  if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
131    // Some people use parseInt instead of Math.floor.  This
132    // optimization makes parseInt on a Smi 12 times faster (60ns
133    // vs 800ns).  The following optimization makes parseInt on a
134    // non-Smi number 9 times faster (230ns vs 2070ns).  Together
135    // they make parseInt on a string 1.4% slower (274ns vs 270ns).
136    if (%_IsSmi(string)) return string;
137    if (IS_NUMBER(string) &&
138        ((0.01 < string && string < 1e9) ||
139            (-1e9 < string && string < -0.01))) {
140      // Truncate number.
141      return string | 0;
142    }
143    string = TO_STRING_INLINE(string);
144    radix = radix | 0;
145  } else {
146    // The spec says ToString should be evaluated before ToInt32.
147    string = TO_STRING_INLINE(string);
148    radix = TO_INT32(radix);
149    if (!(radix == 0 || (2 <= radix && radix <= 36))) {
150      return NAN;
151    }
152  }
153
154  if (%_HasCachedArrayIndex(string) &&
155      (radix == 0 || radix == 10)) {
156    return %_GetCachedArrayIndex(string);
157  }
158  return %StringParseInt(string, radix);
159}
160
161
162// ECMA-262 - 15.1.2.3
163function GlobalParseFloat(string) {
164  string = TO_STRING_INLINE(string);
165  if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
166  return %StringParseFloat(string);
167}
168
169
170function GlobalEval(x) {
171  if (!IS_STRING(x)) return x;
172
173  // For consistency with JSC we require the global object passed to
174  // eval to be the global object from which 'eval' originated. This
175  // is not mandated by the spec.
176  // We only throw if the global has been detached, since we need the
177  // receiver as this-value for the call.
178  if (!%IsAttachedGlobal(global)) {
179    throw new $EvalError('The "this" value passed to eval must ' +
180                         'be the global object from which eval originated');
181  }
182
183  var global_receiver = %GlobalReceiver(global);
184
185  var f = %CompileString(x, false);
186  if (!IS_FUNCTION(f)) return f;
187
188  return %_CallFunction(global_receiver, f);
189}
190
191
192// ----------------------------------------------------------------------------
193
194// Set up global object.
195function SetUpGlobal() {
196  %CheckIsBootstrapping();
197
198  var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
199
200  // ECMA 262 - 15.1.1.1.
201  %SetProperty(global, "NaN", NAN, attributes);
202
203  // ECMA-262 - 15.1.1.2.
204  %SetProperty(global, "Infinity", INFINITY, attributes);
205
206  // ECMA-262 - 15.1.1.3.
207  %SetProperty(global, "undefined", UNDEFINED, attributes);
208
209  // Set up non-enumerable function on the global object.
210  InstallFunctions(global, DONT_ENUM, $Array(
211    "isNaN", GlobalIsNaN,
212    "isFinite", GlobalIsFinite,
213    "parseInt", GlobalParseInt,
214    "parseFloat", GlobalParseFloat,
215    "eval", GlobalEval
216  ));
217}
218
219SetUpGlobal();
220
221
222// ----------------------------------------------------------------------------
223// Object
224
225// ECMA-262 - 15.2.4.2
226function ObjectToString() {
227  if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
228  if (IS_NULL(this)) return "[object Null]";
229  return "[object " + %_ClassOf(ToObject(this)) + "]";
230}
231
232
233// ECMA-262 - 15.2.4.3
234function ObjectToLocaleString() {
235  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
236    throw MakeTypeError("called_on_null_or_undefined",
237                        ["Object.prototype.toLocaleString"]);
238  }
239  return this.toString();
240}
241
242
243// ECMA-262 - 15.2.4.4
244function ObjectValueOf() {
245  return ToObject(this);
246}
247
248
249// ECMA-262 - 15.2.4.5
250function ObjectHasOwnProperty(V) {
251  if (%IsJSProxy(this)) {
252    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
253    if (IS_SYMBOL(V)) return false;
254
255    var handler = %GetHandler(this);
256    return CallTrap1(handler, "hasOwn", DerivedHasOwnTrap, ToName(V));
257  }
258  return %HasLocalProperty(TO_OBJECT_INLINE(this), ToName(V));
259}
260
261
262// ECMA-262 - 15.2.4.6
263function ObjectIsPrototypeOf(V) {
264  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
265    throw MakeTypeError("called_on_null_or_undefined",
266                        ["Object.prototype.isPrototypeOf"]);
267  }
268  if (!IS_SPEC_OBJECT(V)) return false;
269  return %IsInPrototypeChain(this, V);
270}
271
272
273// ECMA-262 - 15.2.4.6
274function ObjectPropertyIsEnumerable(V) {
275  var P = ToName(V);
276  if (%IsJSProxy(this)) {
277    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
278    if (IS_SYMBOL(V)) return false;
279
280    var desc = GetOwnProperty(this, P);
281    return IS_UNDEFINED(desc) ? false : desc.isEnumerable();
282  }
283  return %IsPropertyEnumerable(ToObject(this), P);
284}
285
286
287// Extensions for providing property getters and setters.
288function ObjectDefineGetter(name, fun) {
289  var receiver = this;
290  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
291    receiver = %GlobalReceiver(global);
292  }
293  if (!IS_SPEC_FUNCTION(fun)) {
294    throw new $TypeError(
295        'Object.prototype.__defineGetter__: Expecting function');
296  }
297  var desc = new PropertyDescriptor();
298  desc.setGet(fun);
299  desc.setEnumerable(true);
300  desc.setConfigurable(true);
301  DefineOwnProperty(ToObject(receiver), ToName(name), desc, false);
302}
303
304
305function ObjectLookupGetter(name) {
306  var receiver = this;
307  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
308    receiver = %GlobalReceiver(global);
309  }
310  return %LookupAccessor(ToObject(receiver), ToName(name), GETTER);
311}
312
313
314function ObjectDefineSetter(name, fun) {
315  var receiver = this;
316  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
317    receiver = %GlobalReceiver(global);
318  }
319  if (!IS_SPEC_FUNCTION(fun)) {
320    throw new $TypeError(
321        'Object.prototype.__defineSetter__: Expecting function');
322  }
323  var desc = new PropertyDescriptor();
324  desc.setSet(fun);
325  desc.setEnumerable(true);
326  desc.setConfigurable(true);
327  DefineOwnProperty(ToObject(receiver), ToName(name), desc, false);
328}
329
330
331function ObjectLookupSetter(name) {
332  var receiver = this;
333  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
334    receiver = %GlobalReceiver(global);
335  }
336  return %LookupAccessor(ToObject(receiver), ToName(name), SETTER);
337}
338
339
340function ObjectKeys(obj) {
341  if (!IS_SPEC_OBJECT(obj)) {
342    throw MakeTypeError("called_on_non_object", ["Object.keys"]);
343  }
344  if (%IsJSProxy(obj)) {
345    var handler = %GetHandler(obj);
346    var names = CallTrap0(handler, "keys", DerivedKeysTrap);
347    return ToNameArray(names, "keys", false);
348  }
349  return %LocalKeys(obj);
350}
351
352
353// ES5 8.10.1.
354function IsAccessorDescriptor(desc) {
355  if (IS_UNDEFINED(desc)) return false;
356  return desc.hasGetter() || desc.hasSetter();
357}
358
359
360// ES5 8.10.2.
361function IsDataDescriptor(desc) {
362  if (IS_UNDEFINED(desc)) return false;
363  return desc.hasValue() || desc.hasWritable();
364}
365
366
367// ES5 8.10.3.
368function IsGenericDescriptor(desc) {
369  if (IS_UNDEFINED(desc)) return false;
370  return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
371}
372
373
374function IsInconsistentDescriptor(desc) {
375  return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
376}
377
378
379// ES5 8.10.4
380function FromPropertyDescriptor(desc) {
381  if (IS_UNDEFINED(desc)) return desc;
382
383  if (IsDataDescriptor(desc)) {
384    return { value: desc.getValue(),
385             writable: desc.isWritable(),
386             enumerable: desc.isEnumerable(),
387             configurable: desc.isConfigurable() };
388  }
389  // Must be an AccessorDescriptor then. We never return a generic descriptor.
390  return { get: desc.getGet(),
391           set: desc.getSet() === ObjectSetProto ? ObjectPoisonProto
392                                                 : desc.getSet(),
393           enumerable: desc.isEnumerable(),
394           configurable: desc.isConfigurable() };
395}
396
397
398// Harmony Proxies
399function FromGenericPropertyDescriptor(desc) {
400  if (IS_UNDEFINED(desc)) return desc;
401  var obj = new $Object();
402
403  if (desc.hasValue()) {
404    %IgnoreAttributesAndSetProperty(obj, "value", desc.getValue(), NONE);
405  }
406  if (desc.hasWritable()) {
407    %IgnoreAttributesAndSetProperty(obj, "writable", desc.isWritable(), NONE);
408  }
409  if (desc.hasGetter()) {
410    %IgnoreAttributesAndSetProperty(obj, "get", desc.getGet(), NONE);
411  }
412  if (desc.hasSetter()) {
413    %IgnoreAttributesAndSetProperty(obj, "set", desc.getSet(), NONE);
414  }
415  if (desc.hasEnumerable()) {
416    %IgnoreAttributesAndSetProperty(obj, "enumerable",
417                                    desc.isEnumerable(), NONE);
418  }
419  if (desc.hasConfigurable()) {
420    %IgnoreAttributesAndSetProperty(obj, "configurable",
421                                    desc.isConfigurable(), NONE);
422  }
423  return obj;
424}
425
426
427// ES5 8.10.5.
428function ToPropertyDescriptor(obj) {
429  if (!IS_SPEC_OBJECT(obj)) {
430    throw MakeTypeError("property_desc_object", [obj]);
431  }
432  var desc = new PropertyDescriptor();
433
434  if ("enumerable" in obj) {
435    desc.setEnumerable(ToBoolean(obj.enumerable));
436  }
437
438  if ("configurable" in obj) {
439    desc.setConfigurable(ToBoolean(obj.configurable));
440  }
441
442  if ("value" in obj) {
443    desc.setValue(obj.value);
444  }
445
446  if ("writable" in obj) {
447    desc.setWritable(ToBoolean(obj.writable));
448  }
449
450  if ("get" in obj) {
451    var get = obj.get;
452    if (!IS_UNDEFINED(get) && !IS_SPEC_FUNCTION(get)) {
453      throw MakeTypeError("getter_must_be_callable", [get]);
454    }
455    desc.setGet(get);
456  }
457
458  if ("set" in obj) {
459    var set = obj.set;
460    if (!IS_UNDEFINED(set) && !IS_SPEC_FUNCTION(set)) {
461      throw MakeTypeError("setter_must_be_callable", [set]);
462    }
463    desc.setSet(set);
464  }
465
466  if (IsInconsistentDescriptor(desc)) {
467    throw MakeTypeError("value_and_accessor", [obj]);
468  }
469  return desc;
470}
471
472
473// For Harmony proxies.
474function ToCompletePropertyDescriptor(obj) {
475  var desc = ToPropertyDescriptor(obj);
476  if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) {
477    if (!desc.hasValue()) desc.setValue(UNDEFINED);
478    if (!desc.hasWritable()) desc.setWritable(false);
479  } else {
480    // Is accessor descriptor.
481    if (!desc.hasGetter()) desc.setGet(UNDEFINED);
482    if (!desc.hasSetter()) desc.setSet(UNDEFINED);
483  }
484  if (!desc.hasEnumerable()) desc.setEnumerable(false);
485  if (!desc.hasConfigurable()) desc.setConfigurable(false);
486  return desc;
487}
488
489
490function PropertyDescriptor() {
491  // Initialize here so they are all in-object and have the same map.
492  // Default values from ES5 8.6.1.
493  this.value_ = UNDEFINED;
494  this.hasValue_ = false;
495  this.writable_ = false;
496  this.hasWritable_ = false;
497  this.enumerable_ = false;
498  this.hasEnumerable_ = false;
499  this.configurable_ = false;
500  this.hasConfigurable_ = false;
501  this.get_ = UNDEFINED;
502  this.hasGetter_ = false;
503  this.set_ = UNDEFINED;
504  this.hasSetter_ = false;
505}
506
507SetUpLockedPrototype(PropertyDescriptor, $Array(
508    "value_",
509    "hasValue_",
510    "writable_",
511    "hasWritable_",
512    "enumerable_",
513    "hasEnumerable_",
514    "configurable_",
515    "hasConfigurable_",
516    "get_",
517    "hasGetter_",
518    "set_",
519    "hasSetter_"
520  ), $Array(
521    "toString", function() {
522      return "[object PropertyDescriptor]";
523    },
524    "setValue", function(value) {
525      this.value_ = value;
526      this.hasValue_ = true;
527    },
528    "getValue", function() {
529      return this.value_;
530    },
531    "hasValue", function() {
532      return this.hasValue_;
533    },
534    "setEnumerable", function(enumerable) {
535      this.enumerable_ = enumerable;
536        this.hasEnumerable_ = true;
537    },
538    "isEnumerable", function () {
539      return this.enumerable_;
540    },
541    "hasEnumerable", function() {
542      return this.hasEnumerable_;
543    },
544    "setWritable", function(writable) {
545      this.writable_ = writable;
546      this.hasWritable_ = true;
547    },
548    "isWritable", function() {
549      return this.writable_;
550    },
551    "hasWritable", function() {
552      return this.hasWritable_;
553    },
554    "setConfigurable", function(configurable) {
555      this.configurable_ = configurable;
556      this.hasConfigurable_ = true;
557    },
558    "hasConfigurable", function() {
559      return this.hasConfigurable_;
560    },
561    "isConfigurable", function() {
562      return this.configurable_;
563    },
564    "setGet", function(get) {
565      this.get_ = get;
566        this.hasGetter_ = true;
567    },
568    "getGet", function() {
569      return this.get_;
570    },
571    "hasGetter", function() {
572      return this.hasGetter_;
573    },
574    "setSet", function(set) {
575      this.set_ = set;
576      this.hasSetter_ = true;
577    },
578    "getSet", function() {
579      return this.set_;
580    },
581    "hasSetter", function() {
582      return this.hasSetter_;
583  }));
584
585
586// Converts an array returned from Runtime_GetOwnProperty to an actual
587// property descriptor. For a description of the array layout please
588// see the runtime.cc file.
589function ConvertDescriptorArrayToDescriptor(desc_array) {
590  if (desc_array === false) {
591    throw 'Internal error: invalid desc_array';
592  }
593
594  if (IS_UNDEFINED(desc_array)) {
595    return UNDEFINED;
596  }
597
598  var desc = new PropertyDescriptor();
599  // This is an accessor.
600  if (desc_array[IS_ACCESSOR_INDEX]) {
601    desc.setGet(desc_array[GETTER_INDEX]);
602    desc.setSet(desc_array[SETTER_INDEX]);
603  } else {
604    desc.setValue(desc_array[VALUE_INDEX]);
605    desc.setWritable(desc_array[WRITABLE_INDEX]);
606  }
607  desc.setEnumerable(desc_array[ENUMERABLE_INDEX]);
608  desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]);
609
610  return desc;
611}
612
613
614// For Harmony proxies.
615function GetTrap(handler, name, defaultTrap) {
616  var trap = handler[name];
617  if (IS_UNDEFINED(trap)) {
618    if (IS_UNDEFINED(defaultTrap)) {
619      throw MakeTypeError("handler_trap_missing", [handler, name]);
620    }
621    trap = defaultTrap;
622  } else if (!IS_SPEC_FUNCTION(trap)) {
623    throw MakeTypeError("handler_trap_must_be_callable", [handler, name]);
624  }
625  return trap;
626}
627
628
629function CallTrap0(handler, name, defaultTrap) {
630  return %_CallFunction(handler, GetTrap(handler, name, defaultTrap));
631}
632
633
634function CallTrap1(handler, name, defaultTrap, x) {
635  return %_CallFunction(handler, x, GetTrap(handler, name, defaultTrap));
636}
637
638
639function CallTrap2(handler, name, defaultTrap, x, y) {
640  return %_CallFunction(handler, x, y, GetTrap(handler, name, defaultTrap));
641}
642
643
644// ES5 section 8.12.1.
645function GetOwnProperty(obj, v) {
646  var p = ToName(v);
647  if (%IsJSProxy(obj)) {
648    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
649    if (IS_SYMBOL(v)) return UNDEFINED;
650
651    var handler = %GetHandler(obj);
652    var descriptor = CallTrap1(
653                         handler, "getOwnPropertyDescriptor", UNDEFINED, p);
654    if (IS_UNDEFINED(descriptor)) return descriptor;
655    var desc = ToCompletePropertyDescriptor(descriptor);
656    if (!desc.isConfigurable()) {
657      throw MakeTypeError("proxy_prop_not_configurable",
658                          [handler, "getOwnPropertyDescriptor", p, descriptor]);
659    }
660    return desc;
661  }
662
663  // GetOwnProperty returns an array indexed by the constants
664  // defined in macros.py.
665  // If p is not a property on obj undefined is returned.
666  var props = %GetOwnProperty(ToObject(obj), p);
667
668  // A false value here means that access checks failed.
669  if (props === false) return UNDEFINED;
670
671  return ConvertDescriptorArrayToDescriptor(props);
672}
673
674
675// ES5 section 8.12.7.
676function Delete(obj, p, should_throw) {
677  var desc = GetOwnProperty(obj, p);
678  if (IS_UNDEFINED(desc)) return true;
679  if (desc.isConfigurable()) {
680    %DeleteProperty(obj, p, 0);
681    return true;
682  } else if (should_throw) {
683    throw MakeTypeError("define_disallowed", [p]);
684  } else {
685    return;
686  }
687}
688
689
690// Harmony proxies.
691function DefineProxyProperty(obj, p, attributes, should_throw) {
692  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
693  if (IS_SYMBOL(p)) return false;
694
695  var handler = %GetHandler(obj);
696  var result = CallTrap2(handler, "defineProperty", UNDEFINED, p, attributes);
697  if (!ToBoolean(result)) {
698    if (should_throw) {
699      throw MakeTypeError("handler_returned_false",
700                          [handler, "defineProperty"]);
701    } else {
702      return false;
703    }
704  }
705  return true;
706}
707
708
709// ES5 8.12.9.
710function DefineObjectProperty(obj, p, desc, should_throw) {
711  var current_or_access = %GetOwnProperty(ToObject(obj), ToName(p));
712  // A false value here means that access checks failed.
713  if (current_or_access === false) return UNDEFINED;
714
715  var current = ConvertDescriptorArrayToDescriptor(current_or_access);
716  var extensible = %IsExtensible(ToObject(obj));
717
718  // Error handling according to spec.
719  // Step 3
720  if (IS_UNDEFINED(current) && !extensible) {
721    if (should_throw) {
722      throw MakeTypeError("define_disallowed", [p]);
723    } else {
724      return false;
725    }
726  }
727
728  if (!IS_UNDEFINED(current)) {
729    // Step 5 and 6
730    if ((IsGenericDescriptor(desc) ||
731         IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
732        (!desc.hasEnumerable() ||
733         SameValue(desc.isEnumerable(), current.isEnumerable())) &&
734        (!desc.hasConfigurable() ||
735         SameValue(desc.isConfigurable(), current.isConfigurable())) &&
736        (!desc.hasWritable() ||
737         SameValue(desc.isWritable(), current.isWritable())) &&
738        (!desc.hasValue() ||
739         SameValue(desc.getValue(), current.getValue())) &&
740        (!desc.hasGetter() ||
741         SameValue(desc.getGet(), current.getGet())) &&
742        (!desc.hasSetter() ||
743         SameValue(desc.getSet(), current.getSet()))) {
744      return true;
745    }
746    if (!current.isConfigurable()) {
747      // Step 7
748      if (desc.isConfigurable() ||
749          (desc.hasEnumerable() &&
750           desc.isEnumerable() != current.isEnumerable())) {
751        if (should_throw) {
752          throw MakeTypeError("redefine_disallowed", [p]);
753        } else {
754          return false;
755        }
756      }
757      // Step 8
758      if (!IsGenericDescriptor(desc)) {
759        // Step 9a
760        if (IsDataDescriptor(current) != IsDataDescriptor(desc)) {
761          if (should_throw) {
762            throw MakeTypeError("redefine_disallowed", [p]);
763          } else {
764            return false;
765          }
766        }
767        // Step 10a
768        if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
769          if (!current.isWritable() && desc.isWritable()) {
770            if (should_throw) {
771              throw MakeTypeError("redefine_disallowed", [p]);
772            } else {
773              return false;
774            }
775          }
776          if (!current.isWritable() && desc.hasValue() &&
777              !SameValue(desc.getValue(), current.getValue())) {
778            if (should_throw) {
779              throw MakeTypeError("redefine_disallowed", [p]);
780            } else {
781              return false;
782            }
783          }
784        }
785        // Step 11
786        if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
787          if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) {
788            if (should_throw) {
789              throw MakeTypeError("redefine_disallowed", [p]);
790            } else {
791              return false;
792            }
793          }
794          if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) {
795            if (should_throw) {
796              throw MakeTypeError("redefine_disallowed", [p]);
797            } else {
798              return false;
799            }
800          }
801        }
802      }
803    }
804  }
805
806  // Send flags - enumerable and configurable are common - writable is
807  // only send to the data descriptor.
808  // Take special care if enumerable and configurable is not defined on
809  // desc (we need to preserve the existing values from current).
810  var flag = NONE;
811  if (desc.hasEnumerable()) {
812    flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
813  } else if (!IS_UNDEFINED(current)) {
814    flag |= current.isEnumerable() ? 0 : DONT_ENUM;
815  } else {
816    flag |= DONT_ENUM;
817  }
818
819  if (desc.hasConfigurable()) {
820    flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
821  } else if (!IS_UNDEFINED(current)) {
822    flag |= current.isConfigurable() ? 0 : DONT_DELETE;
823  } else
824    flag |= DONT_DELETE;
825
826  if (IsDataDescriptor(desc) ||
827      (IsGenericDescriptor(desc) &&
828       (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
829    // There are 3 cases that lead here:
830    // Step 4a - defining a new data property.
831    // Steps 9b & 12 - replacing an existing accessor property with a data
832    //                 property.
833    // Step 12 - updating an existing data property with a data or generic
834    //           descriptor.
835
836    if (desc.hasWritable()) {
837      flag |= desc.isWritable() ? 0 : READ_ONLY;
838    } else if (!IS_UNDEFINED(current)) {
839      flag |= current.isWritable() ? 0 : READ_ONLY;
840    } else {
841      flag |= READ_ONLY;
842    }
843
844    var value = UNDEFINED;  // Default value is undefined.
845    if (desc.hasValue()) {
846      value = desc.getValue();
847    } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
848      value = current.getValue();
849    }
850
851    %DefineOrRedefineDataProperty(obj, p, value, flag);
852  } else {
853    // There are 3 cases that lead here:
854    // Step 4b - defining a new accessor property.
855    // Steps 9c & 12 - replacing an existing data property with an accessor
856    //                 property.
857    // Step 12 - updating an existing accessor property with an accessor
858    //           descriptor.
859    var getter = desc.hasGetter() ? desc.getGet() : null;
860    var setter = desc.hasSetter() ? desc.getSet() : null;
861    %DefineOrRedefineAccessorProperty(obj, p, getter, setter, flag);
862  }
863  return true;
864}
865
866
867// ES5 section 15.4.5.1.
868function DefineArrayProperty(obj, p, desc, should_throw) {
869  // Note that the length of an array is not actually stored as part of the
870  // property, hence we use generated code throughout this function instead of
871  // DefineObjectProperty() to modify its value.
872
873  // Step 3 - Special handling for length property.
874  if (p === "length") {
875    var length = obj.length;
876    var old_length = length;
877    if (!desc.hasValue()) {
878      return DefineObjectProperty(obj, "length", desc, should_throw);
879    }
880    var new_length = ToUint32(desc.getValue());
881    if (new_length != ToNumber(desc.getValue())) {
882      throw new $RangeError('defineProperty() array length out of range');
883    }
884    var length_desc = GetOwnProperty(obj, "length");
885    if (new_length != length && !length_desc.isWritable()) {
886      if (should_throw) {
887        throw MakeTypeError("redefine_disallowed", [p]);
888      } else {
889        return false;
890      }
891    }
892    var threw = false;
893
894    var emit_splice = %IsObserved(obj) && new_length !== old_length;
895    var removed;
896    if (emit_splice) {
897      BeginPerformSplice(obj);
898      removed = [];
899      if (new_length < old_length)
900        removed.length = old_length - new_length;
901    }
902
903    while (new_length < length--) {
904      var index = ToString(length);
905      if (emit_splice) {
906        var deletedDesc = GetOwnProperty(obj, index);
907        if (deletedDesc && deletedDesc.hasValue())
908          removed[length - new_length] = deletedDesc.getValue();
909      }
910      if (!Delete(obj, index, false)) {
911        new_length = length + 1;
912        threw = true;
913        break;
914      }
915    }
916    // Make sure the below call to DefineObjectProperty() doesn't overwrite
917    // any magic "length" property by removing the value.
918    // TODO(mstarzinger): This hack should be removed once we have addressed the
919    // respective TODO in Runtime_DefineOrRedefineDataProperty.
920    // For the time being, we need a hack to prevent Object.observe from
921    // generating two change records.
922    obj.length = new_length;
923    desc.value_ = UNDEFINED;
924    desc.hasValue_ = false;
925    threw = !DefineObjectProperty(obj, "length", desc, should_throw) || threw;
926    if (emit_splice) {
927      EndPerformSplice(obj);
928      EnqueueSpliceRecord(obj,
929          new_length < old_length ? new_length : old_length,
930          removed,
931          new_length > old_length ? new_length - old_length : 0);
932    }
933    if (threw) {
934      if (should_throw) {
935        throw MakeTypeError("redefine_disallowed", [p]);
936      } else {
937        return false;
938      }
939    }
940    return true;
941  }
942
943  // Step 4 - Special handling for array index.
944  var index = ToUint32(p);
945  var emit_splice = false;
946  if (ToString(index) == p && index != 4294967295) {
947    var length = obj.length;
948    if (index >= length && %IsObserved(obj)) {
949      emit_splice = true;
950      BeginPerformSplice(obj);
951    }
952
953    var length_desc = GetOwnProperty(obj, "length");
954    if ((index >= length && !length_desc.isWritable()) ||
955        !DefineObjectProperty(obj, p, desc, true)) {
956      if (emit_splice)
957        EndPerformSplice(obj);
958      if (should_throw) {
959        throw MakeTypeError("define_disallowed", [p]);
960      } else {
961        return false;
962      }
963    }
964    if (index >= length) {
965      obj.length = index + 1;
966    }
967    if (emit_splice) {
968      EndPerformSplice(obj);
969      EnqueueSpliceRecord(obj, length, [], index + 1 - length);
970    }
971    return true;
972  }
973
974  // Step 5 - Fallback to default implementation.
975  return DefineObjectProperty(obj, p, desc, should_throw);
976}
977
978
979// ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies.
980function DefineOwnProperty(obj, p, desc, should_throw) {
981  if (%IsJSProxy(obj)) {
982    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
983    if (IS_SYMBOL(p)) return false;
984
985    var attributes = FromGenericPropertyDescriptor(desc);
986    return DefineProxyProperty(obj, p, attributes, should_throw);
987  } else if (IS_ARRAY(obj)) {
988    return DefineArrayProperty(obj, p, desc, should_throw);
989  } else {
990    return DefineObjectProperty(obj, p, desc, should_throw);
991  }
992}
993
994
995// ES5 section 15.2.3.2.
996function ObjectGetPrototypeOf(obj) {
997  if (!IS_SPEC_OBJECT(obj)) {
998    throw MakeTypeError("called_on_non_object", ["Object.getPrototypeOf"]);
999  }
1000  return %GetPrototype(obj);
1001}
1002
1003
1004// ES5 section 15.2.3.3
1005function ObjectGetOwnPropertyDescriptor(obj, p) {
1006  if (!IS_SPEC_OBJECT(obj)) {
1007    throw MakeTypeError("called_on_non_object",
1008                        ["Object.getOwnPropertyDescriptor"]);
1009  }
1010  var desc = GetOwnProperty(obj, p);
1011  return FromPropertyDescriptor(desc);
1012}
1013
1014
1015// For Harmony proxies
1016function ToNameArray(obj, trap, includeSymbols) {
1017  if (!IS_SPEC_OBJECT(obj)) {
1018    throw MakeTypeError("proxy_non_object_prop_names", [obj, trap]);
1019  }
1020  var n = ToUint32(obj.length);
1021  var array = new $Array(n);
1022  var realLength = 0;
1023  var names = { __proto__: null };  // TODO(rossberg): use sets once ready.
1024  for (var index = 0; index < n; index++) {
1025    var s = ToName(obj[index]);
1026    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
1027    if (IS_SYMBOL(s) && !includeSymbols) continue;
1028    if (%HasLocalProperty(names, s)) {
1029      throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s]);
1030    }
1031    array[index] = s;
1032    ++realLength;
1033    names[s] = 0;
1034  }
1035  array.length = realLength;
1036  return array;
1037}
1038
1039
1040// ES5 section 15.2.3.4.
1041function ObjectGetOwnPropertyNames(obj) {
1042  if (!IS_SPEC_OBJECT(obj)) {
1043    throw MakeTypeError("called_on_non_object", ["Object.getOwnPropertyNames"]);
1044  }
1045  // Special handling for proxies.
1046  if (%IsJSProxy(obj)) {
1047    var handler = %GetHandler(obj);
1048    var names = CallTrap0(handler, "getOwnPropertyNames", UNDEFINED);
1049    return ToNameArray(names, "getOwnPropertyNames", false);
1050  }
1051
1052  var nameArrays = new InternalArray();
1053
1054  // Find all the indexed properties.
1055
1056  // Get the local element names.
1057  var localElementNames = %GetLocalElementNames(obj);
1058  for (var i = 0; i < localElementNames.length; ++i) {
1059    localElementNames[i] = %_NumberToString(localElementNames[i]);
1060  }
1061  nameArrays.push(localElementNames);
1062
1063  // Get names for indexed interceptor properties.
1064  var interceptorInfo = %GetInterceptorInfo(obj);
1065  if ((interceptorInfo & 1) != 0) {
1066    var indexedInterceptorNames = %GetIndexedInterceptorElementNames(obj);
1067    if (!IS_UNDEFINED(indexedInterceptorNames)) {
1068      nameArrays.push(indexedInterceptorNames);
1069    }
1070  }
1071
1072  // Find all the named properties.
1073
1074  // Get the local property names.
1075  nameArrays.push(%GetLocalPropertyNames(obj, false));
1076
1077  // Get names for named interceptor properties if any.
1078  if ((interceptorInfo & 2) != 0) {
1079    var namedInterceptorNames = %GetNamedInterceptorPropertyNames(obj);
1080    if (!IS_UNDEFINED(namedInterceptorNames)) {
1081      nameArrays.push(namedInterceptorNames);
1082    }
1083  }
1084
1085  var propertyNames =
1086      %Apply(InternalArray.prototype.concat,
1087             nameArrays[0], nameArrays, 1, nameArrays.length - 1);
1088
1089  // Property names are expected to be unique strings,
1090  // but interceptors can interfere with that assumption.
1091  if (interceptorInfo != 0) {
1092    var propertySet = { __proto__: null };
1093    var j = 0;
1094    for (var i = 0; i < propertyNames.length; ++i) {
1095      if (IS_SYMBOL(propertyNames[i])) continue;
1096      var name = ToString(propertyNames[i]);
1097      // We need to check for the exact property value since for intrinsic
1098      // properties like toString if(propertySet["toString"]) will always
1099      // succeed.
1100      if (propertySet[name] === true) {
1101        continue;
1102      }
1103      propertySet[name] = true;
1104      propertyNames[j++] = name;
1105    }
1106    propertyNames.length = j;
1107  }
1108
1109  return propertyNames;
1110}
1111
1112
1113// ES5 section 15.2.3.5.
1114function ObjectCreate(proto, properties) {
1115  if (!IS_SPEC_OBJECT(proto) && proto !== null) {
1116    throw MakeTypeError("proto_object_or_null", [proto]);
1117  }
1118  var obj = { __proto__: proto };
1119  if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
1120  return obj;
1121}
1122
1123
1124// ES5 section 15.2.3.6.
1125function ObjectDefineProperty(obj, p, attributes) {
1126  if (!IS_SPEC_OBJECT(obj)) {
1127    throw MakeTypeError("called_on_non_object", ["Object.defineProperty"]);
1128  }
1129  var name = ToName(p);
1130  if (%IsJSProxy(obj)) {
1131    // Clone the attributes object for protection.
1132    // TODO(rossberg): not spec'ed yet, so not sure if this should involve
1133    // non-own properties as it does (or non-enumerable ones, as it doesn't?).
1134    var attributesClone = { __proto__: null };
1135    for (var a in attributes) {
1136      attributesClone[a] = attributes[a];
1137    }
1138    DefineProxyProperty(obj, name, attributesClone, true);
1139    // The following would implement the spec as in the current proposal,
1140    // but after recent comments on es-discuss, is most likely obsolete.
1141    /*
1142    var defineObj = FromGenericPropertyDescriptor(desc);
1143    var names = ObjectGetOwnPropertyNames(attributes);
1144    var standardNames =
1145      {value: 0, writable: 0, get: 0, set: 0, enumerable: 0, configurable: 0};
1146    for (var i = 0; i < names.length; i++) {
1147      var N = names[i];
1148      if (!(%HasLocalProperty(standardNames, N))) {
1149        var attr = GetOwnProperty(attributes, N);
1150        DefineOwnProperty(descObj, N, attr, true);
1151      }
1152    }
1153    // This is really confusing the types, but it is what the proxies spec
1154    // currently requires:
1155    desc = descObj;
1156    */
1157  } else {
1158    var desc = ToPropertyDescriptor(attributes);
1159    DefineOwnProperty(obj, name, desc, true);
1160  }
1161  return obj;
1162}
1163
1164
1165function GetOwnEnumerablePropertyNames(properties) {
1166  var names = new InternalArray();
1167  for (var key in properties) {
1168    if (%HasLocalProperty(properties, key)) {
1169      names.push(key);
1170    }
1171  }
1172  return names;
1173}
1174
1175
1176// ES5 section 15.2.3.7.
1177function ObjectDefineProperties(obj, properties) {
1178  if (!IS_SPEC_OBJECT(obj)) {
1179    throw MakeTypeError("called_on_non_object", ["Object.defineProperties"]);
1180  }
1181  var props = ToObject(properties);
1182  var names = GetOwnEnumerablePropertyNames(props);
1183  var descriptors = new InternalArray();
1184  for (var i = 0; i < names.length; i++) {
1185    descriptors.push(ToPropertyDescriptor(props[names[i]]));
1186  }
1187  for (var i = 0; i < names.length; i++) {
1188    DefineOwnProperty(obj, names[i], descriptors[i], true);
1189  }
1190  return obj;
1191}
1192
1193
1194// Harmony proxies.
1195function ProxyFix(obj) {
1196  var handler = %GetHandler(obj);
1197  var props = CallTrap0(handler, "fix", UNDEFINED);
1198  if (IS_UNDEFINED(props)) {
1199    throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
1200  }
1201
1202  if (%IsJSFunctionProxy(obj)) {
1203    var callTrap = %GetCallTrap(obj);
1204    var constructTrap = %GetConstructTrap(obj);
1205    var code = DelegateCallAndConstruct(callTrap, constructTrap);
1206    %Fix(obj);  // becomes a regular function
1207    %SetCode(obj, code);
1208    // TODO(rossberg): What about length and other properties? Not specified.
1209    // We just put in some half-reasonable defaults for now.
1210    var prototype = new $Object();
1211    $Object.defineProperty(prototype, "constructor",
1212      {value: obj, writable: true, enumerable: false, configurable: true});
1213    // TODO(v8:1530): defineProperty does not handle prototype and length.
1214    %FunctionSetPrototype(obj, prototype);
1215    obj.length = 0;
1216  } else {
1217    %Fix(obj);
1218  }
1219  ObjectDefineProperties(obj, props);
1220}
1221
1222
1223// ES5 section 15.2.3.8.
1224function ObjectSeal(obj) {
1225  if (!IS_SPEC_OBJECT(obj)) {
1226    throw MakeTypeError("called_on_non_object", ["Object.seal"]);
1227  }
1228  if (%IsJSProxy(obj)) {
1229    ProxyFix(obj);
1230  }
1231  var names = ObjectGetOwnPropertyNames(obj);
1232  for (var i = 0; i < names.length; i++) {
1233    var name = names[i];
1234    var desc = GetOwnProperty(obj, name);
1235    if (desc.isConfigurable()) {
1236      desc.setConfigurable(false);
1237      DefineOwnProperty(obj, name, desc, true);
1238    }
1239  }
1240  %PreventExtensions(obj);
1241  return obj;
1242}
1243
1244
1245// ES5 section 15.2.3.9.
1246function ObjectFreeze(obj) {
1247  if (!IS_SPEC_OBJECT(obj)) {
1248    throw MakeTypeError("called_on_non_object", ["Object.freeze"]);
1249  }
1250  var isProxy = %IsJSProxy(obj);
1251  if (isProxy || %HasNonStrictArgumentsElements(obj) || %IsObserved(obj)) {
1252    if (isProxy) {
1253      ProxyFix(obj);
1254    }
1255    var names = ObjectGetOwnPropertyNames(obj);
1256    for (var i = 0; i < names.length; i++) {
1257      var name = names[i];
1258      var desc = GetOwnProperty(obj, name);
1259      if (desc.isWritable() || desc.isConfigurable()) {
1260        if (IsDataDescriptor(desc)) desc.setWritable(false);
1261        desc.setConfigurable(false);
1262        DefineOwnProperty(obj, name, desc, true);
1263      }
1264    }
1265    %PreventExtensions(obj);
1266  } else {
1267    // TODO(adamk): Is it worth going to this fast path if the
1268    // object's properties are already in dictionary mode?
1269    %ObjectFreeze(obj);
1270  }
1271  return obj;
1272}
1273
1274
1275// ES5 section 15.2.3.10
1276function ObjectPreventExtension(obj) {
1277  if (!IS_SPEC_OBJECT(obj)) {
1278    throw MakeTypeError("called_on_non_object", ["Object.preventExtension"]);
1279  }
1280  if (%IsJSProxy(obj)) {
1281    ProxyFix(obj);
1282  }
1283  %PreventExtensions(obj);
1284  return obj;
1285}
1286
1287
1288// ES5 section 15.2.3.11
1289function ObjectIsSealed(obj) {
1290  if (!IS_SPEC_OBJECT(obj)) {
1291    throw MakeTypeError("called_on_non_object", ["Object.isSealed"]);
1292  }
1293  if (%IsJSProxy(obj)) {
1294    return false;
1295  }
1296  if (%IsExtensible(obj)) {
1297    return false;
1298  }
1299  var names = ObjectGetOwnPropertyNames(obj);
1300  for (var i = 0; i < names.length; i++) {
1301    var name = names[i];
1302    var desc = GetOwnProperty(obj, name);
1303    if (desc.isConfigurable()) return false;
1304  }
1305  return true;
1306}
1307
1308
1309// ES5 section 15.2.3.12
1310function ObjectIsFrozen(obj) {
1311  if (!IS_SPEC_OBJECT(obj)) {
1312    throw MakeTypeError("called_on_non_object", ["Object.isFrozen"]);
1313  }
1314  if (%IsJSProxy(obj)) {
1315    return false;
1316  }
1317  if (%IsExtensible(obj)) {
1318    return false;
1319  }
1320  var names = ObjectGetOwnPropertyNames(obj);
1321  for (var i = 0; i < names.length; i++) {
1322    var name = names[i];
1323    var desc = GetOwnProperty(obj, name);
1324    if (IsDataDescriptor(desc) && desc.isWritable()) return false;
1325    if (desc.isConfigurable()) return false;
1326  }
1327  return true;
1328}
1329
1330
1331// ES5 section 15.2.3.13
1332function ObjectIsExtensible(obj) {
1333  if (!IS_SPEC_OBJECT(obj)) {
1334    throw MakeTypeError("called_on_non_object", ["Object.isExtensible"]);
1335  }
1336  if (%IsJSProxy(obj)) {
1337    return true;
1338  }
1339  return %IsExtensible(obj);
1340}
1341
1342
1343// Harmony egal.
1344function ObjectIs(obj1, obj2) {
1345  if (obj1 === obj2) {
1346    return (obj1 !== 0) || (1 / obj1 === 1 / obj2);
1347  } else {
1348    return (obj1 !== obj1) && (obj2 !== obj2);
1349  }
1350}
1351
1352
1353// Harmony __proto__ getter.
1354function ObjectGetProto() {
1355  return %GetPrototype(this);
1356}
1357
1358
1359// Harmony __proto__ setter.
1360function ObjectSetProto(obj) {
1361  return %SetPrototype(this, obj);
1362}
1363
1364
1365// Harmony __proto__ poison pill.
1366function ObjectPoisonProto(obj) {
1367  throw MakeTypeError("proto_poison_pill", []);
1368}
1369
1370
1371function ObjectConstructor(x) {
1372  if (%_IsConstructCall()) {
1373    if (x == null) return this;
1374    return ToObject(x);
1375  } else {
1376    if (x == null) return { };
1377    return ToObject(x);
1378  }
1379}
1380
1381
1382// ----------------------------------------------------------------------------
1383// Object
1384
1385function SetUpObject() {
1386  %CheckIsBootstrapping();
1387
1388  %SetNativeFlag($Object);
1389  %SetCode($Object, ObjectConstructor);
1390  %FunctionSetName(ObjectPoisonProto, "__proto__");
1391  %FunctionRemovePrototype(ObjectPoisonProto);
1392  %SetExpectedNumberOfProperties($Object, 4);
1393
1394  %SetProperty($Object.prototype, "constructor", $Object, DONT_ENUM);
1395
1396  // Set up non-enumerable functions on the Object.prototype object.
1397  InstallFunctions($Object.prototype, DONT_ENUM, $Array(
1398    "toString", ObjectToString,
1399    "toLocaleString", ObjectToLocaleString,
1400    "valueOf", ObjectValueOf,
1401    "hasOwnProperty", ObjectHasOwnProperty,
1402    "isPrototypeOf", ObjectIsPrototypeOf,
1403    "propertyIsEnumerable", ObjectPropertyIsEnumerable,
1404    "__defineGetter__", ObjectDefineGetter,
1405    "__lookupGetter__", ObjectLookupGetter,
1406    "__defineSetter__", ObjectDefineSetter,
1407    "__lookupSetter__", ObjectLookupSetter
1408  ));
1409  InstallGetterSetter($Object.prototype, "__proto__",
1410                      ObjectGetProto, ObjectSetProto);
1411
1412  // Set up non-enumerable functions in the Object object.
1413  InstallFunctions($Object, DONT_ENUM, $Array(
1414    "keys", ObjectKeys,
1415    "create", ObjectCreate,
1416    "defineProperty", ObjectDefineProperty,
1417    "defineProperties", ObjectDefineProperties,
1418    "freeze", ObjectFreeze,
1419    "getPrototypeOf", ObjectGetPrototypeOf,
1420    "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
1421    "getOwnPropertyNames", ObjectGetOwnPropertyNames,
1422    "is", ObjectIs,
1423    "isExtensible", ObjectIsExtensible,
1424    "isFrozen", ObjectIsFrozen,
1425    "isSealed", ObjectIsSealed,
1426    "preventExtensions", ObjectPreventExtension,
1427    "seal", ObjectSeal
1428  ));
1429}
1430
1431SetUpObject();
1432
1433
1434// ----------------------------------------------------------------------------
1435// Boolean
1436
1437function BooleanConstructor(x) {
1438  if (%_IsConstructCall()) {
1439    %_SetValueOf(this, ToBoolean(x));
1440  } else {
1441    return ToBoolean(x);
1442  }
1443}
1444
1445
1446function BooleanToString() {
1447  // NOTE: Both Boolean objects and values can enter here as
1448  // 'this'. This is not as dictated by ECMA-262.
1449  var b = this;
1450  if (!IS_BOOLEAN(b)) {
1451    if (!IS_BOOLEAN_WRAPPER(b)) {
1452      throw new $TypeError('Boolean.prototype.toString is not generic');
1453    }
1454    b = %_ValueOf(b);
1455  }
1456  return b ? 'true' : 'false';
1457}
1458
1459
1460function BooleanValueOf() {
1461  // NOTE: Both Boolean objects and values can enter here as
1462  // 'this'. This is not as dictated by ECMA-262.
1463  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) {
1464    throw new $TypeError('Boolean.prototype.valueOf is not generic');
1465  }
1466  return %_ValueOf(this);
1467}
1468
1469
1470// ----------------------------------------------------------------------------
1471
1472function SetUpBoolean () {
1473  %CheckIsBootstrapping();
1474
1475  %SetCode($Boolean, BooleanConstructor);
1476  %FunctionSetPrototype($Boolean, new $Boolean(false));
1477  %SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM);
1478
1479  InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
1480    "toString", BooleanToString,
1481    "valueOf", BooleanValueOf
1482  ));
1483}
1484
1485SetUpBoolean();
1486
1487
1488// ----------------------------------------------------------------------------
1489// Number
1490
1491function NumberConstructor(x) {
1492  var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
1493  if (%_IsConstructCall()) {
1494    %_SetValueOf(this, value);
1495  } else {
1496    return value;
1497  }
1498}
1499
1500
1501// ECMA-262 section 15.7.4.2.
1502function NumberToString(radix) {
1503  // NOTE: Both Number objects and values can enter here as
1504  // 'this'. This is not as dictated by ECMA-262.
1505  var number = this;
1506  if (!IS_NUMBER(this)) {
1507    if (!IS_NUMBER_WRAPPER(this)) {
1508      throw new $TypeError('Number.prototype.toString is not generic');
1509    }
1510    // Get the value of this number in case it's an object.
1511    number = %_ValueOf(this);
1512  }
1513  // Fast case: Convert number in radix 10.
1514  if (IS_UNDEFINED(radix) || radix === 10) {
1515    return %_NumberToString(number);
1516  }
1517
1518  // Convert the radix to an integer and check the range.
1519  radix = TO_INTEGER(radix);
1520  if (radix < 2 || radix > 36) {
1521    throw new $RangeError('toString() radix argument must be between 2 and 36');
1522  }
1523  // Convert the number to a string in the given radix.
1524  return %NumberToRadixString(number, radix);
1525}
1526
1527
1528// ECMA-262 section 15.7.4.3
1529function NumberToLocaleString() {
1530  return %_CallFunction(this, NumberToString);
1531}
1532
1533
1534// ECMA-262 section 15.7.4.4
1535function NumberValueOf() {
1536  // NOTE: Both Number objects and values can enter here as
1537  // 'this'. This is not as dictated by ECMA-262.
1538  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) {
1539    throw new $TypeError('Number.prototype.valueOf is not generic');
1540  }
1541  return %_ValueOf(this);
1542}
1543
1544
1545// ECMA-262 section 15.7.4.5
1546function NumberToFixed(fractionDigits) {
1547  var x = this;
1548  if (!IS_NUMBER(this)) {
1549    if (!IS_NUMBER_WRAPPER(this)) {
1550      throw MakeTypeError("incompatible_method_receiver",
1551                          ["Number.prototype.toFixed", this]);
1552    }
1553    // Get the value of this number in case it's an object.
1554    x = %_ValueOf(this);
1555  }
1556  var f = TO_INTEGER(fractionDigits);
1557
1558  if (f < 0 || f > 20) {
1559    throw new $RangeError("toFixed() digits argument must be between 0 and 20");
1560  }
1561
1562  if (NUMBER_IS_NAN(x)) return "NaN";
1563  if (x == INFINITY) return "Infinity";
1564  if (x == -INFINITY) return "-Infinity";
1565
1566  return %NumberToFixed(x, f);
1567}
1568
1569
1570// ECMA-262 section 15.7.4.6
1571function NumberToExponential(fractionDigits) {
1572  var x = this;
1573  if (!IS_NUMBER(this)) {
1574    if (!IS_NUMBER_WRAPPER(this)) {
1575      throw MakeTypeError("incompatible_method_receiver",
1576                          ["Number.prototype.toExponential", this]);
1577    }
1578    // Get the value of this number in case it's an object.
1579    x = %_ValueOf(this);
1580  }
1581  var f = IS_UNDEFINED(fractionDigits) ? UNDEFINED : TO_INTEGER(fractionDigits);
1582
1583  if (NUMBER_IS_NAN(x)) return "NaN";
1584  if (x == INFINITY) return "Infinity";
1585  if (x == -INFINITY) return "-Infinity";
1586
1587  if (IS_UNDEFINED(f)) {
1588    f = -1;  // Signal for runtime function that f is not defined.
1589  } else if (f < 0 || f > 20) {
1590    throw new $RangeError("toExponential() argument must be between 0 and 20");
1591  }
1592  return %NumberToExponential(x, f);
1593}
1594
1595
1596// ECMA-262 section 15.7.4.7
1597function NumberToPrecision(precision) {
1598  var x = this;
1599  if (!IS_NUMBER(this)) {
1600    if (!IS_NUMBER_WRAPPER(this)) {
1601      throw MakeTypeError("incompatible_method_receiver",
1602                          ["Number.prototype.toPrecision", this]);
1603    }
1604    // Get the value of this number in case it's an object.
1605    x = %_ValueOf(this);
1606  }
1607  if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
1608  var p = TO_INTEGER(precision);
1609
1610  if (NUMBER_IS_NAN(x)) return "NaN";
1611  if (x == INFINITY) return "Infinity";
1612  if (x == -INFINITY) return "-Infinity";
1613
1614  if (p < 1 || p > 21) {
1615    throw new $RangeError("toPrecision() argument must be between 1 and 21");
1616  }
1617  return %NumberToPrecision(x, p);
1618}
1619
1620
1621// Harmony isFinite.
1622function NumberIsFinite(number) {
1623  return IS_NUMBER(number) && NUMBER_IS_FINITE(number);
1624}
1625
1626
1627// Harmony isNaN.
1628function NumberIsNaN(number) {
1629  return IS_NUMBER(number) && NUMBER_IS_NAN(number);
1630}
1631
1632
1633// ----------------------------------------------------------------------------
1634
1635function SetUpNumber() {
1636  %CheckIsBootstrapping();
1637
1638  %SetCode($Number, NumberConstructor);
1639  %FunctionSetPrototype($Number, new $Number(0));
1640
1641  %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
1642  // Set up the constructor property on the Number prototype object.
1643  %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);
1644
1645  %OptimizeObjectForAddingMultipleProperties($Number, 5);
1646  // ECMA-262 section 15.7.3.1.
1647  %SetProperty($Number,
1648               "MAX_VALUE",
1649               1.7976931348623157e+308,
1650               DONT_ENUM | DONT_DELETE | READ_ONLY);
1651
1652  // ECMA-262 section 15.7.3.2.
1653  %SetProperty($Number, "MIN_VALUE", 5e-324,
1654               DONT_ENUM | DONT_DELETE | READ_ONLY);
1655
1656  // ECMA-262 section 15.7.3.3.
1657  %SetProperty($Number, "NaN", NAN, DONT_ENUM | DONT_DELETE | READ_ONLY);
1658
1659  // ECMA-262 section 15.7.3.4.
1660  %SetProperty($Number,
1661               "NEGATIVE_INFINITY",
1662               -INFINITY,
1663               DONT_ENUM | DONT_DELETE | READ_ONLY);
1664
1665  // ECMA-262 section 15.7.3.5.
1666  %SetProperty($Number,
1667               "POSITIVE_INFINITY",
1668               INFINITY,
1669               DONT_ENUM | DONT_DELETE | READ_ONLY);
1670  %ToFastProperties($Number);
1671
1672  // Set up non-enumerable functions on the Number prototype object.
1673  InstallFunctions($Number.prototype, DONT_ENUM, $Array(
1674    "toString", NumberToString,
1675    "toLocaleString", NumberToLocaleString,
1676    "valueOf", NumberValueOf,
1677    "toFixed", NumberToFixed,
1678    "toExponential", NumberToExponential,
1679    "toPrecision", NumberToPrecision
1680  ));
1681  InstallFunctions($Number, DONT_ENUM, $Array(
1682    "isFinite", NumberIsFinite,
1683    "isNaN", NumberIsNaN
1684  ));
1685}
1686
1687SetUpNumber();
1688
1689
1690// ----------------------------------------------------------------------------
1691// Function
1692
1693function FunctionSourceString(func) {
1694  while (%IsJSFunctionProxy(func)) {
1695    func = %GetCallTrap(func);
1696  }
1697
1698  if (!IS_FUNCTION(func)) {
1699    throw new $TypeError('Function.prototype.toString is not generic');
1700  }
1701
1702  var source = %FunctionGetSourceCode(func);
1703  if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
1704    var name = %FunctionGetName(func);
1705    if (name) {
1706      // Mimic what KJS does.
1707      return 'function ' + name + '() { [native code] }';
1708    } else {
1709      return 'function () { [native code] }';
1710    }
1711  }
1712
1713  var name = %FunctionNameShouldPrintAsAnonymous(func)
1714      ? 'anonymous'
1715      : %FunctionGetName(func);
1716  var head = %FunctionIsGenerator(func) ? 'function* ' : 'function ';
1717  return head + name + source;
1718}
1719
1720
1721function FunctionToString() {
1722  return FunctionSourceString(this);
1723}
1724
1725
1726// ES5 15.3.4.5
1727function FunctionBind(this_arg) { // Length is 1.
1728  if (!IS_SPEC_FUNCTION(this)) {
1729    throw new $TypeError('Bind must be called on a function');
1730  }
1731  var boundFunction = function () {
1732    // Poison .arguments and .caller, but is otherwise not detectable.
1733    "use strict";
1734    // This function must not use any object literals (Object, Array, RegExp),
1735    // since the literals-array is being used to store the bound data.
1736    if (%_IsConstructCall()) {
1737      return %NewObjectFromBound(boundFunction);
1738    }
1739    var bindings = %BoundFunctionGetBindings(boundFunction);
1740
1741    var argc = %_ArgumentsLength();
1742    if (argc == 0) {
1743      return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
1744    }
1745    if (bindings.length === 2) {
1746      return %Apply(bindings[0], bindings[1], arguments, 0, argc);
1747    }
1748    var bound_argc = bindings.length - 2;
1749    var argv = new InternalArray(bound_argc + argc);
1750    for (var i = 0; i < bound_argc; i++) {
1751      argv[i] = bindings[i + 2];
1752    }
1753    for (var j = 0; j < argc; j++) {
1754      argv[i++] = %_Arguments(j);
1755    }
1756    return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
1757  };
1758
1759  %FunctionRemovePrototype(boundFunction);
1760  var new_length = 0;
1761  if (%_ClassOf(this) == "Function") {
1762    // Function or FunctionProxy.
1763    var old_length = this.length;
1764    // FunctionProxies might provide a non-UInt32 value. If so, ignore it.
1765    if ((typeof old_length === "number") &&
1766        ((old_length >>> 0) === old_length)) {
1767      var argc = %_ArgumentsLength();
1768      if (argc > 0) argc--;  // Don't count the thisArg as parameter.
1769      new_length = old_length - argc;
1770      if (new_length < 0) new_length = 0;
1771    }
1772  }
1773  // This runtime function finds any remaining arguments on the stack,
1774  // so we don't pass the arguments object.
1775  var result = %FunctionBindArguments(boundFunction, this,
1776                                      this_arg, new_length);
1777
1778  // We already have caller and arguments properties on functions,
1779  // which are non-configurable. It therefore makes no sence to
1780  // try to redefine these as defined by the spec. The spec says
1781  // that bind should make these throw a TypeError if get or set
1782  // is called and make them non-enumerable and non-configurable.
1783  // To be consistent with our normal functions we leave this as it is.
1784  // TODO(lrn): Do set these to be thrower.
1785  return result;
1786}
1787
1788
1789function NewFunctionString(arguments, function_token) {
1790  var n = arguments.length;
1791  var p = '';
1792  if (n > 1) {
1793    p = ToString(arguments[0]);
1794    for (var i = 1; i < n - 1; i++) {
1795      p += ',' + ToString(arguments[i]);
1796    }
1797    // If the formal parameters string include ) - an illegal
1798    // character - it may make the combined function expression
1799    // compile. We avoid this problem by checking for this early on.
1800    if (%_CallFunction(p, ')', StringIndexOf) != -1) {
1801      throw MakeSyntaxError('paren_in_arg_string', []);
1802    }
1803    // If the formal parameters include an unbalanced block comment, the
1804    // function must be rejected. Since JavaScript does not allow nested
1805    // comments we can include a trailing block comment to catch this.
1806    p += '\n/' + '**/';
1807  }
1808  var body = (n > 0) ? ToString(arguments[n - 1]) : '';
1809  return '(' + function_token + '(' + p + ') {\n' + body + '\n})';
1810}
1811
1812
1813function FunctionConstructor(arg1) {  // length == 1
1814  var source = NewFunctionString(arguments, 'function');
1815  var global_receiver = %GlobalReceiver(global);
1816  // Compile the string in the constructor and not a helper so that errors
1817  // appear to come from here.
1818  var f = %_CallFunction(global_receiver, %CompileString(source, true));
1819  %FunctionMarkNameShouldPrintAsAnonymous(f);
1820  return f;
1821}
1822
1823
1824// ----------------------------------------------------------------------------
1825
1826function SetUpFunction() {
1827  %CheckIsBootstrapping();
1828
1829  %SetCode($Function, FunctionConstructor);
1830  %SetProperty($Function.prototype, "constructor", $Function, DONT_ENUM);
1831
1832  InstallFunctions($Function.prototype, DONT_ENUM, $Array(
1833    "bind", FunctionBind,
1834    "toString", FunctionToString
1835  ));
1836}
1837
1838SetUpFunction();
1839
1840
1841//----------------------------------------------------------------------------
1842
1843// TODO(rossberg): very simple abstraction for generic microtask queue.
1844// Eventually, we should move to a real event queue that allows to maintain
1845// relative ordering of different kinds of tasks.
1846
1847RunMicrotasks.runners = new InternalArray;
1848
1849function RunMicrotasks() {
1850  while (%SetMicrotaskPending(false)) {
1851    for (var i in RunMicrotasks.runners) RunMicrotasks.runners[i]();
1852  }
1853}
1854