1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 // This file relies on the fact that the following declarations have been made 29 // in runtime.js: 30 // var $Object = global.Object; 31 // var $Boolean = global.Boolean; 32 // var $Number = global.Number; 33 // var $Function = global.Function; 34 // var $Array = global.Array; 35 // 36 // in math.js: 37 // var $floor = MathFloor 38 39 var $isNaN = GlobalIsNaN; 40 var $isFinite = GlobalIsFinite; 41 42 // ---------------------------------------------------------------------------- 43 44 // Helper function used to install functions on objects. 45 function InstallFunctions(object, attributes, functions) { 46 if (functions.length >= 8) { 47 %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1); 48 } 49 for (var i = 0; i < functions.length; i += 2) { 50 var key = functions[i]; 51 var f = functions[i + 1]; 52 %FunctionSetName(f, key); 53 %FunctionRemovePrototype(f); 54 %SetProperty(object, key, f, attributes); 55 %SetNativeFlag(f); 56 } 57 %ToFastProperties(object); 58 } 59 60 61 // Helper function to install a getter-only accessor property. 62 function InstallGetter(object, name, getter) { 63 %FunctionSetName(getter, name); 64 %FunctionRemovePrototype(getter); 65 %DefineOrRedefineAccessorProperty(object, name, getter, null, DONT_ENUM); 66 %SetNativeFlag(getter); 67 } 68 69 70 // Helper function to install a getter/setter accessor property. 71 function InstallGetterSetter(object, name, getter, setter) { 72 %FunctionSetName(getter, name); 73 %FunctionSetName(setter, name); 74 %FunctionRemovePrototype(getter); 75 %FunctionRemovePrototype(setter); 76 %DefineOrRedefineAccessorProperty(object, name, getter, setter, DONT_ENUM); 77 %SetNativeFlag(getter); 78 %SetNativeFlag(setter); 79 } 80 81 82 // Prevents changes to the prototype of a built-in function. 83 // The "prototype" property of the function object is made non-configurable, 84 // and the prototype object is made non-extensible. The latter prevents 85 // changing the __proto__ property. 86 function SetUpLockedPrototype(constructor, fields, methods) { 87 %CheckIsBootstrapping(); 88 var prototype = constructor.prototype; 89 // Install functions first, because this function is used to initialize 90 // PropertyDescriptor itself. 91 var property_count = (methods.length >> 1) + (fields ? fields.length : 0); 92 if (property_count >= 4) { 93 %OptimizeObjectForAddingMultipleProperties(prototype, property_count); 94 } 95 if (fields) { 96 for (var i = 0; i < fields.length; i++) { 97 %SetProperty(prototype, fields[i], UNDEFINED, DONT_ENUM | DONT_DELETE); 98 } 99 } 100 for (var i = 0; i < methods.length; i += 2) { 101 var key = methods[i]; 102 var f = methods[i + 1]; 103 %SetProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY); 104 %SetNativeFlag(f); 105 } 106 %SetPrototype(prototype, null); 107 %ToFastProperties(prototype); 108 } 109 110 111 // ---------------------------------------------------------------------------- 112 113 114 // ECMA 262 - 15.1.4 115 function GlobalIsNaN(number) { 116 if (!IS_NUMBER(number)) number = NonNumberToNumber(number); 117 return NUMBER_IS_NAN(number); 118 } 119 120 121 // ECMA 262 - 15.1.5 122 function GlobalIsFinite(number) { 123 if (!IS_NUMBER(number)) number = NonNumberToNumber(number); 124 return NUMBER_IS_FINITE(number); 125 } 126 127 128 // ECMA-262 - 15.1.2.2 129 function GlobalParseInt(string, radix) { 130 if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) { 131 // Some people use parseInt instead of Math.floor. This 132 // optimization makes parseInt on a Smi 12 times faster (60ns 133 // vs 800ns). The following optimization makes parseInt on a 134 // non-Smi number 9 times faster (230ns vs 2070ns). Together 135 // they make parseInt on a string 1.4% slower (274ns vs 270ns). 136 if (%_IsSmi(string)) return string; 137 if (IS_NUMBER(string) && 138 ((0.01 < string && string < 1e9) || 139 (-1e9 < string && string < -0.01))) { 140 // Truncate number. 141 return string | 0; 142 } 143 string = TO_STRING_INLINE(string); 144 radix = radix | 0; 145 } else { 146 // The spec says ToString should be evaluated before ToInt32. 147 string = TO_STRING_INLINE(string); 148 radix = TO_INT32(radix); 149 if (!(radix == 0 || (2 <= radix && radix <= 36))) { 150 return NAN; 151 } 152 } 153 154 if (%_HasCachedArrayIndex(string) && 155 (radix == 0 || radix == 10)) { 156 return %_GetCachedArrayIndex(string); 157 } 158 return %StringParseInt(string, radix); 159 } 160 161 162 // ECMA-262 - 15.1.2.3 163 function GlobalParseFloat(string) { 164 string = TO_STRING_INLINE(string); 165 if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string); 166 return %StringParseFloat(string); 167 } 168 169 170 function GlobalEval(x) { 171 if (!IS_STRING(x)) return x; 172 173 // For consistency with JSC we require the global object passed to 174 // eval to be the global object from which 'eval' originated. This 175 // is not mandated by the spec. 176 // We only throw if the global has been detached, since we need the 177 // receiver as this-value for the call. 178 if (!%IsAttachedGlobal(global)) { 179 throw new $EvalError('The "this" value passed to eval must ' + 180 'be the global object from which eval originated'); 181 } 182 183 var global_receiver = %GlobalReceiver(global); 184 185 var f = %CompileString(x, false); 186 if (!IS_FUNCTION(f)) return f; 187 188 return %_CallFunction(global_receiver, f); 189 } 190 191 192 // ---------------------------------------------------------------------------- 193 194 // Set up global object. 195 function SetUpGlobal() { 196 %CheckIsBootstrapping(); 197 198 var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY; 199 200 // ECMA 262 - 15.1.1.1. 201 %SetProperty(global, "NaN", NAN, attributes); 202 203 // ECMA-262 - 15.1.1.2. 204 %SetProperty(global, "Infinity", INFINITY, attributes); 205 206 // ECMA-262 - 15.1.1.3. 207 %SetProperty(global, "undefined", UNDEFINED, attributes); 208 209 // Set up non-enumerable function on the global object. 210 InstallFunctions(global, DONT_ENUM, $Array( 211 "isNaN", GlobalIsNaN, 212 "isFinite", GlobalIsFinite, 213 "parseInt", GlobalParseInt, 214 "parseFloat", GlobalParseFloat, 215 "eval", GlobalEval 216 )); 217 } 218 219 SetUpGlobal(); 220 221 222 // ---------------------------------------------------------------------------- 223 // Object 224 225 // ECMA-262 - 15.2.4.2 226 function ObjectToString() { 227 if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]"; 228 if (IS_NULL(this)) return "[object Null]"; 229 return "[object " + %_ClassOf(ToObject(this)) + "]"; 230 } 231 232 233 // ECMA-262 - 15.2.4.3 234 function ObjectToLocaleString() { 235 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { 236 throw MakeTypeError("called_on_null_or_undefined", 237 ["Object.prototype.toLocaleString"]); 238 } 239 return this.toString(); 240 } 241 242 243 // ECMA-262 - 15.2.4.4 244 function ObjectValueOf() { 245 return ToObject(this); 246 } 247 248 249 // ECMA-262 - 15.2.4.5 250 function ObjectHasOwnProperty(V) { 251 if (%IsJSProxy(this)) { 252 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 253 if (IS_SYMBOL(V)) return false; 254 255 var handler = %GetHandler(this); 256 return CallTrap1(handler, "hasOwn", DerivedHasOwnTrap, ToName(V)); 257 } 258 return %HasLocalProperty(TO_OBJECT_INLINE(this), ToName(V)); 259 } 260 261 262 // ECMA-262 - 15.2.4.6 263 function ObjectIsPrototypeOf(V) { 264 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { 265 throw MakeTypeError("called_on_null_or_undefined", 266 ["Object.prototype.isPrototypeOf"]); 267 } 268 if (!IS_SPEC_OBJECT(V)) return false; 269 return %IsInPrototypeChain(this, V); 270 } 271 272 273 // ECMA-262 - 15.2.4.6 274 function ObjectPropertyIsEnumerable(V) { 275 var P = ToName(V); 276 if (%IsJSProxy(this)) { 277 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 278 if (IS_SYMBOL(V)) return false; 279 280 var desc = GetOwnProperty(this, P); 281 return IS_UNDEFINED(desc) ? false : desc.isEnumerable(); 282 } 283 return %IsPropertyEnumerable(ToObject(this), P); 284 } 285 286 287 // Extensions for providing property getters and setters. 288 function ObjectDefineGetter(name, fun) { 289 var receiver = this; 290 if (receiver == null && !IS_UNDETECTABLE(receiver)) { 291 receiver = %GlobalReceiver(global); 292 } 293 if (!IS_SPEC_FUNCTION(fun)) { 294 throw new $TypeError( 295 'Object.prototype.__defineGetter__: Expecting function'); 296 } 297 var desc = new PropertyDescriptor(); 298 desc.setGet(fun); 299 desc.setEnumerable(true); 300 desc.setConfigurable(true); 301 DefineOwnProperty(ToObject(receiver), ToName(name), desc, false); 302 } 303 304 305 function ObjectLookupGetter(name) { 306 var receiver = this; 307 if (receiver == null && !IS_UNDETECTABLE(receiver)) { 308 receiver = %GlobalReceiver(global); 309 } 310 return %LookupAccessor(ToObject(receiver), ToName(name), GETTER); 311 } 312 313 314 function ObjectDefineSetter(name, fun) { 315 var receiver = this; 316 if (receiver == null && !IS_UNDETECTABLE(receiver)) { 317 receiver = %GlobalReceiver(global); 318 } 319 if (!IS_SPEC_FUNCTION(fun)) { 320 throw new $TypeError( 321 'Object.prototype.__defineSetter__: Expecting function'); 322 } 323 var desc = new PropertyDescriptor(); 324 desc.setSet(fun); 325 desc.setEnumerable(true); 326 desc.setConfigurable(true); 327 DefineOwnProperty(ToObject(receiver), ToName(name), desc, false); 328 } 329 330 331 function ObjectLookupSetter(name) { 332 var receiver = this; 333 if (receiver == null && !IS_UNDETECTABLE(receiver)) { 334 receiver = %GlobalReceiver(global); 335 } 336 return %LookupAccessor(ToObject(receiver), ToName(name), SETTER); 337 } 338 339 340 function ObjectKeys(obj) { 341 if (!IS_SPEC_OBJECT(obj)) { 342 throw MakeTypeError("called_on_non_object", ["Object.keys"]); 343 } 344 if (%IsJSProxy(obj)) { 345 var handler = %GetHandler(obj); 346 var names = CallTrap0(handler, "keys", DerivedKeysTrap); 347 return ToNameArray(names, "keys", false); 348 } 349 return %LocalKeys(obj); 350 } 351 352 353 // ES5 8.10.1. 354 function IsAccessorDescriptor(desc) { 355 if (IS_UNDEFINED(desc)) return false; 356 return desc.hasGetter() || desc.hasSetter(); 357 } 358 359 360 // ES5 8.10.2. 361 function IsDataDescriptor(desc) { 362 if (IS_UNDEFINED(desc)) return false; 363 return desc.hasValue() || desc.hasWritable(); 364 } 365 366 367 // ES5 8.10.3. 368 function IsGenericDescriptor(desc) { 369 if (IS_UNDEFINED(desc)) return false; 370 return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc)); 371 } 372 373 374 function IsInconsistentDescriptor(desc) { 375 return IsAccessorDescriptor(desc) && IsDataDescriptor(desc); 376 } 377 378 379 // ES5 8.10.4 380 function FromPropertyDescriptor(desc) { 381 if (IS_UNDEFINED(desc)) return desc; 382 383 if (IsDataDescriptor(desc)) { 384 return { value: desc.getValue(), 385 writable: desc.isWritable(), 386 enumerable: desc.isEnumerable(), 387 configurable: desc.isConfigurable() }; 388 } 389 // Must be an AccessorDescriptor then. We never return a generic descriptor. 390 return { get: desc.getGet(), 391 set: desc.getSet() === ObjectSetProto ? ObjectPoisonProto 392 : desc.getSet(), 393 enumerable: desc.isEnumerable(), 394 configurable: desc.isConfigurable() }; 395 } 396 397 398 // Harmony Proxies 399 function FromGenericPropertyDescriptor(desc) { 400 if (IS_UNDEFINED(desc)) return desc; 401 var obj = new $Object(); 402 403 if (desc.hasValue()) { 404 %IgnoreAttributesAndSetProperty(obj, "value", desc.getValue(), NONE); 405 } 406 if (desc.hasWritable()) { 407 %IgnoreAttributesAndSetProperty(obj, "writable", desc.isWritable(), NONE); 408 } 409 if (desc.hasGetter()) { 410 %IgnoreAttributesAndSetProperty(obj, "get", desc.getGet(), NONE); 411 } 412 if (desc.hasSetter()) { 413 %IgnoreAttributesAndSetProperty(obj, "set", desc.getSet(), NONE); 414 } 415 if (desc.hasEnumerable()) { 416 %IgnoreAttributesAndSetProperty(obj, "enumerable", 417 desc.isEnumerable(), NONE); 418 } 419 if (desc.hasConfigurable()) { 420 %IgnoreAttributesAndSetProperty(obj, "configurable", 421 desc.isConfigurable(), NONE); 422 } 423 return obj; 424 } 425 426 427 // ES5 8.10.5. 428 function ToPropertyDescriptor(obj) { 429 if (!IS_SPEC_OBJECT(obj)) { 430 throw MakeTypeError("property_desc_object", [obj]); 431 } 432 var desc = new PropertyDescriptor(); 433 434 if ("enumerable" in obj) { 435 desc.setEnumerable(ToBoolean(obj.enumerable)); 436 } 437 438 if ("configurable" in obj) { 439 desc.setConfigurable(ToBoolean(obj.configurable)); 440 } 441 442 if ("value" in obj) { 443 desc.setValue(obj.value); 444 } 445 446 if ("writable" in obj) { 447 desc.setWritable(ToBoolean(obj.writable)); 448 } 449 450 if ("get" in obj) { 451 var get = obj.get; 452 if (!IS_UNDEFINED(get) && !IS_SPEC_FUNCTION(get)) { 453 throw MakeTypeError("getter_must_be_callable", [get]); 454 } 455 desc.setGet(get); 456 } 457 458 if ("set" in obj) { 459 var set = obj.set; 460 if (!IS_UNDEFINED(set) && !IS_SPEC_FUNCTION(set)) { 461 throw MakeTypeError("setter_must_be_callable", [set]); 462 } 463 desc.setSet(set); 464 } 465 466 if (IsInconsistentDescriptor(desc)) { 467 throw MakeTypeError("value_and_accessor", [obj]); 468 } 469 return desc; 470 } 471 472 473 // For Harmony proxies. 474 function ToCompletePropertyDescriptor(obj) { 475 var desc = ToPropertyDescriptor(obj); 476 if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) { 477 if (!desc.hasValue()) desc.setValue(UNDEFINED); 478 if (!desc.hasWritable()) desc.setWritable(false); 479 } else { 480 // Is accessor descriptor. 481 if (!desc.hasGetter()) desc.setGet(UNDEFINED); 482 if (!desc.hasSetter()) desc.setSet(UNDEFINED); 483 } 484 if (!desc.hasEnumerable()) desc.setEnumerable(false); 485 if (!desc.hasConfigurable()) desc.setConfigurable(false); 486 return desc; 487 } 488 489 490 function PropertyDescriptor() { 491 // Initialize here so they are all in-object and have the same map. 492 // Default values from ES5 8.6.1. 493 this.value_ = UNDEFINED; 494 this.hasValue_ = false; 495 this.writable_ = false; 496 this.hasWritable_ = false; 497 this.enumerable_ = false; 498 this.hasEnumerable_ = false; 499 this.configurable_ = false; 500 this.hasConfigurable_ = false; 501 this.get_ = UNDEFINED; 502 this.hasGetter_ = false; 503 this.set_ = UNDEFINED; 504 this.hasSetter_ = false; 505 } 506 507 SetUpLockedPrototype(PropertyDescriptor, $Array( 508 "value_", 509 "hasValue_", 510 "writable_", 511 "hasWritable_", 512 "enumerable_", 513 "hasEnumerable_", 514 "configurable_", 515 "hasConfigurable_", 516 "get_", 517 "hasGetter_", 518 "set_", 519 "hasSetter_" 520 ), $Array( 521 "toString", function() { 522 return "[object PropertyDescriptor]"; 523 }, 524 "setValue", function(value) { 525 this.value_ = value; 526 this.hasValue_ = true; 527 }, 528 "getValue", function() { 529 return this.value_; 530 }, 531 "hasValue", function() { 532 return this.hasValue_; 533 }, 534 "setEnumerable", function(enumerable) { 535 this.enumerable_ = enumerable; 536 this.hasEnumerable_ = true; 537 }, 538 "isEnumerable", function () { 539 return this.enumerable_; 540 }, 541 "hasEnumerable", function() { 542 return this.hasEnumerable_; 543 }, 544 "setWritable", function(writable) { 545 this.writable_ = writable; 546 this.hasWritable_ = true; 547 }, 548 "isWritable", function() { 549 return this.writable_; 550 }, 551 "hasWritable", function() { 552 return this.hasWritable_; 553 }, 554 "setConfigurable", function(configurable) { 555 this.configurable_ = configurable; 556 this.hasConfigurable_ = true; 557 }, 558 "hasConfigurable", function() { 559 return this.hasConfigurable_; 560 }, 561 "isConfigurable", function() { 562 return this.configurable_; 563 }, 564 "setGet", function(get) { 565 this.get_ = get; 566 this.hasGetter_ = true; 567 }, 568 "getGet", function() { 569 return this.get_; 570 }, 571 "hasGetter", function() { 572 return this.hasGetter_; 573 }, 574 "setSet", function(set) { 575 this.set_ = set; 576 this.hasSetter_ = true; 577 }, 578 "getSet", function() { 579 return this.set_; 580 }, 581 "hasSetter", function() { 582 return this.hasSetter_; 583 })); 584 585 586 // Converts an array returned from Runtime_GetOwnProperty to an actual 587 // property descriptor. For a description of the array layout please 588 // see the runtime.cc file. 589 function ConvertDescriptorArrayToDescriptor(desc_array) { 590 if (desc_array === false) { 591 throw 'Internal error: invalid desc_array'; 592 } 593 594 if (IS_UNDEFINED(desc_array)) { 595 return UNDEFINED; 596 } 597 598 var desc = new PropertyDescriptor(); 599 // This is an accessor. 600 if (desc_array[IS_ACCESSOR_INDEX]) { 601 desc.setGet(desc_array[GETTER_INDEX]); 602 desc.setSet(desc_array[SETTER_INDEX]); 603 } else { 604 desc.setValue(desc_array[VALUE_INDEX]); 605 desc.setWritable(desc_array[WRITABLE_INDEX]); 606 } 607 desc.setEnumerable(desc_array[ENUMERABLE_INDEX]); 608 desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]); 609 610 return desc; 611 } 612 613 614 // For Harmony proxies. 615 function GetTrap(handler, name, defaultTrap) { 616 var trap = handler[name]; 617 if (IS_UNDEFINED(trap)) { 618 if (IS_UNDEFINED(defaultTrap)) { 619 throw MakeTypeError("handler_trap_missing", [handler, name]); 620 } 621 trap = defaultTrap; 622 } else if (!IS_SPEC_FUNCTION(trap)) { 623 throw MakeTypeError("handler_trap_must_be_callable", [handler, name]); 624 } 625 return trap; 626 } 627 628 629 function CallTrap0(handler, name, defaultTrap) { 630 return %_CallFunction(handler, GetTrap(handler, name, defaultTrap)); 631 } 632 633 634 function CallTrap1(handler, name, defaultTrap, x) { 635 return %_CallFunction(handler, x, GetTrap(handler, name, defaultTrap)); 636 } 637 638 639 function CallTrap2(handler, name, defaultTrap, x, y) { 640 return %_CallFunction(handler, x, y, GetTrap(handler, name, defaultTrap)); 641 } 642 643 644 // ES5 section 8.12.1. 645 function GetOwnProperty(obj, v) { 646 var p = ToName(v); 647 if (%IsJSProxy(obj)) { 648 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 649 if (IS_SYMBOL(v)) return UNDEFINED; 650 651 var handler = %GetHandler(obj); 652 var descriptor = CallTrap1( 653 handler, "getOwnPropertyDescriptor", UNDEFINED, p); 654 if (IS_UNDEFINED(descriptor)) return descriptor; 655 var desc = ToCompletePropertyDescriptor(descriptor); 656 if (!desc.isConfigurable()) { 657 throw MakeTypeError("proxy_prop_not_configurable", 658 [handler, "getOwnPropertyDescriptor", p, descriptor]); 659 } 660 return desc; 661 } 662 663 // GetOwnProperty returns an array indexed by the constants 664 // defined in macros.py. 665 // If p is not a property on obj undefined is returned. 666 var props = %GetOwnProperty(ToObject(obj), p); 667 668 // A false value here means that access checks failed. 669 if (props === false) return UNDEFINED; 670 671 return ConvertDescriptorArrayToDescriptor(props); 672 } 673 674 675 // ES5 section 8.12.7. 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", UNDEFINED, p, attributes); 697 if (!ToBoolean(result)) { 698 if (should_throw) { 699 throw MakeTypeError("handler_returned_false", 700 [handler, "defineProperty"]); 701 } else { 702 return false; 703 } 704 } 705 return true; 706 } 707 708 709 // ES5 8.12.9. 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 UNDEFINED; 714 715 var current = ConvertDescriptorArrayToDescriptor(current_or_access); 716 var extensible = %IsExtensible(ToObject(obj)); 717 718 // Error handling according to spec. 719 // Step 3 720 if (IS_UNDEFINED(current) && !extensible) { 721 if (should_throw) { 722 throw MakeTypeError("define_disallowed", [p]); 723 } else { 724 return false; 725 } 726 } 727 728 if (!IS_UNDEFINED(current)) { 729 // Step 5 and 6 730 if ((IsGenericDescriptor(desc) || 731 IsDataDescriptor(desc) == IsDataDescriptor(current)) && 732 (!desc.hasEnumerable() || 733 SameValue(desc.isEnumerable(), current.isEnumerable())) && 734 (!desc.hasConfigurable() || 735 SameValue(desc.isConfigurable(), current.isConfigurable())) && 736 (!desc.hasWritable() || 737 SameValue(desc.isWritable(), current.isWritable())) && 738 (!desc.hasValue() || 739 SameValue(desc.getValue(), current.getValue())) && 740 (!desc.hasGetter() || 741 SameValue(desc.getGet(), current.getGet())) && 742 (!desc.hasSetter() || 743 SameValue(desc.getSet(), current.getSet()))) { 744 return true; 745 } 746 if (!current.isConfigurable()) { 747 // Step 7 748 if (desc.isConfigurable() || 749 (desc.hasEnumerable() && 750 desc.isEnumerable() != current.isEnumerable())) { 751 if (should_throw) { 752 throw MakeTypeError("redefine_disallowed", [p]); 753 } else { 754 return false; 755 } 756 } 757 // Step 8 758 if (!IsGenericDescriptor(desc)) { 759 // Step 9a 760 if (IsDataDescriptor(current) != IsDataDescriptor(desc)) { 761 if (should_throw) { 762 throw MakeTypeError("redefine_disallowed", [p]); 763 } else { 764 return false; 765 } 766 } 767 // Step 10a 768 if (IsDataDescriptor(current) && IsDataDescriptor(desc)) { 769 if (!current.isWritable() && desc.isWritable()) { 770 if (should_throw) { 771 throw MakeTypeError("redefine_disallowed", [p]); 772 } else { 773 return false; 774 } 775 } 776 if (!current.isWritable() && desc.hasValue() && 777 !SameValue(desc.getValue(), current.getValue())) { 778 if (should_throw) { 779 throw MakeTypeError("redefine_disallowed", [p]); 780 } else { 781 return false; 782 } 783 } 784 } 785 // Step 11 786 if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) { 787 if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) { 788 if (should_throw) { 789 throw MakeTypeError("redefine_disallowed", [p]); 790 } else { 791 return false; 792 } 793 } 794 if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) { 795 if (should_throw) { 796 throw MakeTypeError("redefine_disallowed", [p]); 797 } else { 798 return false; 799 } 800 } 801 } 802 } 803 } 804 } 805 806 // Send flags - enumerable and configurable are common - writable is 807 // only send to the data descriptor. 808 // Take special care if enumerable and configurable is not defined on 809 // desc (we need to preserve the existing values from current). 810 var flag = NONE; 811 if (desc.hasEnumerable()) { 812 flag |= desc.isEnumerable() ? 0 : DONT_ENUM; 813 } else if (!IS_UNDEFINED(current)) { 814 flag |= current.isEnumerable() ? 0 : DONT_ENUM; 815 } else { 816 flag |= DONT_ENUM; 817 } 818 819 if (desc.hasConfigurable()) { 820 flag |= desc.isConfigurable() ? 0 : DONT_DELETE; 821 } else if (!IS_UNDEFINED(current)) { 822 flag |= current.isConfigurable() ? 0 : DONT_DELETE; 823 } else 824 flag |= DONT_DELETE; 825 826 if (IsDataDescriptor(desc) || 827 (IsGenericDescriptor(desc) && 828 (IS_UNDEFINED(current) || IsDataDescriptor(current)))) { 829 // There are 3 cases that lead here: 830 // Step 4a - defining a new data property. 831 // Steps 9b & 12 - replacing an existing accessor property with a data 832 // property. 833 // Step 12 - updating an existing data property with a data or generic 834 // descriptor. 835 836 if (desc.hasWritable()) { 837 flag |= desc.isWritable() ? 0 : READ_ONLY; 838 } else if (!IS_UNDEFINED(current)) { 839 flag |= current.isWritable() ? 0 : READ_ONLY; 840 } else { 841 flag |= READ_ONLY; 842 } 843 844 var value = UNDEFINED; // Default value is undefined. 845 if (desc.hasValue()) { 846 value = desc.getValue(); 847 } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) { 848 value = current.getValue(); 849 } 850 851 %DefineOrRedefineDataProperty(obj, p, value, flag); 852 } else { 853 // There are 3 cases that lead here: 854 // Step 4b - defining a new accessor property. 855 // Steps 9c & 12 - replacing an existing data property with an accessor 856 // property. 857 // Step 12 - updating an existing accessor property with an accessor 858 // descriptor. 859 var getter = desc.hasGetter() ? desc.getGet() : null; 860 var setter = desc.hasSetter() ? desc.getSet() : null; 861 %DefineOrRedefineAccessorProperty(obj, p, getter, setter, flag); 862 } 863 return true; 864 } 865 866 867 // ES5 section 15.4.5.1. 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_ = UNDEFINED; 924 desc.hasValue_ = false; 925 threw = !DefineObjectProperty(obj, "length", desc, should_throw) || threw; 926 if (emit_splice) { 927 EndPerformSplice(obj); 928 EnqueueSpliceRecord(obj, 929 new_length < old_length ? new_length : old_length, 930 removed, 931 new_length > old_length ? new_length - old_length : 0); 932 } 933 if (threw) { 934 if (should_throw) { 935 throw MakeTypeError("redefine_disallowed", [p]); 936 } else { 937 return false; 938 } 939 } 940 return true; 941 } 942 943 // Step 4 - Special handling for array index. 944 var index = ToUint32(p); 945 var emit_splice = false; 946 if (ToString(index) == p && index != 4294967295) { 947 var length = obj.length; 948 if (index >= length && %IsObserved(obj)) { 949 emit_splice = true; 950 BeginPerformSplice(obj); 951 } 952 953 var length_desc = GetOwnProperty(obj, "length"); 954 if ((index >= length && !length_desc.isWritable()) || 955 !DefineObjectProperty(obj, p, desc, true)) { 956 if (emit_splice) 957 EndPerformSplice(obj); 958 if (should_throw) { 959 throw MakeTypeError("define_disallowed", [p]); 960 } else { 961 return false; 962 } 963 } 964 if (index >= length) { 965 obj.length = index + 1; 966 } 967 if (emit_splice) { 968 EndPerformSplice(obj); 969 EnqueueSpliceRecord(obj, length, [], index + 1 - length); 970 } 971 return true; 972 } 973 974 // Step 5 - Fallback to default implementation. 975 return DefineObjectProperty(obj, p, desc, should_throw); 976 } 977 978 979 // ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies. 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", UNDEFINED); 1049 return ToNameArray(names, "getOwnPropertyNames", false); 1050 } 1051 1052 var nameArrays = new InternalArray(); 1053 1054 // Find all the indexed properties. 1055 1056 // Get the local element names. 1057 var localElementNames = %GetLocalElementNames(obj); 1058 for (var i = 0; i < localElementNames.length; ++i) { 1059 localElementNames[i] = %_NumberToString(localElementNames[i]); 1060 } 1061 nameArrays.push(localElementNames); 1062 1063 // Get names for indexed interceptor properties. 1064 var interceptorInfo = %GetInterceptorInfo(obj); 1065 if ((interceptorInfo & 1) != 0) { 1066 var indexedInterceptorNames = %GetIndexedInterceptorElementNames(obj); 1067 if (!IS_UNDEFINED(indexedInterceptorNames)) { 1068 nameArrays.push(indexedInterceptorNames); 1069 } 1070 } 1071 1072 // Find all the named properties. 1073 1074 // Get the local property names. 1075 nameArrays.push(%GetLocalPropertyNames(obj, false)); 1076 1077 // Get names for named interceptor properties if any. 1078 if ((interceptorInfo & 2) != 0) { 1079 var namedInterceptorNames = %GetNamedInterceptorPropertyNames(obj); 1080 if (!IS_UNDEFINED(namedInterceptorNames)) { 1081 nameArrays.push(namedInterceptorNames); 1082 } 1083 } 1084 1085 var propertyNames = 1086 %Apply(InternalArray.prototype.concat, 1087 nameArrays[0], nameArrays, 1, nameArrays.length - 1); 1088 1089 // Property names are expected to be unique strings, 1090 // but interceptors can interfere with that assumption. 1091 if (interceptorInfo != 0) { 1092 var propertySet = { __proto__: null }; 1093 var j = 0; 1094 for (var i = 0; i < propertyNames.length; ++i) { 1095 if (IS_SYMBOL(propertyNames[i])) continue; 1096 var name = ToString(propertyNames[i]); 1097 // We need to check for the exact property value since for intrinsic 1098 // properties like toString if(propertySet["toString"]) will always 1099 // succeed. 1100 if (propertySet[name] === true) { 1101 continue; 1102 } 1103 propertySet[name] = true; 1104 propertyNames[j++] = name; 1105 } 1106 propertyNames.length = j; 1107 } 1108 1109 return propertyNames; 1110 } 1111 1112 1113 // ES5 section 15.2.3.5. 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", UNDEFINED); 1198 if (IS_UNDEFINED(props)) { 1199 throw MakeTypeError("handler_returned_undefined", [handler, "fix"]); 1200 } 1201 1202 if (%IsJSFunctionProxy(obj)) { 1203 var callTrap = %GetCallTrap(obj); 1204 var constructTrap = %GetConstructTrap(obj); 1205 var code = DelegateCallAndConstruct(callTrap, constructTrap); 1206 %Fix(obj); // becomes a regular function 1207 %SetCode(obj, code); 1208 // TODO(rossberg): What about length and other properties? Not specified. 1209 // We just put in some half-reasonable defaults for now. 1210 var prototype = new $Object(); 1211 $Object.defineProperty(prototype, "constructor", 1212 {value: obj, writable: true, enumerable: false, configurable: true}); 1213 // TODO(v8:1530): defineProperty does not handle prototype and length. 1214 %FunctionSetPrototype(obj, prototype); 1215 obj.length = 0; 1216 } else { 1217 %Fix(obj); 1218 } 1219 ObjectDefineProperties(obj, props); 1220 } 1221 1222 1223 // ES5 section 15.2.3.8. 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) || %IsObserved(obj)) { 1252 if (isProxy) { 1253 ProxyFix(obj); 1254 } 1255 var names = ObjectGetOwnPropertyNames(obj); 1256 for (var i = 0; i < names.length; i++) { 1257 var name = names[i]; 1258 var desc = GetOwnProperty(obj, name); 1259 if (desc.isWritable() || desc.isConfigurable()) { 1260 if (IsDataDescriptor(desc)) desc.setWritable(false); 1261 desc.setConfigurable(false); 1262 DefineOwnProperty(obj, name, desc, true); 1263 } 1264 } 1265 %PreventExtensions(obj); 1266 } else { 1267 // TODO(adamk): Is it worth going to this fast path if the 1268 // object's properties are already in dictionary mode? 1269 %ObjectFreeze(obj); 1270 } 1271 return obj; 1272 } 1273 1274 1275 // ES5 section 15.2.3.10 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 == INFINITY) return "Infinity"; 1564 if (x == -INFINITY) return "-Infinity"; 1565 1566 return %NumberToFixed(x, f); 1567 } 1568 1569 1570 // ECMA-262 section 15.7.4.6 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) ? UNDEFINED : TO_INTEGER(fractionDigits); 1582 1583 if (NUMBER_IS_NAN(x)) return "NaN"; 1584 if (x == INFINITY) return "Infinity"; 1585 if (x == -INFINITY) return "-Infinity"; 1586 1587 if (IS_UNDEFINED(f)) { 1588 f = -1; // Signal for runtime function that f is not defined. 1589 } else if (f < 0 || f > 20) { 1590 throw new $RangeError("toExponential() argument must be between 0 and 20"); 1591 } 1592 return %NumberToExponential(x, f); 1593 } 1594 1595 1596 // ECMA-262 section 15.7.4.7 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 == INFINITY) return "Infinity"; 1612 if (x == -INFINITY) return "-Infinity"; 1613 1614 if (p < 1 || p > 21) { 1615 throw new $RangeError("toPrecision() argument must be between 1 and 21"); 1616 } 1617 return %NumberToPrecision(x, p); 1618 } 1619 1620 1621 // Harmony isFinite. 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 -INFINITY, 1663 DONT_ENUM | DONT_DELETE | READ_ONLY); 1664 1665 // ECMA-262 section 15.7.3.5. 1666 %SetProperty($Number, 1667 "POSITIVE_INFINITY", 1668 INFINITY, 1669 DONT_ENUM | DONT_DELETE | READ_ONLY); 1670 %ToFastProperties($Number); 1671 1672 // Set up non-enumerable functions on the Number prototype object. 1673 InstallFunctions($Number.prototype, DONT_ENUM, $Array( 1674 "toString", NumberToString, 1675 "toLocaleString", NumberToLocaleString, 1676 "valueOf", NumberValueOf, 1677 "toFixed", NumberToFixed, 1678 "toExponential", NumberToExponential, 1679 "toPrecision", NumberToPrecision 1680 )); 1681 InstallFunctions($Number, DONT_ENUM, $Array( 1682 "isFinite", NumberIsFinite, 1683 "isNaN", NumberIsNaN 1684 )); 1685 } 1686 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 1840 1841 //---------------------------------------------------------------------------- 1842 1843 // TODO(rossberg): very simple abstraction for generic microtask queue. 1844 // Eventually, we should move to a real event queue that allows to maintain 1845 // relative ordering of different kinds of tasks. 1846 1847 RunMicrotasks.runners = new InternalArray; 1848 1849 function RunMicrotasks() { 1850 while (%SetMicrotaskPending(false)) { 1851 for (var i in RunMicrotasks.runners) RunMicrotasks.runners[i](); 1852 } 1853 } 1854