v8natives.js 53 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4 5 6

// This file relies on the fact that the following declarations have been made
// in runtime.js:
7 8 9 10 11
// var $Object = global.Object;
// var $Boolean = global.Boolean;
// var $Number = global.Number;
// var $Function = global.Function;
// var $Array = global.Array;
12 13
//
// in math.js:
14
// var $floor = MathFloor
15

16 17
var $isNaN = GlobalIsNaN;
var $isFinite = GlobalIsFinite;
18

19
// ----------------------------------------------------------------------------
20

21 22
// Helper function used to install functions on objects.
function InstallFunctions(object, attributes, functions) {
23
  if (functions.length >= 8) {
24
    %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
25
  }
26 27 28 29
  for (var i = 0; i < functions.length; i += 2) {
    var key = functions[i];
    var f = functions[i + 1];
    %FunctionSetName(f, key);
30
    %FunctionRemovePrototype(f);
31
    %SetProperty(object, key, f, attributes);
32
    %SetNativeFlag(f);
33
  }
34
  %ToFastProperties(object);
35
}
36

37

38
// Helper function to install a getter-only accessor property.
39 40 41 42 43 44 45 46
function InstallGetter(object, name, getter) {
  %FunctionSetName(getter, name);
  %FunctionRemovePrototype(getter);
  %DefineOrRedefineAccessorProperty(object, name, getter, null, DONT_ENUM);
  %SetNativeFlag(getter);
}


47 48 49 50 51 52 53 54 55 56 57 58
// Helper function to install a getter/setter accessor property.
function InstallGetterSetter(object, name, getter, setter) {
  %FunctionSetName(getter, name);
  %FunctionSetName(setter, name);
  %FunctionRemovePrototype(getter);
  %FunctionRemovePrototype(setter);
  %DefineOrRedefineAccessorProperty(object, name, getter, setter, DONT_ENUM);
  %SetNativeFlag(getter);
  %SetNativeFlag(setter);
}


59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
// Helper function for installing constant properties on objects.
function InstallConstants(object, constants) {
  if (constants.length >= 4) {
    %OptimizeObjectForAddingMultipleProperties(object, constants.length >> 1);
  }
  var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
  for (var i = 0; i < constants.length; i += 2) {
    var name = constants[i];
    var k = constants[i + 1];
    %SetProperty(object, name, k, attributes);
  }
  %ToFastProperties(object);
}


74
// Prevents changes to the prototype of a built-in function.
75 76 77 78 79 80 81 82 83 84 85 86 87 88
// The "prototype" property of the function object is made non-configurable,
// and the prototype object is made non-extensible. The latter prevents
// changing the __proto__ property.
function SetUpLockedPrototype(constructor, fields, methods) {
  %CheckIsBootstrapping();
  var prototype = constructor.prototype;
  // Install functions first, because this function is used to initialize
  // PropertyDescriptor itself.
  var property_count = (methods.length >> 1) + (fields ? fields.length : 0);
  if (property_count >= 4) {
    %OptimizeObjectForAddingMultipleProperties(prototype, property_count);
  }
  if (fields) {
    for (var i = 0; i < fields.length; i++) {
89
      %SetProperty(prototype, fields[i], UNDEFINED, DONT_ENUM | DONT_DELETE);
90 91 92 93 94 95 96 97
    }
  }
  for (var i = 0; i < methods.length; i += 2) {
    var key = methods[i];
    var f = methods[i + 1];
    %SetProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY);
    %SetNativeFlag(f);
  }
98
  %SetPrototype(prototype, null);
99 100 101 102
  %ToFastProperties(prototype);
}


103
// ----------------------------------------------------------------------------
104 105 106


// ECMA 262 - 15.1.4
107
function GlobalIsNaN(number) {
108 109
  if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
  return NUMBER_IS_NAN(number);
110
}
111 112 113


// ECMA 262 - 15.1.5
114
function GlobalIsFinite(number) {
115
  if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
116
  return NUMBER_IS_FINITE(number);
117
}
118 119 120


// ECMA-262 - 15.1.2.2
121
function GlobalParseInt(string, radix) {
122
  if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
123 124 125 126 127 128
    // Some people use parseInt instead of Math.floor.  This
    // optimization makes parseInt on a Smi 12 times faster (60ns
    // vs 800ns).  The following optimization makes parseInt on a
    // non-Smi number 9 times faster (230ns vs 2070ns).  Together
    // they make parseInt on a string 1.4% slower (274ns vs 270ns).
    if (%_IsSmi(string)) return string;
129
    if (IS_NUMBER(string) &&
130 131
        ((0.01 < string && string < 1e9) ||
            (-1e9 < string && string < -0.01))) {
132 133
      // Truncate number.
      return string | 0;
134
    }
135
    string = TO_STRING_INLINE(string);
136
    radix = radix | 0;
137
  } else {
138 139
    // The spec says ToString should be evaluated before ToInt32.
    string = TO_STRING_INLINE(string);
140
    radix = TO_INT32(radix);
141
    if (!(radix == 0 || (2 <= radix && radix <= 36))) {
142
      return NAN;
143
    }
144
  }
145

146 147 148 149 150
  if (%_HasCachedArrayIndex(string) &&
      (radix == 0 || radix == 10)) {
    return %_GetCachedArrayIndex(string);
  }
  return %StringParseInt(string, radix);
151
}
152 153 154


// ECMA-262 - 15.1.2.3
155
function GlobalParseFloat(string) {
156 157 158
  string = TO_STRING_INLINE(string);
  if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
  return %StringParseFloat(string);
159 160
}

161

162 163 164
function GlobalEval(x) {
  if (!IS_STRING(x)) return x;

165 166 167
  // For consistency with JSC we require the global object passed to
  // eval to be the global object from which 'eval' originated. This
  // is not mandated by the spec.
168 169
  // We only throw if the global has been detached, since we need the
  // receiver as this-value for the call.
170
  if (!%IsAttachedGlobal(global)) {
171
    throw new $EvalError('The "this" value passed to eval must ' +
172
                         'be the global object from which eval originated');
173
  }
174

175 176
  var global_receiver = %GlobalReceiver(global);

177
  var f = %CompileString(x, false);
178 179
  if (!IS_FUNCTION(f)) return f;

180
  return %_CallFunction(global_receiver, f);
181 182 183 184 185
}


// ----------------------------------------------------------------------------

186 187 188
// Set up global object.
function SetUpGlobal() {
  %CheckIsBootstrapping();
189

190 191
  var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;

192
  // ECMA 262 - 15.1.1.1.
193
  %SetProperty(global, "NaN", NAN, attributes);
194 195

  // ECMA-262 - 15.1.1.2.
196
  %SetProperty(global, "Infinity", INFINITY, attributes);
197 198

  // ECMA-262 - 15.1.1.3.
199
  %SetProperty(global, "undefined", UNDEFINED, attributes);
200

201
  // Set up non-enumerable function on the global object.
202 203 204 205 206
  InstallFunctions(global, DONT_ENUM, $Array(
    "isNaN", GlobalIsNaN,
    "isFinite", GlobalIsFinite,
    "parseInt", GlobalParseInt,
    "parseFloat", GlobalParseFloat,
207
    "eval", GlobalEval
208 209 210
  ));
}

211
SetUpGlobal();
212 213 214 215 216


// ----------------------------------------------------------------------------
// Object

217 218
// ECMA-262 - 15.2.4.2
function ObjectToString() {
219 220
  if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
  if (IS_NULL(this)) return "[object Null]";
221
  return "[object " + %_ClassOf(ToObject(this)) + "]";
222
}
223 224


225 226
// ECMA-262 - 15.2.4.3
function ObjectToLocaleString() {
227
  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.toLocaleString");
228
  return this.toString();
229
}
230 231


232 233
// ECMA-262 - 15.2.4.4
function ObjectValueOf() {
234
  return ToObject(this);
235
}
236 237


238 239
// ECMA-262 - 15.2.4.5
function ObjectHasOwnProperty(V) {
240
  if (%IsJSProxy(this)) {
241 242 243
    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
    if (IS_SYMBOL(V)) return false;

244
    var handler = %GetHandler(this);
245
    return CallTrap1(handler, "hasOwn", DerivedHasOwnTrap, ToName(V));
246
  }
247
  return %HasOwnProperty(TO_OBJECT_INLINE(this), ToName(V));
248
}
249 250


251 252
// ECMA-262 - 15.2.4.6
function ObjectIsPrototypeOf(V) {
253
  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.isPrototypeOf");
254
  if (!IS_SPEC_OBJECT(V)) return false;
255
  return %IsInPrototypeChain(this, V);
256
}
257 258


259 260
// ECMA-262 - 15.2.4.6
function ObjectPropertyIsEnumerable(V) {
261
  var P = ToName(V);
262
  if (%IsJSProxy(this)) {
263 264 265
    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
    if (IS_SYMBOL(V)) return false;

266
    var desc = GetOwnPropertyJS(this, P);
267 268 269
    return IS_UNDEFINED(desc) ? false : desc.isEnumerable();
  }
  return %IsPropertyEnumerable(ToObject(this), P);
270
}
271 272 273


// Extensions for providing property getters and setters.
274
function ObjectDefineGetter(name, fun) {
275 276 277
  var receiver = this;
  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
    receiver = %GlobalReceiver(global);
278
  }
279
  if (!IS_SPEC_FUNCTION(fun)) {
280 281
    throw new $TypeError(
        'Object.prototype.__defineGetter__: Expecting function');
282
  }
283 284 285 286
  var desc = new PropertyDescriptor();
  desc.setGet(fun);
  desc.setEnumerable(true);
  desc.setConfigurable(true);
287
  DefineOwnProperty(ToObject(receiver), ToName(name), desc, false);
288
}
289 290


291
function ObjectLookupGetter(name) {
292 293 294
  var receiver = this;
  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
    receiver = %GlobalReceiver(global);
295
  }
296
  return %LookupAccessor(ToObject(receiver), ToName(name), GETTER);
297
}
298 299


300
function ObjectDefineSetter(name, fun) {
301 302 303
  var receiver = this;
  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
    receiver = %GlobalReceiver(global);
304
  }
305
  if (!IS_SPEC_FUNCTION(fun)) {
306 307 308
    throw new $TypeError(
        'Object.prototype.__defineSetter__: Expecting function');
  }
309 310 311 312
  var desc = new PropertyDescriptor();
  desc.setSet(fun);
  desc.setEnumerable(true);
  desc.setConfigurable(true);
313
  DefineOwnProperty(ToObject(receiver), ToName(name), desc, false);
314
}
315 316


317
function ObjectLookupSetter(name) {
318 319 320
  var receiver = this;
  if (receiver == null && !IS_UNDETECTABLE(receiver)) {
    receiver = %GlobalReceiver(global);
321
  }
322
  return %LookupAccessor(ToObject(receiver), ToName(name), SETTER);
323
}
324 325


326
function ObjectKeys(obj) {
327
  if (!IS_SPEC_OBJECT(obj)) {
328
    throw MakeTypeError("called_on_non_object", ["Object.keys"]);
329
  }
330 331
  if (%IsJSProxy(obj)) {
    var handler = %GetHandler(obj);
332
    var names = CallTrap0(handler, "keys", DerivedKeysTrap);
333
    return ToNameArray(names, "keys", false);
334
  }
335
  return %OwnKeys(obj);
336 337 338
}


339 340 341
// ES5 8.10.1.
function IsAccessorDescriptor(desc) {
  if (IS_UNDEFINED(desc)) return false;
342
  return desc.hasGetter() || desc.hasSetter();
343 344 345 346 347 348
}


// ES5 8.10.2.
function IsDataDescriptor(desc) {
  if (IS_UNDEFINED(desc)) return false;
349
  return desc.hasValue() || desc.hasWritable();
350 351 352 353 354
}


// ES5 8.10.3.
function IsGenericDescriptor(desc) {
355
  if (IS_UNDEFINED(desc)) return false;
356 357 358 359 360 361 362 363
  return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
}


function IsInconsistentDescriptor(desc) {
  return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
}

364

365 366
// ES5 8.10.4
function FromPropertyDescriptor(desc) {
367
  if (IS_UNDEFINED(desc)) return desc;
368

369
  if (IsDataDescriptor(desc)) {
370 371 372 373
    return { value: desc.getValue(),
             writable: desc.isWritable(),
             enumerable: desc.isEnumerable(),
             configurable: desc.isConfigurable() };
374
  }
375 376
  // Must be an AccessorDescriptor then. We never return a generic descriptor.
  return { get: desc.getGet(),
377
           set: desc.getSet(),
378 379
           enumerable: desc.isEnumerable(),
           configurable: desc.isConfigurable() };
380
}
381

382

383 384 385 386
// Harmony Proxies
function FromGenericPropertyDescriptor(desc) {
  if (IS_UNDEFINED(desc)) return desc;
  var obj = new $Object();
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

  if (desc.hasValue()) {
    %IgnoreAttributesAndSetProperty(obj, "value", desc.getValue(), NONE);
  }
  if (desc.hasWritable()) {
    %IgnoreAttributesAndSetProperty(obj, "writable", desc.isWritable(), NONE);
  }
  if (desc.hasGetter()) {
    %IgnoreAttributesAndSetProperty(obj, "get", desc.getGet(), NONE);
  }
  if (desc.hasSetter()) {
    %IgnoreAttributesAndSetProperty(obj, "set", desc.getSet(), NONE);
  }
  if (desc.hasEnumerable()) {
    %IgnoreAttributesAndSetProperty(obj, "enumerable",
                                    desc.isEnumerable(), NONE);
  }
  if (desc.hasConfigurable()) {
    %IgnoreAttributesAndSetProperty(obj, "configurable",
                                    desc.isConfigurable(), NONE);
  }
408 409 410
  return obj;
}

411

412 413
// ES5 8.10.5.
function ToPropertyDescriptor(obj) {
414
  if (!IS_SPEC_OBJECT(obj)) {
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    throw MakeTypeError("property_desc_object", [obj]);
  }
  var desc = new PropertyDescriptor();

  if ("enumerable" in obj) {
    desc.setEnumerable(ToBoolean(obj.enumerable));
  }

  if ("configurable" in obj) {
    desc.setConfigurable(ToBoolean(obj.configurable));
  }

  if ("value" in obj) {
    desc.setValue(obj.value);
  }

  if ("writable" in obj) {
    desc.setWritable(ToBoolean(obj.writable));
  }

  if ("get" in obj) {
    var get = obj.get;
437
    if (!IS_UNDEFINED(get) && !IS_SPEC_FUNCTION(get)) {
438 439 440 441 442 443 444
      throw MakeTypeError("getter_must_be_callable", [get]);
    }
    desc.setGet(get);
  }

  if ("set" in obj) {
    var set = obj.set;
445
    if (!IS_UNDEFINED(set) && !IS_SPEC_FUNCTION(set)) {
446 447 448 449 450 451 452 453 454 455 456 457
      throw MakeTypeError("setter_must_be_callable", [set]);
    }
    desc.setSet(set);
  }

  if (IsInconsistentDescriptor(desc)) {
    throw MakeTypeError("value_and_accessor", [obj]);
  }
  return desc;
}


458 459
// For Harmony proxies.
function ToCompletePropertyDescriptor(obj) {
460
  var desc = ToPropertyDescriptor(obj);
461
  if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) {
462
    if (!desc.hasValue()) desc.setValue(UNDEFINED);
463
    if (!desc.hasWritable()) desc.setWritable(false);
464 465
  } else {
    // Is accessor descriptor.
466 467
    if (!desc.hasGetter()) desc.setGet(UNDEFINED);
    if (!desc.hasSetter()) desc.setSet(UNDEFINED);
468
  }
469 470
  if (!desc.hasEnumerable()) desc.setEnumerable(false);
  if (!desc.hasConfigurable()) desc.setConfigurable(false);
471 472 473 474
  return desc;
}


475 476 477
function PropertyDescriptor() {
  // Initialize here so they are all in-object and have the same map.
  // Default values from ES5 8.6.1.
478
  this.value_ = UNDEFINED;
479 480 481 482
  this.hasValue_ = false;
  this.writable_ = false;
  this.hasWritable_ = false;
  this.enumerable_ = false;
483
  this.hasEnumerable_ = false;
484
  this.configurable_ = false;
485
  this.hasConfigurable_ = false;
486
  this.get_ = UNDEFINED;
487
  this.hasGetter_ = false;
488
  this.set_ = UNDEFINED;
489 490 491
  this.hasSetter_ = false;
}

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
SetUpLockedPrototype(PropertyDescriptor, $Array(
    "value_",
    "hasValue_",
    "writable_",
    "hasWritable_",
    "enumerable_",
    "hasEnumerable_",
    "configurable_",
    "hasConfigurable_",
    "get_",
    "hasGetter_",
    "set_",
    "hasSetter_"
  ), $Array(
    "toString", function() {
      return "[object PropertyDescriptor]";
    },
    "setValue", function(value) {
      this.value_ = value;
      this.hasValue_ = true;
    },
    "getValue", function() {
      return this.value_;
    },
    "hasValue", function() {
      return this.hasValue_;
    },
    "setEnumerable", function(enumerable) {
      this.enumerable_ = enumerable;
        this.hasEnumerable_ = true;
    },
    "isEnumerable", function () {
      return this.enumerable_;
    },
    "hasEnumerable", function() {
      return this.hasEnumerable_;
    },
    "setWritable", function(writable) {
      this.writable_ = writable;
      this.hasWritable_ = true;
    },
    "isWritable", function() {
      return this.writable_;
    },
    "hasWritable", function() {
      return this.hasWritable_;
    },
    "setConfigurable", function(configurable) {
      this.configurable_ = configurable;
      this.hasConfigurable_ = true;
    },
    "hasConfigurable", function() {
      return this.hasConfigurable_;
    },
    "isConfigurable", function() {
      return this.configurable_;
    },
    "setGet", function(get) {
      this.get_ = get;
        this.hasGetter_ = true;
    },
    "getGet", function() {
      return this.get_;
    },
    "hasGetter", function() {
      return this.hasGetter_;
    },
    "setSet", function(set) {
      this.set_ = set;
      this.hasSetter_ = true;
    },
    "getSet", function() {
      return this.set_;
    },
    "hasSetter", function() {
      return this.hasSetter_;
  }));
569 570


571 572 573 574
// Converts an array returned from Runtime_GetOwnProperty to an actual
// property descriptor. For a description of the array layout please
// see the runtime.cc file.
function ConvertDescriptorArrayToDescriptor(desc_array) {
575
  if (desc_array === false) {
576 577
    throw 'Internal error: invalid desc_array';
  }
578

579
  if (IS_UNDEFINED(desc_array)) {
580
    return UNDEFINED;
581
  }
582

583 584 585 586 587
  var desc = new PropertyDescriptor();
  // This is an accessor.
  if (desc_array[IS_ACCESSOR_INDEX]) {
    desc.setGet(desc_array[GETTER_INDEX]);
    desc.setSet(desc_array[SETTER_INDEX]);
588
  } else {
589 590
    desc.setValue(desc_array[VALUE_INDEX]);
    desc.setWritable(desc_array[WRITABLE_INDEX]);
591
  }
592 593
  desc.setEnumerable(desc_array[ENUMERABLE_INDEX]);
  desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]);
594 595 596 597 598

  return desc;
}


599 600 601 602 603 604 605 606
// For Harmony proxies.
function GetTrap(handler, name, defaultTrap) {
  var trap = handler[name];
  if (IS_UNDEFINED(trap)) {
    if (IS_UNDEFINED(defaultTrap)) {
      throw MakeTypeError("handler_trap_missing", [handler, name]);
    }
    trap = defaultTrap;
607
  } else if (!IS_SPEC_FUNCTION(trap)) {
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
    throw MakeTypeError("handler_trap_must_be_callable", [handler, name]);
  }
  return trap;
}


function CallTrap0(handler, name, defaultTrap) {
  return %_CallFunction(handler, GetTrap(handler, name, defaultTrap));
}


function CallTrap1(handler, name, defaultTrap, x) {
  return %_CallFunction(handler, x, GetTrap(handler, name, defaultTrap));
}


function CallTrap2(handler, name, defaultTrap, x, y) {
  return %_CallFunction(handler, x, y, GetTrap(handler, name, defaultTrap));
}


629
// ES5 section 8.12.1.
630
function GetOwnPropertyJS(obj, v) {
631
  var p = ToName(v);
632
  if (%IsJSProxy(obj)) {
633
    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
634
    if (IS_SYMBOL(v)) return UNDEFINED;
635

636
    var handler = %GetHandler(obj);
637 638
    var descriptor = CallTrap1(
                         handler, "getOwnPropertyDescriptor", UNDEFINED, p);
639 640 641 642 643 644 645 646 647
    if (IS_UNDEFINED(descriptor)) return descriptor;
    var desc = ToCompletePropertyDescriptor(descriptor);
    if (!desc.isConfigurable()) {
      throw MakeTypeError("proxy_prop_not_configurable",
                          [handler, "getOwnPropertyDescriptor", p, descriptor]);
    }
    return desc;
  }

648 649 650
  // GetOwnProperty returns an array indexed by the constants
  // defined in macros.py.
  // If p is not a property on obj undefined is returned.
651
  var props = %GetOwnProperty(ToObject(obj), p);
652 653

  // A false value here means that access checks failed.
654
  if (props === false) return UNDEFINED;
655 656 657 658 659

  return ConvertDescriptorArrayToDescriptor(props);
}


660 661
// ES5 section 8.12.7.
function Delete(obj, p, should_throw) {
662
  var desc = GetOwnPropertyJS(obj, p);
663 664 665 666 667 668 669 670 671 672 673 674
  if (IS_UNDEFINED(desc)) return true;
  if (desc.isConfigurable()) {
    %DeleteProperty(obj, p, 0);
    return true;
  } else if (should_throw) {
    throw MakeTypeError("define_disallowed", [p]);
  } else {
    return;
  }
}


675 676
// Harmony proxies.
function DefineProxyProperty(obj, p, attributes, should_throw) {
677 678 679
  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
  if (IS_SYMBOL(p)) return false;

680
  var handler = %GetHandler(obj);
681
  var result = CallTrap2(handler, "defineProperty", UNDEFINED, p, attributes);
682 683
  if (!ToBoolean(result)) {
    if (should_throw) {
684 685
      throw MakeTypeError("handler_returned_false",
                          [handler, "defineProperty"]);
686 687 688 689 690 691 692 693
    } else {
      return false;
    }
  }
  return true;
}


694
// ES5 8.12.9.
695
function DefineObjectProperty(obj, p, desc, should_throw) {
696
  var current_or_access = %GetOwnProperty(ToObject(obj), ToName(p));
697
  // A false value here means that access checks failed.
698
  if (current_or_access === false) return UNDEFINED;
699 700

  var current = ConvertDescriptorArrayToDescriptor(current_or_access);
701 702 703 704
  var extensible = %IsExtensible(ToObject(obj));

  // Error handling according to spec.
  // Step 3
705 706
  if (IS_UNDEFINED(current) && !extensible) {
    if (should_throw) {
707
      throw MakeTypeError("define_disallowed", [p]);
708
    } else {
709
      return false;
710 711
    }
  }
712

713
  if (!IS_UNDEFINED(current)) {
714
    // Step 5 and 6
715 716 717 718
    if ((IsGenericDescriptor(desc) ||
         IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
        (!desc.hasEnumerable() ||
         SameValue(desc.isEnumerable(), current.isEnumerable())) &&
719
        (!desc.hasConfigurable() ||
720
         SameValue(desc.isConfigurable(), current.isConfigurable())) &&
721
        (!desc.hasWritable() ||
722 723 724 725 726 727 728 729 730
         SameValue(desc.isWritable(), current.isWritable())) &&
        (!desc.hasValue() ||
         SameValue(desc.getValue(), current.getValue())) &&
        (!desc.hasGetter() ||
         SameValue(desc.getGet(), current.getGet())) &&
        (!desc.hasSetter() ||
         SameValue(desc.getSet(), current.getSet()))) {
      return true;
    }
731 732 733 734
    if (!current.isConfigurable()) {
      // Step 7
      if (desc.isConfigurable() ||
          (desc.hasEnumerable() &&
735
           desc.isEnumerable() != current.isEnumerable())) {
736
        if (should_throw) {
737
          throw MakeTypeError("redefine_disallowed", [p]);
738
        } else {
739
          return false;
740
        }
741
      }
742 743 744
      // Step 8
      if (!IsGenericDescriptor(desc)) {
        // Step 9a
745
        if (IsDataDescriptor(current) != IsDataDescriptor(desc)) {
746
          if (should_throw) {
747
            throw MakeTypeError("redefine_disallowed", [p]);
748
          } else {
749
            return false;
750
          }
751
        }
752 753
        // Step 10a
        if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
754
          if (!current.isWritable() && desc.isWritable()) {
755
            if (should_throw) {
756
              throw MakeTypeError("redefine_disallowed", [p]);
757
            } else {
758
              return false;
759
            }
760
          }
761 762
          if (!current.isWritable() && desc.hasValue() &&
              !SameValue(desc.getValue(), current.getValue())) {
763
            if (should_throw) {
764
              throw MakeTypeError("redefine_disallowed", [p]);
765
            } else {
766
              return false;
767
            }
768 769 770 771
          }
        }
        // Step 11
        if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
772
          if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) {
773
            if (should_throw) {
774
              throw MakeTypeError("redefine_disallowed", [p]);
775
            } else {
776
              return false;
777
            }
778
          }
779
          if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) {
780
            if (should_throw) {
781
              throw MakeTypeError("redefine_disallowed", [p]);
782
            } else {
783
              return false;
784
            }
785
          }
786
        }
787 788 789 790
      }
    }
  }

791
  // Send flags - enumerable and configurable are common - writable is
792 793 794 795 796 797 798 799
  // only send to the data descriptor.
  // Take special care if enumerable and configurable is not defined on
  // desc (we need to preserve the existing values from current).
  var flag = NONE;
  if (desc.hasEnumerable()) {
    flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
  } else if (!IS_UNDEFINED(current)) {
    flag |= current.isEnumerable() ? 0 : DONT_ENUM;
800
  } else {
801 802 803 804 805 806 807 808 809 810
    flag |= DONT_ENUM;
  }

  if (desc.hasConfigurable()) {
    flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
  } else if (!IS_UNDEFINED(current)) {
    flag |= current.isConfigurable() ? 0 : DONT_DELETE;
  } else
    flag |= DONT_DELETE;

811 812 813 814 815 816 817 818 819 820
  if (IsDataDescriptor(desc) ||
      (IsGenericDescriptor(desc) &&
       (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
    // There are 3 cases that lead here:
    // Step 4a - defining a new data property.
    // Steps 9b & 12 - replacing an existing accessor property with a data
    //                 property.
    // Step 12 - updating an existing data property with a data or generic
    //           descriptor.

821 822 823 824 825 826 827
    if (desc.hasWritable()) {
      flag |= desc.isWritable() ? 0 : READ_ONLY;
    } else if (!IS_UNDEFINED(current)) {
      flag |= current.isWritable() ? 0 : READ_ONLY;
    } else {
      flag |= READ_ONLY;
    }
828

829
    var value = UNDEFINED;  // Default value is undefined.
lrn@chromium.org's avatar
lrn@chromium.org committed
830 831
    if (desc.hasValue()) {
      value = desc.getValue();
832
    } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
lrn@chromium.org's avatar
lrn@chromium.org committed
833 834
      value = current.getValue();
    }
835

lrn@chromium.org's avatar
lrn@chromium.org committed
836
    %DefineOrRedefineDataProperty(obj, p, value, flag);
837
  } else {
838 839 840 841 842 843
    // There are 3 cases that lead here:
    // Step 4b - defining a new accessor property.
    // Steps 9c & 12 - replacing an existing data property with an accessor
    //                 property.
    // Step 12 - updating an existing accessor property with an accessor
    //           descriptor.
844 845 846
    var getter = desc.hasGetter() ? desc.getGet() : null;
    var setter = desc.hasSetter() ? desc.getSet() : null;
    %DefineOrRedefineAccessorProperty(obj, p, getter, setter, flag);
847 848 849 850 851
  }
  return true;
}


852 853 854 855 856 857 858
// ES5 section 15.4.5.1.
function DefineArrayProperty(obj, p, desc, should_throw) {
  // Note that the length of an array is not actually stored as part of the
  // property, hence we use generated code throughout this function instead of
  // DefineObjectProperty() to modify its value.

  // Step 3 - Special handling for length property.
859
  if (p === "length") {
860
    var length = obj.length;
861
    var old_length = length;
862 863 864 865 866 867 868
    if (!desc.hasValue()) {
      return DefineObjectProperty(obj, "length", desc, should_throw);
    }
    var new_length = ToUint32(desc.getValue());
    if (new_length != ToNumber(desc.getValue())) {
      throw new $RangeError('defineProperty() array length out of range');
    }
869
    var length_desc = GetOwnPropertyJS(obj, "length");
870 871 872 873 874 875 876 877
    if (new_length != length && !length_desc.isWritable()) {
      if (should_throw) {
        throw MakeTypeError("redefine_disallowed", [p]);
      } else {
        return false;
      }
    }
    var threw = false;
878 879 880 881 882 883 884 885 886 887

    var emit_splice = %IsObserved(obj) && new_length !== old_length;
    var removed;
    if (emit_splice) {
      BeginPerformSplice(obj);
      removed = [];
      if (new_length < old_length)
        removed.length = old_length - new_length;
    }

888
    while (new_length < length--) {
889 890
      var index = ToString(length);
      if (emit_splice) {
891
        var deletedDesc = GetOwnPropertyJS(obj, index);
892 893 894 895
        if (deletedDesc && deletedDesc.hasValue())
          removed[length - new_length] = deletedDesc.getValue();
      }
      if (!Delete(obj, index, false)) {
896 897 898 899 900
        new_length = length + 1;
        threw = true;
        break;
      }
    }
901 902
    // Make sure the below call to DefineObjectProperty() doesn't overwrite
    // any magic "length" property by removing the value.
903 904 905 906
    // TODO(mstarzinger): This hack should be removed once we have addressed the
    // respective TODO in Runtime_DefineOrRedefineDataProperty.
    // For the time being, we need a hack to prevent Object.observe from
    // generating two change records.
907
    obj.length = new_length;
908
    desc.value_ = UNDEFINED;
909
    desc.hasValue_ = false;
910
    threw = !DefineObjectProperty(obj, "length", desc, should_throw) || threw;
911 912 913 914 915 916 917
    if (emit_splice) {
      EndPerformSplice(obj);
      EnqueueSpliceRecord(obj,
          new_length < old_length ? new_length : old_length,
          removed,
          new_length > old_length ? new_length - old_length : 0);
    }
918
    if (threw) {
919 920 921 922 923 924 925 926 927 928 929
      if (should_throw) {
        throw MakeTypeError("redefine_disallowed", [p]);
      } else {
        return false;
      }
    }
    return true;
  }

  // Step 4 - Special handling for array index.
  var index = ToUint32(p);
930
  var emit_splice = false;
931
  if (ToString(index) == p && index != 4294967295) {
932
    var length = obj.length;
933 934 935 936 937
    if (index >= length && %IsObserved(obj)) {
      emit_splice = true;
      BeginPerformSplice(obj);
    }

938
    var length_desc = GetOwnPropertyJS(obj, "length");
939 940
    if ((index >= length && !length_desc.isWritable()) ||
        !DefineObjectProperty(obj, p, desc, true)) {
941 942
      if (emit_splice)
        EndPerformSplice(obj);
943 944 945 946 947 948 949 950 951
      if (should_throw) {
        throw MakeTypeError("define_disallowed", [p]);
      } else {
        return false;
      }
    }
    if (index >= length) {
      obj.length = index + 1;
    }
952 953
    if (emit_splice) {
      EndPerformSplice(obj);
954
      EnqueueSpliceRecord(obj, length, [], index + 1 - length);
955
    }
956 957 958 959 960 961 962 963 964 965 966
    return true;
  }

  // Step 5 - Fallback to default implementation.
  return DefineObjectProperty(obj, p, desc, should_throw);
}


// ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies.
function DefineOwnProperty(obj, p, desc, should_throw) {
  if (%IsJSProxy(obj)) {
967 968 969
    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
    if (IS_SYMBOL(p)) return false;

970 971 972 973 974 975 976 977 978 979
    var attributes = FromGenericPropertyDescriptor(desc);
    return DefineProxyProperty(obj, p, attributes, should_throw);
  } else if (IS_ARRAY(obj)) {
    return DefineArrayProperty(obj, p, desc, should_throw);
  } else {
    return DefineObjectProperty(obj, p, desc, should_throw);
  }
}


980 981
// ES5 section 15.2.3.2.
function ObjectGetPrototypeOf(obj) {
982
  if (!IS_SPEC_OBJECT(obj)) {
983
    throw MakeTypeError("called_on_non_object", ["Object.getPrototypeOf"]);
984
  }
985
  return %GetPrototype(obj);
986 987
}

988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
// ES6 section 19.1.2.19.
function ObjectSetPrototypeOf(obj, proto) {
  CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");

  if (proto !== null && !IS_SPEC_OBJECT(proto)) {
    throw MakeTypeError("proto_object_or_null", [proto]);
  }

  if (IS_SPEC_OBJECT(obj)) {
    %SetPrototype(obj, proto);
  }

  return obj;
}

1003

1004
// ES5 section 15.2.3.3
1005
function ObjectGetOwnPropertyDescriptor(obj, p) {
1006
  if (!IS_SPEC_OBJECT(obj)) {
1007 1008
    throw MakeTypeError("called_on_non_object",
                        ["Object.getOwnPropertyDescriptor"]);
1009
  }
1010
  var desc = GetOwnPropertyJS(obj, p);
1011 1012 1013 1014
  return FromPropertyDescriptor(desc);
}


1015
// For Harmony proxies
1016
function ToNameArray(obj, trap, includeSymbols) {
1017 1018 1019 1020 1021
  if (!IS_SPEC_OBJECT(obj)) {
    throw MakeTypeError("proxy_non_object_prop_names", [obj, trap]);
  }
  var n = ToUint32(obj.length);
  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 (%HasOwnProperty(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 1037 1038 1039
  return array;
}


1040
function ObjectGetOwnPropertyKeys(obj, symbolsOnly) {
1041
  var nameArrays = new InternalArray();
1042 1043 1044
  var filter = symbolsOnly ?
      PROPERTY_ATTRIBUTES_STRING | PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL :
      PROPERTY_ATTRIBUTES_SYMBOLIC;
1045

1046 1047
  // Find all the indexed properties.

1048
  // Only get own element names if we want to include string keys.
1049
  if (!symbolsOnly) {
1050 1051 1052
    var ownElementNames = %GetOwnElementNames(obj);
    for (var i = 0; i < ownElementNames.length; ++i) {
      ownElementNames[i] = %_NumberToString(ownElementNames[i]);
1053
    }
1054
    nameArrays.push(ownElementNames);
1055 1056 1057 1058 1059 1060 1061 1062

    // Get names for indexed interceptor properties.
    var interceptorInfo = %GetInterceptorInfo(obj);
    if ((interceptorInfo & 1) != 0) {
      var indexedInterceptorNames = %GetIndexedInterceptorElementNames(obj);
      if (!IS_UNDEFINED(indexedInterceptorNames)) {
        nameArrays.push(indexedInterceptorNames);
      }
1063
    }
1064 1065 1066 1067
  }

  // Find all the named properties.

1068 1069
  // Get own property names.
  nameArrays.push(%GetOwnPropertyNames(obj, filter));
1070 1071

  // Get names for named interceptor properties if any.
1072
  if ((interceptorInfo & 2) != 0) {
1073 1074
    var namedInterceptorNames =
        %GetNamedInterceptorPropertyNames(obj);
1075 1076
    if (!IS_UNDEFINED(namedInterceptorNames)) {
      nameArrays.push(namedInterceptorNames);
1077 1078 1079
    }
  }

1080 1081 1082 1083 1084
  var propertyNames =
      %Apply(InternalArray.prototype.concat,
             nameArrays[0], nameArrays, 1, nameArrays.length - 1);

  // Property names are expected to be unique strings,
1085 1086
  // but interceptors can interfere with that assumption.
  if (interceptorInfo != 0) {
1087
    var seenKeys = { __proto__: null };
1088 1089
    var j = 0;
    for (var i = 0; i < propertyNames.length; ++i) {
1090 1091 1092 1093 1094 1095
      var name = propertyNames[i];
      if (symbolsOnly) {
        if (!IS_SYMBOL(name) || IS_PRIVATE(name)) continue;
      } else {
        if (IS_SYMBOL(name)) continue;
        name = ToString(name);
1096
      }
1097 1098
      if (seenKeys[name]) continue;
      seenKeys[name] = true;
1099
      propertyNames[j++] = name;
1100
    }
1101
    propertyNames.length = j;
1102
  }
1103

1104 1105 1106 1107
  return propertyNames;
}


1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
// ES5 section 15.2.3.4.
function ObjectGetOwnPropertyNames(obj) {
  if (!IS_SPEC_OBJECT(obj)) {
    throw MakeTypeError("called_on_non_object", ["Object.getOwnPropertyNames"]);
  }
  // Special handling for proxies.
  if (%IsJSProxy(obj)) {
    var handler = %GetHandler(obj);
    var names = CallTrap0(handler, "getOwnPropertyNames", UNDEFINED);
    return ToNameArray(names, "getOwnPropertyNames", false);
  }

  return ObjectGetOwnPropertyKeys(obj, false);
}


1124 1125
// ES5 section 15.2.3.5.
function ObjectCreate(proto, properties) {
1126
  if (!IS_SPEC_OBJECT(proto) && proto !== null) {
1127 1128
    throw MakeTypeError("proto_object_or_null", [proto]);
  }
1129
  var obj = { __proto__: proto };
1130 1131 1132 1133 1134
  if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
  return obj;
}


1135 1136
// ES5 section 15.2.3.6.
function ObjectDefineProperty(obj, p, attributes) {
1137
  if (!IS_SPEC_OBJECT(obj)) {
1138
    throw MakeTypeError("called_on_non_object", ["Object.defineProperty"]);
1139
  }
1140
  var name = ToName(p);
1141 1142 1143 1144
  if (%IsJSProxy(obj)) {
    // Clone the attributes object for protection.
    // TODO(rossberg): not spec'ed yet, so not sure if this should involve
    // non-own properties as it does (or non-enumerable ones, as it doesn't?).
1145
    var attributesClone = { __proto__: null };
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
    for (var a in attributes) {
      attributesClone[a] = attributes[a];
    }
    DefineProxyProperty(obj, name, attributesClone, true);
    // The following would implement the spec as in the current proposal,
    // but after recent comments on es-discuss, is most likely obsolete.
    /*
    var defineObj = FromGenericPropertyDescriptor(desc);
    var names = ObjectGetOwnPropertyNames(attributes);
    var standardNames =
      {value: 0, writable: 0, get: 0, set: 0, enumerable: 0, configurable: 0};
    for (var i = 0; i < names.length; i++) {
      var N = names[i];
1159
      if (!(%HasOwnProperty(standardNames, N))) {
1160
        var attr = GetOwnPropertyJS(attributes, N);
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
        DefineOwnProperty(descObj, N, attr, true);
      }
    }
    // This is really confusing the types, but it is what the proxies spec
    // currently requires:
    desc = descObj;
    */
  } else {
    var desc = ToPropertyDescriptor(attributes);
    DefineOwnProperty(obj, name, desc, true);
  }
1172 1173 1174 1175
  return obj;
}


1176
function GetOwnEnumerablePropertyNames(properties) {
1177
  var names = new InternalArray();
1178
  for (var key in properties) {
1179
    if (%HasOwnProperty(properties, key)) {
1180 1181 1182 1183 1184 1185 1186
      names.push(key);
    }
  }
  return names;
}


1187
// ES5 section 15.2.3.7.
1188
function ObjectDefineProperties(obj, properties) {
1189
  if (!IS_SPEC_OBJECT(obj)) {
1190
    throw MakeTypeError("called_on_non_object", ["Object.defineProperties"]);
1191
  }
1192
  var props = ToObject(properties);
1193
  var names = GetOwnEnumerablePropertyNames(props);
1194
  var descriptors = new InternalArray();
1195
  for (var i = 0; i < names.length; i++) {
1196 1197 1198 1199
    descriptors.push(ToPropertyDescriptor(props[names[i]]));
  }
  for (var i = 0; i < names.length; i++) {
    DefineOwnProperty(obj, names[i], descriptors[i], true);
1200
  }
1201
  return obj;
1202 1203 1204
}


1205 1206 1207
// Harmony proxies.
function ProxyFix(obj) {
  var handler = %GetHandler(obj);
1208
  var props = CallTrap0(handler, "fix", UNDEFINED);
1209 1210 1211
  if (IS_UNDEFINED(props)) {
    throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
  }
1212

1213
  if (%IsJSFunctionProxy(obj)) {
1214 1215 1216 1217 1218
    var callTrap = %GetCallTrap(obj);
    var constructTrap = %GetConstructTrap(obj);
    var code = DelegateCallAndConstruct(callTrap, constructTrap);
    %Fix(obj);  // becomes a regular function
    %SetCode(obj, code);
1219 1220 1221 1222
    // TODO(rossberg): What about length and other properties? Not specified.
    // We just put in some half-reasonable defaults for now.
    var prototype = new $Object();
    $Object.defineProperty(prototype, "constructor",
1223 1224 1225 1226
      {value: obj, writable: true, enumerable: false, configurable: true});
    // TODO(v8:1530): defineProperty does not handle prototype and length.
    %FunctionSetPrototype(obj, prototype);
    obj.length = 0;
1227 1228 1229
  } else {
    %Fix(obj);
  }
1230 1231 1232 1233
  ObjectDefineProperties(obj, props);
}


1234 1235
// ES5 section 15.2.3.8.
function ObjectSeal(obj) {
1236
  if (!IS_SPEC_OBJECT(obj)) {
1237
    throw MakeTypeError("called_on_non_object", ["Object.seal"]);
1238
  }
1239 1240 1241
  if (%IsJSProxy(obj)) {
    ProxyFix(obj);
  }
1242
  var names = ObjectGetOwnPropertyNames(obj);
1243 1244
  for (var i = 0; i < names.length; i++) {
    var name = names[i];
1245
    var desc = GetOwnPropertyJS(obj, name);
1246 1247 1248 1249
    if (desc.isConfigurable()) {
      desc.setConfigurable(false);
      DefineOwnProperty(obj, name, desc, true);
    }
1250
  }
1251 1252
  %PreventExtensions(obj);
  return obj;
1253 1254 1255
}


1256
// ES5 section 15.2.3.9.
1257
function ObjectFreezeJS(obj) {
1258
  if (!IS_SPEC_OBJECT(obj)) {
1259
    throw MakeTypeError("called_on_non_object", ["Object.freeze"]);
1260
  }
1261
  var isProxy = %IsJSProxy(obj);
1262
  if (isProxy || %HasSloppyArgumentsElements(obj) || %IsObserved(obj)) {
1263 1264
    if (isProxy) {
      ProxyFix(obj);
1265
    }
1266 1267 1268
    var names = ObjectGetOwnPropertyNames(obj);
    for (var i = 0; i < names.length; i++) {
      var name = names[i];
1269
      var desc = GetOwnPropertyJS(obj, name);
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
      if (desc.isWritable() || desc.isConfigurable()) {
        if (IsDataDescriptor(desc)) desc.setWritable(false);
        desc.setConfigurable(false);
        DefineOwnProperty(obj, name, desc, true);
      }
    }
    %PreventExtensions(obj);
  } else {
    // TODO(adamk): Is it worth going to this fast path if the
    // object's properties are already in dictionary mode?
    %ObjectFreeze(obj);
1281
  }
1282
  return obj;
1283 1284 1285
}


1286 1287
// ES5 section 15.2.3.10
function ObjectPreventExtension(obj) {
1288
  if (!IS_SPEC_OBJECT(obj)) {
1289
    throw MakeTypeError("called_on_non_object", ["Object.preventExtension"]);
1290
  }
1291 1292 1293
  if (%IsJSProxy(obj)) {
    ProxyFix(obj);
  }
1294 1295 1296 1297 1298
  %PreventExtensions(obj);
  return obj;
}


1299 1300
// ES5 section 15.2.3.11
function ObjectIsSealed(obj) {
1301
  if (!IS_SPEC_OBJECT(obj)) {
1302
    throw MakeTypeError("called_on_non_object", ["Object.isSealed"]);
1303
  }
1304 1305 1306
  if (%IsJSProxy(obj)) {
    return false;
  }
1307 1308 1309
  if (%IsExtensible(obj)) {
    return false;
  }
1310
  var names = ObjectGetOwnPropertyNames(obj);
1311 1312
  for (var i = 0; i < names.length; i++) {
    var name = names[i];
1313
    var desc = GetOwnPropertyJS(obj, name);
1314
    if (desc.isConfigurable()) return false;
1315
  }
1316
  return true;
1317 1318 1319
}


1320 1321
// ES5 section 15.2.3.12
function ObjectIsFrozen(obj) {
1322
  if (!IS_SPEC_OBJECT(obj)) {
1323
    throw MakeTypeError("called_on_non_object", ["Object.isFrozen"]);
1324
  }
1325 1326 1327
  if (%IsJSProxy(obj)) {
    return false;
  }
1328 1329 1330
  if (%IsExtensible(obj)) {
    return false;
  }
1331
  var names = ObjectGetOwnPropertyNames(obj);
1332 1333
  for (var i = 0; i < names.length; i++) {
    var name = names[i];
1334
    var desc = GetOwnPropertyJS(obj, name);
1335 1336
    if (IsDataDescriptor(desc) && desc.isWritable()) return false;
    if (desc.isConfigurable()) return false;
1337
  }
1338
  return true;
1339 1340 1341
}


1342 1343
// ES5 section 15.2.3.13
function ObjectIsExtensible(obj) {
1344
  if (!IS_SPEC_OBJECT(obj)) {
1345
    throw MakeTypeError("called_on_non_object", ["Object.isExtensible"]);
1346
  }
1347 1348 1349
  if (%IsJSProxy(obj)) {
    return true;
  }
1350 1351 1352 1353
  return %IsExtensible(obj);
}


1354 1355 1356
// Harmony egal.
function ObjectIs(obj1, obj2) {
  if (obj1 === obj2) {
1357
    return (obj1 !== 0) || (1 / obj1 === 1 / obj2);
1358 1359 1360 1361 1362 1363
  } else {
    return (obj1 !== obj1) && (obj2 !== obj2);
  }
}


1364
// ECMA-262, Edition 6, section B.2.2.1.1
1365
function ObjectGetProto() {
1366
  return %GetPrototype(ToObject(this));
1367 1368 1369
}


1370 1371 1372 1373
// ECMA-262, Edition 6, section B.2.2.1.2
function ObjectSetProto(proto) {
  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.__proto__");

1374
  if ((IS_SPEC_OBJECT(proto) || IS_NULL(proto)) && IS_SPEC_OBJECT(this)) {
1375 1376
    %SetPrototype(this, proto);
  }
1377 1378 1379
}


1380
function ObjectConstructor(x) {
1381
  if (%_IsConstructCall()) {
1382 1383 1384 1385 1386 1387
    if (x == null) return this;
    return ToObject(x);
  } else {
    if (x == null) return { };
    return ToObject(x);
  }
1388
}
1389 1390 1391


// ----------------------------------------------------------------------------
1392
// Object
1393

1394 1395
function SetUpObject() {
  %CheckIsBootstrapping();
1396

1397
  %SetNativeFlag($Object);
1398
  %SetCode($Object, ObjectConstructor);
1399

1400 1401
  %SetProperty($Object.prototype, "constructor", $Object, DONT_ENUM);

1402
  // Set up non-enumerable functions on the Object.prototype object.
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414
  InstallFunctions($Object.prototype, DONT_ENUM, $Array(
    "toString", ObjectToString,
    "toLocaleString", ObjectToLocaleString,
    "valueOf", ObjectValueOf,
    "hasOwnProperty", ObjectHasOwnProperty,
    "isPrototypeOf", ObjectIsPrototypeOf,
    "propertyIsEnumerable", ObjectPropertyIsEnumerable,
    "__defineGetter__", ObjectDefineGetter,
    "__lookupGetter__", ObjectLookupGetter,
    "__defineSetter__", ObjectDefineSetter,
    "__lookupSetter__", ObjectLookupSetter
  ));
1415 1416 1417 1418
  InstallGetterSetter($Object.prototype, "__proto__",
                      ObjectGetProto, ObjectSetProto);

  // Set up non-enumerable functions in the Object object.
1419
  InstallFunctions($Object, DONT_ENUM, $Array(
1420
    "keys", ObjectKeys,
1421
    "create", ObjectCreate,
1422 1423
    "defineProperty", ObjectDefineProperty,
    "defineProperties", ObjectDefineProperties,
1424
    "freeze", ObjectFreezeJS,
1425
    "getPrototypeOf", ObjectGetPrototypeOf,
1426
    "setPrototypeOf", ObjectSetPrototypeOf,
1427
    "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
1428
    "getOwnPropertyNames", ObjectGetOwnPropertyNames,
1429
    // getOwnPropertySymbols is added in symbol.js.
1430
    "is", ObjectIs,
1431
    "isExtensible", ObjectIsExtensible,
1432
    "isFrozen", ObjectIsFrozen,
1433 1434 1435
    "isSealed", ObjectIsSealed,
    "preventExtensions", ObjectPreventExtension,
    "seal", ObjectSeal
1436 1437
    // deliverChangeRecords, getNotifier, observe and unobserve are added
    // in object-observe.js.
1438
  ));
1439
}
1440

1441
SetUpObject();
1442

1443

1444 1445 1446
// ----------------------------------------------------------------------------
// Boolean

1447 1448 1449 1450 1451 1452 1453 1454 1455
function BooleanConstructor(x) {
  if (%_IsConstructCall()) {
    %_SetValueOf(this, ToBoolean(x));
  } else {
    return ToBoolean(x);
  }
}


1456
function BooleanToString() {
1457 1458
  // NOTE: Both Boolean objects and values can enter here as
  // 'this'. This is not as dictated by ECMA-262.
1459 1460 1461 1462 1463 1464 1465 1466
  var b = this;
  if (!IS_BOOLEAN(b)) {
    if (!IS_BOOLEAN_WRAPPER(b)) {
      throw new $TypeError('Boolean.prototype.toString is not generic');
    }
    b = %_ValueOf(b);
  }
  return b ? 'true' : 'false';
1467
}
1468 1469


1470
function BooleanValueOf() {
1471 1472
  // NOTE: Both Boolean objects and values can enter here as
  // 'this'. This is not as dictated by ECMA-262.
1473
  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) {
1474
    throw new $TypeError('Boolean.prototype.valueOf is not generic');
1475
  }
1476
  return %_ValueOf(this);
1477 1478 1479 1480
}


// ----------------------------------------------------------------------------
1481

1482 1483
function SetUpBoolean () {
  %CheckIsBootstrapping();
1484 1485 1486 1487 1488

  %SetCode($Boolean, BooleanConstructor);
  %FunctionSetPrototype($Boolean, new $Boolean(false));
  %SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM);

1489 1490
  InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
    "toString", BooleanToString,
1491
    "valueOf", BooleanValueOf
1492 1493 1494
  ));
}

1495 1496
SetUpBoolean();

1497

1498 1499 1500
// ----------------------------------------------------------------------------
// Number

1501
function NumberConstructor(x) {
1502
  var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
1503
  if (%_IsConstructCall()) {
1504 1505 1506 1507
    %_SetValueOf(this, value);
  } else {
    return value;
  }
1508
}
1509 1510 1511


// ECMA-262 section 15.7.4.2.
1512
function NumberToString(radix) {
1513 1514 1515 1516
  // NOTE: Both Number objects and values can enter here as
  // 'this'. This is not as dictated by ECMA-262.
  var number = this;
  if (!IS_NUMBER(this)) {
1517
    if (!IS_NUMBER_WRAPPER(this)) {
1518
      throw new $TypeError('Number.prototype.toString is not generic');
1519
    }
1520 1521 1522 1523 1524
    // Get the value of this number in case it's an object.
    number = %_ValueOf(this);
  }
  // Fast case: Convert number in radix 10.
  if (IS_UNDEFINED(radix) || radix === 10) {
1525
    return %_NumberToString(number);
1526 1527 1528 1529 1530 1531 1532 1533 1534
  }

  // Convert the radix to an integer and check the range.
  radix = TO_INTEGER(radix);
  if (radix < 2 || radix > 36) {
    throw new $RangeError('toString() radix argument must be between 2 and 36');
  }
  // Convert the number to a string in the given radix.
  return %NumberToRadixString(number, radix);
1535
}
1536 1537 1538


// ECMA-262 section 15.7.4.3
1539
function NumberToLocaleString() {
1540
  return %_CallFunction(this, NumberToString);
1541
}
1542 1543 1544


// ECMA-262 section 15.7.4.4
1545
function NumberValueOf() {
1546 1547
  // NOTE: Both Number objects and values can enter here as
  // 'this'. This is not as dictated by ECMA-262.
1548
  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) {
1549
    throw new $TypeError('Number.prototype.valueOf is not generic');
1550
  }
1551
  return %_ValueOf(this);
1552
}
1553 1554 1555


// ECMA-262 section 15.7.4.5
1556
function NumberToFixedJS(fractionDigits) {
1557 1558 1559 1560 1561 1562 1563 1564 1565
  var x = this;
  if (!IS_NUMBER(this)) {
    if (!IS_NUMBER_WRAPPER(this)) {
      throw MakeTypeError("incompatible_method_receiver",
                          ["Number.prototype.toFixed", this]);
    }
    // Get the value of this number in case it's an object.
    x = %_ValueOf(this);
  }
1566
  var f = TO_INTEGER(fractionDigits);
1567

1568 1569 1570
  if (f < 0 || f > 20) {
    throw new $RangeError("toFixed() digits argument must be between 0 and 20");
  }
1571 1572

  if (NUMBER_IS_NAN(x)) return "NaN";
1573 1574
  if (x == INFINITY) return "Infinity";
  if (x == -INFINITY) return "-Infinity";
1575

1576
  return %NumberToFixed(x, f);
1577
}
1578 1579 1580


// ECMA-262 section 15.7.4.6
1581
function NumberToExponentialJS(fractionDigits) {
1582 1583 1584 1585 1586
  var x = this;
  if (!IS_NUMBER(this)) {
    if (!IS_NUMBER_WRAPPER(this)) {
      throw MakeTypeError("incompatible_method_receiver",
                          ["Number.prototype.toExponential", this]);
1587
    }
1588 1589
    // Get the value of this number in case it's an object.
    x = %_ValueOf(this);
1590
  }
1591
  var f = IS_UNDEFINED(fractionDigits) ? UNDEFINED : TO_INTEGER(fractionDigits);
1592 1593

  if (NUMBER_IS_NAN(x)) return "NaN";
1594 1595
  if (x == INFINITY) return "Infinity";
  if (x == -INFINITY) return "-Infinity";
1596 1597 1598 1599 1600

  if (IS_UNDEFINED(f)) {
    f = -1;  // Signal for runtime function that f is not defined.
  } else if (f < 0 || f > 20) {
    throw new $RangeError("toExponential() argument must be between 0 and 20");
1601
  }
1602
  return %NumberToExponential(x, f);
1603
}
1604 1605 1606


// ECMA-262 section 15.7.4.7
1607
function NumberToPrecisionJS(precision) {
1608 1609 1610 1611 1612 1613 1614 1615
  var x = this;
  if (!IS_NUMBER(this)) {
    if (!IS_NUMBER_WRAPPER(this)) {
      throw MakeTypeError("incompatible_method_receiver",
                          ["Number.prototype.toPrecision", this]);
    }
    // Get the value of this number in case it's an object.
    x = %_ValueOf(this);
1616
  }
1617 1618
  if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
  var p = TO_INTEGER(precision);
1619 1620

  if (NUMBER_IS_NAN(x)) return "NaN";
1621 1622
  if (x == INFINITY) return "Infinity";
  if (x == -INFINITY) return "-Infinity";
1623

1624 1625 1626 1627
  if (p < 1 || p > 21) {
    throw new $RangeError("toPrecision() argument must be between 1 and 21");
  }
  return %NumberToPrecision(x, p);
1628 1629 1630
}


1631 1632 1633 1634 1635 1636
// Harmony isFinite.
function NumberIsFinite(number) {
  return IS_NUMBER(number) && NUMBER_IS_FINITE(number);
}


1637 1638 1639 1640 1641 1642
// Harmony isInteger
function NumberIsInteger(number) {
  return NumberIsFinite(number) && TO_INTEGER(number) == number;
}


1643 1644 1645 1646 1647 1648
// Harmony isNaN.
function NumberIsNaN(number) {
  return IS_NUMBER(number) && NUMBER_IS_NAN(number);
}


1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659
// Harmony isSafeInteger
function NumberIsSafeInteger(number) {
  if (NumberIsFinite(number)) {
    var integral = TO_INTEGER(number);
    if (integral == number)
      return MathAbs(integral) <= $Number.MAX_SAFE_INTEGER;
  }
  return false;
}


1660 1661
// ----------------------------------------------------------------------------

1662 1663
function SetUpNumber() {
  %CheckIsBootstrapping();
1664 1665 1666 1667

  %SetCode($Number, NumberConstructor);
  %FunctionSetPrototype($Number, new $Number(0));

1668
  %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
1669
  // Set up the constructor property on the Number prototype object.
1670 1671
  %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);

1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689
  InstallConstants($Number, $Array(
      // ECMA-262 section 15.7.3.1.
      "MAX_VALUE", 1.7976931348623157e+308,
      // ECMA-262 section 15.7.3.2.
      "MIN_VALUE", 5e-324,
      // ECMA-262 section 15.7.3.3.
      "NaN", NAN,
      // ECMA-262 section 15.7.3.4.
      "NEGATIVE_INFINITY", -INFINITY,
      // ECMA-262 section 15.7.3.5.
      "POSITIVE_INFINITY", INFINITY,

      // --- Harmony constants (no spec refs until settled.)

      "MAX_SAFE_INTEGER", %_MathPow(2, 53) - 1,
      "MIN_SAFE_INTEGER", -%_MathPow(2, 53) + 1,
      "EPSILON", %_MathPow(2, -52)
  ));
1690

1691
  // Set up non-enumerable functions on the Number prototype object.
1692 1693 1694 1695
  InstallFunctions($Number.prototype, DONT_ENUM, $Array(
    "toString", NumberToString,
    "toLocaleString", NumberToLocaleString,
    "valueOf", NumberValueOf,
1696 1697 1698
    "toFixed", NumberToFixedJS,
    "toExponential", NumberToExponentialJS,
    "toPrecision", NumberToPrecisionJS
1699
  ));
1700 1701

  // Harmony Number constructor additions
1702 1703
  InstallFunctions($Number, DONT_ENUM, $Array(
    "isFinite", NumberIsFinite,
1704 1705 1706 1707 1708
    "isInteger", NumberIsInteger,
    "isNaN", NumberIsNaN,
    "isSafeInteger", NumberIsSafeInteger,
    "parseInt", GlobalParseInt,
    "parseFloat", GlobalParseFloat
1709
  ));
1710 1711
}

1712
SetUpNumber();
1713

1714 1715 1716 1717 1718

// ----------------------------------------------------------------------------
// Function

function FunctionSourceString(func) {
1719 1720 1721 1722
  while (%IsJSFunctionProxy(func)) {
    func = %GetCallTrap(func);
  }

1723
  if (!IS_FUNCTION(func)) {
1724
    throw new $TypeError('Function.prototype.toString is not generic');
1725
  }
1726 1727

  var source = %FunctionGetSourceCode(func);
1728
  if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
1729 1730 1731 1732 1733 1734 1735 1736 1737
    var name = %FunctionGetName(func);
    if (name) {
      // Mimic what KJS does.
      return 'function ' + name + '() { [native code] }';
    } else {
      return 'function () { [native code] }';
    }
  }

1738 1739 1740
  var name = %FunctionNameShouldPrintAsAnonymous(func)
      ? 'anonymous'
      : %FunctionGetName(func);
1741 1742
  var head = %FunctionIsGenerator(func) ? 'function* ' : 'function ';
  return head + name + source;
1743
}
1744 1745


1746
function FunctionToString() {
1747
  return FunctionSourceString(this);
1748
}
1749 1750


1751 1752
// ES5 15.3.4.5
function FunctionBind(this_arg) { // Length is 1.
1753
  if (!IS_SPEC_FUNCTION(this)) {
1754 1755 1756
    throw new $TypeError('Bind must be called on a function');
  }
  var boundFunction = function () {
1757 1758
    // Poison .arguments and .caller, but is otherwise not detectable.
    "use strict";
1759 1760 1761 1762
    // This function must not use any object literals (Object, Array, RegExp),
    // since the literals-array is being used to store the bound data.
    if (%_IsConstructCall()) {
      return %NewObjectFromBound(boundFunction);
1763
    }
1764
    var bindings = %BoundFunctionGetBindings(boundFunction);
1765

1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784
    var argc = %_ArgumentsLength();
    if (argc == 0) {
      return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
    }
    if (bindings.length === 2) {
      return %Apply(bindings[0], bindings[1], arguments, 0, argc);
    }
    var bound_argc = bindings.length - 2;
    var argv = new InternalArray(bound_argc + argc);
    for (var i = 0; i < bound_argc; i++) {
      argv[i] = bindings[i + 2];
    }
    for (var j = 0; j < argc; j++) {
      argv[i++] = %_Arguments(j);
    }
    return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
  };

  var new_length = 0;
1785 1786 1787 1788 1789 1790 1791 1792
  var old_length = this.length;
  // FunctionProxies might provide a non-UInt32 value. If so, ignore it.
  if ((typeof old_length === "number") &&
      ((old_length >>> 0) === old_length)) {
    var argc = %_ArgumentsLength();
    if (argc > 0) argc--;  // Don't count the thisArg as parameter.
    new_length = old_length - argc;
    if (new_length < 0) new_length = 0;
1793
  }
1794 1795
  // This runtime function finds any remaining arguments on the stack,
  // so we don't pass the arguments object.
1796 1797
  var result = %FunctionBindArguments(boundFunction, this,
                                      this_arg, new_length);
1798 1799 1800 1801 1802 1803

  // We already have caller and arguments properties on functions,
  // which are non-configurable. It therefore makes no sence to
  // try to redefine these as defined by the spec. The spec says
  // that bind should make these throw a TypeError if get or set
  // is called and make them non-enumerable and non-configurable.
1804
  // To be consistent with our normal functions we leave this as it is.
1805
  // TODO(lrn): Do set these to be thrower.
1806 1807 1808 1809
  return result;
}


1810 1811
function NewFunctionString(arguments, function_token) {
  var n = arguments.length;
1812 1813
  var p = '';
  if (n > 1) {
1814 1815 1816 1817
    p = ToString(arguments[0]);
    for (var i = 1; i < n - 1; i++) {
      p += ',' + ToString(arguments[i]);
    }
1818 1819 1820
    // If the formal parameters string include ) - an illegal
    // character - it may make the combined function expression
    // compile. We avoid this problem by checking for this early on.
1821
    if (%_CallFunction(p, ')', StringIndexOfJS) != -1) {
1822
      throw MakeSyntaxError('paren_in_arg_string', []);
1823
    }
1824 1825 1826 1827
    // If the formal parameters include an unbalanced block comment, the
    // function must be rejected. Since JavaScript does not allow nested
    // comments we can include a trailing block comment to catch this.
    p += '\n/' + '**/';
1828
  }
1829 1830 1831
  var body = (n > 0) ? ToString(arguments[n - 1]) : '';
  return '(' + function_token + '(' + p + ') {\n' + body + '\n})';
}
1832

1833 1834 1835

function FunctionConstructor(arg1) {  // length == 1
  var source = NewFunctionString(arguments, 'function');
1836
  var global_receiver = %GlobalReceiver(global);
1837 1838
  // Compile the string in the constructor and not a helper so that errors
  // appear to come from here.
1839 1840 1841
  var f = %CompileString(source, true);
  if (!IS_FUNCTION(f)) return f;
  f = %_CallFunction(global_receiver, f);
1842
  %FunctionMarkNameShouldPrintAsAnonymous(f);
1843
  return f;
1844
}
1845

1846 1847 1848

// ----------------------------------------------------------------------------

1849 1850
function SetUpFunction() {
  %CheckIsBootstrapping();
1851

1852
  %SetCode($Function, FunctionConstructor);
1853
  %SetProperty($Function.prototype, "constructor", $Function, DONT_ENUM);
1854

1855
  InstallFunctions($Function.prototype, DONT_ENUM, $Array(
1856
    "bind", FunctionBind,
1857 1858 1859 1860
    "toString", FunctionToString
  ));
}

1861
SetUpFunction();