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 // var $NaN = 0/0; 36 // 37 // in math.js: 38 // var $floor = MathFloor 39 40 var $isNaN = GlobalIsNaN; 41 var $isFinite = GlobalIsFinite; 42 43 // ---------------------------------------------------------------------------- 44 45 // Helper function used to install functions on objects. 46 function InstallFunctions(object, attributes, functions) { 47 if (functions.length >= 8) { 48 %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1); 49 } 50 for (var i = 0; i < functions.length; i += 2) { 51 var key = functions[i]; 52 var f = functions[i + 1]; 53 %FunctionSetName(f, key); 54 %FunctionRemovePrototype(f); 55 %SetProperty(object, key, f, attributes); 56 %SetNativeFlag(f); 57 } 58 %ToFastProperties(object); 59 } 60 61 62 // Helper function to install a getter-only accessor property. 63 function InstallGetter(object, name, getter) { 64 %FunctionSetName(getter, name); 65 %FunctionRemovePrototype(getter); 66 %DefineOrRedefineAccessorProperty(object, name, getter, null, DONT_ENUM); 67 %SetNativeFlag(getter); 68 } 69 70 71 // Helper function to install a getter/setter accessor property. 72 function InstallGetterSetter(object, name, getter, setter) { 73 %FunctionSetName(getter, name); 74 %FunctionSetName(setter, name); 75 %FunctionRemovePrototype(getter); 76 %FunctionRemovePrototype(setter); 77 %DefineOrRedefineAccessorProperty(object, name, getter, setter, DONT_ENUM); 78 %SetNativeFlag(getter); 79 %SetNativeFlag(setter); 80 } 81 82 83 // Prevents changes to the prototype of a built-in function. 84 // The "prototype" property of the function object is made non-configurable, 85 // and the prototype object is made non-extensible. The latter prevents 86 // changing the __proto__ property. 87 function SetUpLockedPrototype(constructor, fields, methods) { 88 %CheckIsBootstrapping(); 89 var prototype = constructor.prototype; 90 // Install functions first, because this function is used to initialize 91 // PropertyDescriptor itself. 92 var property_count = (methods.length >> 1) + (fields ? fields.length : 0); 93 if (property_count >= 4) { 94 %OptimizeObjectForAddingMultipleProperties(prototype, property_count); 95 } 96 if (fields) { 97 for (var i = 0; i < fields.length; i++) { 98 %SetProperty(prototype, fields[i], void 0, DONT_ENUM | DONT_DELETE); 99 } 100 } 101 for (var i = 0; i < methods.length; i += 2) { 102 var key = methods[i]; 103 var f = methods[i + 1]; 104 %SetProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY); 105 %SetNativeFlag(f); 106 } 107 %SetPrototype(prototype, null); 108 %ToFastProperties(prototype); 109 } 110 111 112 // ---------------------------------------------------------------------------- 113 114 115 // ECMA 262 - 15.1.4 116 function GlobalIsNaN(number) { 117 if (!IS_NUMBER(number)) number = NonNumberToNumber(number); 118 return NUMBER_IS_NAN(number); 119 } 120 121 122 // ECMA 262 - 15.1.5 123 function GlobalIsFinite(number) { 124 if (!IS_NUMBER(number)) number = NonNumberToNumber(number); 125 return NUMBER_IS_FINITE(number); 126 } 127 128 129 // ECMA-262 - 15.1.2.2 130 function GlobalParseInt(string, radix) { 131 if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) { 132 // Some people use parseInt instead of Math.floor. This 133 // optimization makes parseInt on a Smi 12 times faster (60ns 134 // vs 800ns). The following optimization makes parseInt on a 135 // non-Smi number 9 times faster (230ns vs 2070ns). Together 136 // they make parseInt on a string 1.4% slower (274ns vs 270ns). 137 if (%_IsSmi(string)) return string; 138 if (IS_NUMBER(string) && 139 ((0.01 < string && string < 1e9) || 140 (-1e9 < string && string < -0.01))) { 141 // Truncate number. 142 return string | 0; 143 } 144 string = TO_STRING_INLINE(string); 145 radix = radix | 0; 146 } else { 147 // The spec says ToString should be evaluated before ToInt32. 148 string = TO_STRING_INLINE(string); 149 radix = TO_INT32(radix); 150 if (!(radix == 0 || (2 <= radix && radix <= 36))) { 151 return $NaN; 152 } 153 } 154 155 if (%_HasCachedArrayIndex(string) && 156 (radix == 0 || radix == 10)) { 157 return %_GetCachedArrayIndex(string); 158 } 159 return %StringParseInt(string, radix); 160 } 161 162 163 // ECMA-262 - 15.1.2.3 164 function GlobalParseFloat(string) { 165 string = TO_STRING_INLINE(string); 166 if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string); 167 return %StringParseFloat(string); 168 } 169 170 171 function GlobalEval(x) { 172 if (!IS_STRING(x)) return x; 173 174 var global_receiver = %GlobalReceiver(global); 175 var global_is_detached = (global === global_receiver); 176 177 // For consistency with JSC we require the global object passed to 178 // eval to be the global object from which 'eval' originated. This 179 // is not mandated by the spec. 180 // We only throw if the global has been detached, since we need the 181 // receiver as this-value for the call. 182 if (global_is_detached) { 183 throw new $EvalError('The "this" value passed to eval must ' + 184 'be the global object from which eval originated'); 185 } 186 187 var f = %CompileString(x, false); 188 if (!IS_FUNCTION(f)) return f; 189 190 return %_CallFunction(global_receiver, f); 191 } 192 193 194 // ---------------------------------------------------------------------------- 195 196 // Set up global object. 197 function SetUpGlobal() { 198 %CheckIsBootstrapping(); 199 200 // ECMA 262 - 15.1.1.1. 201 %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY); 202 203 // ECMA-262 - 15.1.1.2. 204 %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE | READ_ONLY); 205 206 // ECMA-262 - 15.1.1.3. 207 %SetProperty(global, "undefined", void 0, 208 DONT_ENUM | DONT_DELETE | READ_ONLY); 209 210 // Set up non-enumerable function on the global object. 211 InstallFunctions(global, DONT_ENUM, $Array( 212 "isNaN", GlobalIsNaN, 213 "isFinite", GlobalIsFinite, 214 "parseInt", GlobalParseInt, 215 "parseFloat", GlobalParseFloat, 216 "eval", GlobalEval 217 )); 218 } 219 220 SetUpGlobal(); 221 222 223 // ---------------------------------------------------------------------------- 224 // Object 225 226 // ECMA-262 - 15.2.4.2 227 function ObjectToString() { 228 if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]"; 229 if (IS_NULL(this)) return "[object Null]"; 230 return "[object " + %_ClassOf(ToObject(this)) + "]"; 231 } 232 233 234 // ECMA-262 - 15.2.4.3 235 function ObjectToLocaleString() { 236 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { 237 throw MakeTypeError("called_on_null_or_undefined", 238 ["Object.prototype.toLocaleString"]); 239 } 240 return this.toString(); 241 } 242 243 244 // ECMA-262 - 15.2.4.4 245 function ObjectValueOf() { 246 return ToObject(this); 247 } 248 249 250 // ECMA-262 - 15.2.4.5 251 function ObjectHasOwnProperty(V) { 252 if (%IsJSProxy(this)) { 253 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 254 if (IS_SYMBOL(V)) return false; 255 256 var handler = %GetHandler(this); 257 return CallTrap1(handler, "hasOwn", DerivedHasOwnTrap, ToName(V)); 258 } 259 return %HasLocalProperty(TO_OBJECT_INLINE(this), ToName(V)); 260 } 261 262 263 // ECMA-262 - 15.2.4.6 264 function ObjectIsPrototypeOf(V) { 265 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { 266 throw MakeTypeError("called_on_null_or_undefined", 267 ["Object.prototype.isPrototypeOf"]); 268 } 269 if (!IS_SPEC_OBJECT(V)) return false; 270 return %IsInPrototypeChain(this, V); 271 } 272 273 274 // ECMA-262 - 15.2.4.6 275 function ObjectPropertyIsEnumerable(V) { 276 var P = ToName(V); 277 if (%IsJSProxy(this)) { 278 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 279 if (IS_SYMBOL(V)) return false; 280 281 var desc = GetOwnProperty(this, P); 282 return IS_UNDEFINED(desc) ? false : desc.isEnumerable(); 283 } 284 return %IsPropertyEnumerable(ToObject(this), P); 285 } 286 287 288 // Extensions for providing property getters and setters. 289 function ObjectDefineGetter(name, fun) { 290 var receiver = this; 291 if (receiver == null && !IS_UNDETECTABLE(receiver)) { 292 receiver = %GlobalReceiver(global); 293 } 294 if (!IS_SPEC_FUNCTION(fun)) { 295 throw new $TypeError( 296 'Object.prototype.__defineGetter__: Expecting function'); 297 } 298 var desc = new PropertyDescriptor(); 299 desc.setGet(fun); 300 desc.setEnumerable(true); 301 desc.setConfigurable(true); 302 DefineOwnProperty(ToObject(receiver), ToName(name), desc, false); 303 } 304 305 306 function ObjectLookupGetter(name) { 307 var receiver = this; 308 if (receiver == null && !IS_UNDETECTABLE(receiver)) { 309 receiver = %GlobalReceiver(global); 310 } 311 return %LookupAccessor(ToObject(receiver), ToName(name), GETTER); 312 } 313 314 315 function ObjectDefineSetter(name, fun) { 316 var receiver = this; 317 if (receiver == null && !IS_UNDETECTABLE(receiver)) { 318 receiver = %GlobalReceiver(global); 319 } 320 if (!IS_SPEC_FUNCTION(fun)) { 321 throw new $TypeError( 322 'Object.prototype.__defineSetter__: Expecting function'); 323 } 324 var desc = new PropertyDescriptor(); 325 desc.setSet(fun); 326 desc.setEnumerable(true); 327 desc.setConfigurable(true); 328 DefineOwnProperty(ToObject(receiver), ToName(name), desc, false); 329 } 330 331 332 function ObjectLookupSetter(name) { 333 var receiver = this; 334 if (receiver == null && !IS_UNDETECTABLE(receiver)) { 335 receiver = %GlobalReceiver(global); 336 } 337 return %LookupAccessor(ToObject(receiver), ToName(name), SETTER); 338 } 339 340 341 function ObjectKeys(obj) { 342 if (!IS_SPEC_OBJECT(obj)) { 343 throw MakeTypeError("called_on_non_object", ["Object.keys"]); 344 } 345 if (%IsJSProxy(obj)) { 346 var handler = %GetHandler(obj); 347 var names = CallTrap0(handler, "keys", DerivedKeysTrap); 348 return ToNameArray(names, "keys", false); 349 } 350 return %LocalKeys(obj); 351 } 352 353 354 // ES5 8.10.1. 355 function IsAccessorDescriptor(desc) { 356 if (IS_UNDEFINED(desc)) return false; 357 return desc.hasGetter() || desc.hasSetter(); 358 } 359 360 361 // ES5 8.10.2. 362 function IsDataDescriptor(desc) { 363 if (IS_UNDEFINED(desc)) return false; 364 return desc.hasValue() || desc.hasWritable(); 365 } 366 367 368 // ES5 8.10.3. 369 function IsGenericDescriptor(desc) { 370 if (IS_UNDEFINED(desc)) return false; 371 return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc)); 372 } 373 374 375 function IsInconsistentDescriptor(desc) { 376 return IsAccessorDescriptor(desc) && IsDataDescriptor(desc); 377 } 378 379 380 // ES5 8.10.4 381 function FromPropertyDescriptor(desc) { 382 if (IS_UNDEFINED(desc)) return desc; 383 384 if (IsDataDescriptor(desc)) { 385 return { value: desc.getValue(), 386 writable: desc.isWritable(), 387 enumerable: desc.isEnumerable(), 388 configurable: desc.isConfigurable() }; 389 } 390 // Must be an AccessorDescriptor then. We never return a generic descriptor. 391 return { get: desc.getGet(), 392 set: desc.getSet() === ObjectSetProto ? ObjectPoisonProto 393 : desc.getSet(), 394 enumerable: desc.isEnumerable(), 395 configurable: desc.isConfigurable() }; 396 } 397 398 399 // Harmony Proxies 400 function FromGenericPropertyDescriptor(desc) { 401 if (IS_UNDEFINED(desc)) return desc; 402 var obj = new $Object(); 403 404 if (desc.hasValue()) { 405 %IgnoreAttributesAndSetProperty(obj, "value", desc.getValue(), NONE); 406 } 407 if (desc.hasWritable()) { 408 %IgnoreAttributesAndSetProperty(obj, "writable", desc.isWritable(), NONE); 409 } 410 if (desc.hasGetter()) { 411 %IgnoreAttributesAndSetProperty(obj, "get", desc.getGet(), NONE); 412 } 413 if (desc.hasSetter()) { 414 %IgnoreAttributesAndSetProperty(obj, "set", desc.getSet(), NONE); 415 } 416 if (desc.hasEnumerable()) { 417 %IgnoreAttributesAndSetProperty(obj, "enumerable", 418 desc.isEnumerable(), NONE); 419 } 420 if (desc.hasConfigurable()) { 421 %IgnoreAttributesAndSetProperty(obj, "configurable", 422 desc.isConfigurable(), NONE); 423 } 424 return obj; 425 } 426 427 428 // ES5 8.10.5. 429 function ToPropertyDescriptor(obj) { 430 if (!IS_SPEC_OBJECT(obj)) { 431 throw MakeTypeError("property_desc_object", [obj]); 432 } 433 var desc = new PropertyDescriptor(); 434 435 if ("enumerable" in obj) { 436 desc.setEnumerable(ToBoolean(obj.enumerable)); 437 } 438 439 if ("configurable" in obj) { 440 desc.setConfigurable(ToBoolean(obj.configurable)); 441 } 442 443 if ("value" in obj) { 444 desc.setValue(obj.value); 445 } 446 447 if ("writable" in obj) { 448 desc.setWritable(ToBoolean(obj.writable)); 449 } 450 451 if ("get" in obj) { 452 var get = obj.get; 453 if (!IS_UNDEFINED(get) && !IS_SPEC_FUNCTION(get)) { 454 throw MakeTypeError("getter_must_be_callable", [get]); 455 } 456 desc.setGet(get); 457 } 458 459 if ("set" in obj) { 460 var set = obj.set; 461 if (!IS_UNDEFINED(set) && !IS_SPEC_FUNCTION(set)) { 462 throw MakeTypeError("setter_must_be_callable", [set]); 463 } 464 desc.setSet(set); 465 } 466 467 if (IsInconsistentDescriptor(desc)) { 468 throw MakeTypeError("value_and_accessor", [obj]); 469 } 470 return desc; 471 } 472 473 474 // For Harmony proxies. 475 function ToCompletePropertyDescriptor(obj) { 476 var desc = ToPropertyDescriptor(obj); 477 if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) { 478 if (!desc.hasValue()) desc.setValue(void 0); 479 if (!desc.hasWritable()) desc.setWritable(false); 480 } else { 481 // Is accessor descriptor. 482 if (!desc.hasGetter()) desc.setGet(void 0); 483 if (!desc.hasSetter()) desc.setSet(void 0); 484 } 485 if (!desc.hasEnumerable()) desc.setEnumerable(false); 486 if (!desc.hasConfigurable()) desc.setConfigurable(false); 487 return desc; 488 } 489 490 491 function PropertyDescriptor() { 492 // Initialize here so they are all in-object and have the same map. 493 // Default values from ES5 8.6.1. 494 this.value_ = void 0; 495 this.hasValue_ = false; 496 this.writable_ = false; 497 this.hasWritable_ = false; 498 this.enumerable_ = false; 499 this.hasEnumerable_ = false; 500 this.configurable_ = false; 501 this.hasConfigurable_ = false; 502 this.get_ = void 0; 503 this.hasGetter_ = false; 504 this.set_ = void 0; 505 this.hasSetter_ = false; 506 } 507 508 SetUpLockedPrototype(PropertyDescriptor, $Array( 509 "value_", 510 "hasValue_", 511 "writable_", 512 "hasWritable_", 513 "enumerable_", 514 "hasEnumerable_", 515 "configurable_", 516 "hasConfigurable_", 517 "get_", 518 "hasGetter_", 519 "set_", 520 "hasSetter_" 521 ), $Array( 522 "toString", function() { 523 return "[object PropertyDescriptor]"; 524 }, 525 "setValue", function(value) { 526 this.value_ = value; 527 this.hasValue_ = true; 528 }, 529 "getValue", function() { 530 return this.value_; 531 }, 532 "hasValue", function() { 533 return this.hasValue_; 534 }, 535 "setEnumerable", function(enumerable) { 536 this.enumerable_ = enumerable; 537 this.hasEnumerable_ = true; 538 }, 539 "isEnumerable", function () { 540 return this.enumerable_; 541 }, 542 "hasEnumerable", function() { 543 return this.hasEnumerable_; 544 }, 545 "setWritable", function(writable) { 546 this.writable_ = writable; 547 this.hasWritable_ = true; 548 }, 549 "isWritable", function() { 550 return this.writable_; 551 }, 552 "hasWritable", function() { 553 return this.hasWritable_; 554 }, 555 "setConfigurable", function(configurable) { 556 this.configurable_ = configurable; 557 this.hasConfigurable_ = true; 558 }, 559 "hasConfigurable", function() { 560 return this.hasConfigurable_; 561 }, 562 "isConfigurable", function() { 563 return this.configurable_; 564 }, 565 "setGet", function(get) { 566 this.get_ = get; 567 this.hasGetter_ = true; 568 }, 569 "getGet", function() { 570 return this.get_; 571 }, 572 "hasGetter", function() { 573 return this.hasGetter_; 574 }, 575 "setSet", function(set) { 576 this.set_ = set; 577 this.hasSetter_ = true; 578 }, 579 "getSet", function() { 580 return this.set_; 581 }, 582 "hasSetter", function() { 583 return this.hasSetter_; 584 })); 585 586 587 // Converts an array returned from Runtime_GetOwnProperty to an actual 588 // property descriptor. For a description of the array layout please 589 // see the runtime.cc file. 590 function ConvertDescriptorArrayToDescriptor(desc_array) { 591 if (desc_array === false) { 592 throw 'Internal error: invalid desc_array'; 593 } 594 595 if (IS_UNDEFINED(desc_array)) { 596 return void 0; 597 } 598 599 var desc = new PropertyDescriptor(); 600 // This is an accessor. 601 if (desc_array[IS_ACCESSOR_INDEX]) { 602 desc.setGet(desc_array[GETTER_INDEX]); 603 desc.setSet(desc_array[SETTER_INDEX]); 604 } else { 605 desc.setValue(desc_array[VALUE_INDEX]); 606 desc.setWritable(desc_array[WRITABLE_INDEX]); 607 } 608 desc.setEnumerable(desc_array[ENUMERABLE_INDEX]); 609 desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]); 610 611 return desc; 612 } 613 614 615 // For Harmony proxies. 616 function GetTrap(handler, name, defaultTrap) { 617 var trap = handler[name]; 618 if (IS_UNDEFINED(trap)) { 619 if (IS_UNDEFINED(defaultTrap)) { 620 throw MakeTypeError("handler_trap_missing", [handler, name]); 621 } 622 trap = defaultTrap; 623 } else if (!IS_SPEC_FUNCTION(trap)) { 624 throw MakeTypeError("handler_trap_must_be_callable", [handler, name]); 625 } 626 return trap; 627 } 628 629 630 function CallTrap0(handler, name, defaultTrap) { 631 return %_CallFunction(handler, GetTrap(handler, name, defaultTrap)); 632 } 633 634 635 function CallTrap1(handler, name, defaultTrap, x) { 636 return %_CallFunction(handler, x, GetTrap(handler, name, defaultTrap)); 637 } 638 639 640 function CallTrap2(handler, name, defaultTrap, x, y) { 641 return %_CallFunction(handler, x, y, GetTrap(handler, name, defaultTrap)); 642 } 643 644 645 // ES5 section 8.12.1. 646 function GetOwnProperty(obj, v) { 647 var p = ToName(v); 648 if (%IsJSProxy(obj)) { 649 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 650 if (IS_SYMBOL(v)) return void 0; 651 652 var handler = %GetHandler(obj); 653 var descriptor = CallTrap1(handler, "getOwnPropertyDescriptor", void 0, 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 void 0; 670 671 return ConvertDescriptorArrayToDescriptor(props); 672 } 673 674 675 // ES5 section 8.12.7. 676 function 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. 691 function 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", void 0, 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. 710 function 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 void 0; 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 = void 0; // 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. 868 function 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_ = void 0; 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. 980 function 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. 996 function 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 1005 function 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 1016 function 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. 1041 function 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", void 0); 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. 1114 function 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. 1125 function 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 1165 function 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. 1177 function 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. 1195 function ProxyFix(obj) { 1196 var handler = %GetHandler(obj); 1197 var props = CallTrap0(handler, "fix", void 0); 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. 1224 function 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. 1246 function 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)) { 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 1276 function 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 1289 function 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 1310 function 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 1332 function 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. 1344 function 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. 1354 function ObjectGetProto() { 1355 return %GetPrototype(this); 1356 } 1357 1358 1359 // Harmony __proto__ setter. 1360 function ObjectSetProto(obj) { 1361 return %SetPrototype(this, obj); 1362 } 1363 1364 1365 // Harmony __proto__ poison pill. 1366 function ObjectPoisonProto(obj) { 1367 throw MakeTypeError("proto_poison_pill", []); 1368 } 1369 1370 1371 function 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 1385 function 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 1431 SetUpObject(); 1432 1433 1434 // ---------------------------------------------------------------------------- 1435 // Boolean 1436 1437 function BooleanConstructor(x) { 1438 if (%_IsConstructCall()) { 1439 %_SetValueOf(this, ToBoolean(x)); 1440 } else { 1441 return ToBoolean(x); 1442 } 1443 } 1444 1445 1446 function 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 1460 function 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 1472 function 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 1485 SetUpBoolean(); 1486 1487 1488 // ---------------------------------------------------------------------------- 1489 // Number 1490 1491 function 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. 1502 function 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 1529 function NumberToLocaleString() { 1530 return %_CallFunction(this, NumberToString); 1531 } 1532 1533 1534 // ECMA-262 section 15.7.4.4 1535 function 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 1546 function 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 == 1/0) return "Infinity"; 1564 if (x == -1/0) return "-Infinity"; 1565 1566 return %NumberToFixed(x, f); 1567 } 1568 1569 1570 // ECMA-262 section 15.7.4.6 1571 function 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) ? void 0 : TO_INTEGER(fractionDigits); 1582 1583 if (NUMBER_IS_NAN(x)) return "NaN"; 1584 if (x == 1/0) return "Infinity"; 1585 if (x == -1/0) 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 1597 function 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 == 1/0) return "Infinity"; 1612 if (x == -1/0) 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. 1622 function NumberIsFinite(number) { 1623 return IS_NUMBER(number) && NUMBER_IS_FINITE(number); 1624 } 1625 1626 1627 // Harmony isNaN. 1628 function NumberIsNaN(number) { 1629 return IS_NUMBER(number) && NUMBER_IS_NAN(number); 1630 } 1631 1632 1633 // ---------------------------------------------------------------------------- 1634 1635 function 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 -1/0, 1663 DONT_ENUM | DONT_DELETE | READ_ONLY); 1664 1665 // ECMA-262 section 15.7.3.5. 1666 %SetProperty($Number, 1667 "POSITIVE_INFINITY", 1668 1/0, 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 1687 SetUpNumber(); 1688 1689 1690 // ---------------------------------------------------------------------------- 1691 // Function 1692 1693 function 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 1721 function FunctionToString() { 1722 return FunctionSourceString(this); 1723 } 1724 1725 1726 // ES5 15.3.4.5 1727 function 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 1789 function 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 1813 function 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 1826 function 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 1838 SetUpFunction(); 1839