1 // Copyright 2006-2008 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 // This file relies on the fact that the following declarations have been made 29 // 30 // in runtime.js: 31 // const $Object = global.Object; 32 // const $Boolean = global.Boolean; 33 // const $Number = global.Number; 34 // const $Function = global.Function; 35 // const $Array = global.Array; 36 // const $NaN = 0/0; 37 // 38 // in math.js: 39 // const $floor = MathFloor 40 41 const $isNaN = GlobalIsNaN; 42 const $isFinite = GlobalIsFinite; 43 44 45 // ---------------------------------------------------------------------------- 46 47 48 // Helper function used to install functions on objects. 49 function InstallFunctions(object, attributes, functions) { 50 if (functions.length >= 8) { 51 %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1); 52 } 53 for (var i = 0; i < functions.length; i += 2) { 54 var key = functions[i]; 55 var f = functions[i + 1]; 56 %FunctionSetName(f, key); 57 %FunctionRemovePrototype(f); 58 %SetProperty(object, key, f, attributes); 59 } 60 %ToFastProperties(object); 61 } 62 63 // Emulates JSC by installing functions on a hidden prototype that 64 // lies above the current object/prototype. This lets you override 65 // functions on String.prototype etc. and then restore the old function 66 // with delete. See http://code.google.com/p/chromium/issues/detail?id=1717 67 function InstallFunctionsOnHiddenPrototype(object, attributes, functions) { 68 var hidden_prototype = new $Object(); 69 %SetHiddenPrototype(object, hidden_prototype); 70 InstallFunctions(hidden_prototype, attributes, functions); 71 } 72 73 74 // ---------------------------------------------------------------------------- 75 76 77 // ECMA 262 - 15.1.4 78 function GlobalIsNaN(number) { 79 var n = ToNumber(number); 80 return NUMBER_IS_NAN(n); 81 } 82 83 84 // ECMA 262 - 15.1.5 85 function GlobalIsFinite(number) { 86 if (!IS_NUMBER(number)) number = NonNumberToNumber(number); 87 88 // NaN - NaN == NaN, Infinity - Infinity == NaN, -Infinity - -Infinity == NaN. 89 return %_IsSmi(number) || number - number == 0; 90 } 91 92 93 // ECMA-262 - 15.1.2.2 94 function GlobalParseInt(string, radix) { 95 if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) { 96 // Some people use parseInt instead of Math.floor. This 97 // optimization makes parseInt on a Smi 12 times faster (60ns 98 // vs 800ns). The following optimization makes parseInt on a 99 // non-Smi number 9 times faster (230ns vs 2070ns). Together 100 // they make parseInt on a string 1.4% slower (274ns vs 270ns). 101 if (%_IsSmi(string)) return string; 102 if (IS_NUMBER(string) && 103 ((0.01 < string && string < 1e9) || 104 (-1e9 < string && string < -0.01))) { 105 // Truncate number. 106 return string | 0; 107 } 108 if (IS_UNDEFINED(radix)) radix = 0; 109 } else { 110 radix = TO_INT32(radix); 111 if (!(radix == 0 || (2 <= radix && radix <= 36))) 112 return $NaN; 113 } 114 string = TO_STRING_INLINE(string); 115 if (%_HasCachedArrayIndex(string) && 116 (radix == 0 || radix == 10)) { 117 return %_GetCachedArrayIndex(string); 118 } 119 return %StringParseInt(string, radix); 120 } 121 122 123 // ECMA-262 - 15.1.2.3 124 function GlobalParseFloat(string) { 125 string = TO_STRING_INLINE(string); 126 if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string); 127 return %StringParseFloat(string); 128 } 129 130 131 function GlobalEval(x) { 132 if (!IS_STRING(x)) return x; 133 134 var global_receiver = %GlobalReceiver(global); 135 var this_is_global_receiver = (this === global_receiver); 136 var global_is_detached = (global === global_receiver); 137 138 if (!this_is_global_receiver || global_is_detached) { 139 throw new $EvalError('The "this" object passed to eval must ' + 140 'be the global object from which eval originated'); 141 } 142 143 var f = %CompileString(x); 144 if (!IS_FUNCTION(f)) return f; 145 146 return %_CallFunction(this, f); 147 } 148 149 150 // ---------------------------------------------------------------------------- 151 152 153 function SetupGlobal() { 154 // ECMA 262 - 15.1.1.1. 155 %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE); 156 157 // ECMA-262 - 15.1.1.2. 158 %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE); 159 160 // ECMA-262 - 15.1.1.3. 161 %SetProperty(global, "undefined", void 0, DONT_ENUM | DONT_DELETE); 162 163 // Setup non-enumerable function on the global object. 164 InstallFunctions(global, DONT_ENUM, $Array( 165 "isNaN", GlobalIsNaN, 166 "isFinite", GlobalIsFinite, 167 "parseInt", GlobalParseInt, 168 "parseFloat", GlobalParseFloat, 169 "eval", GlobalEval 170 )); 171 } 172 173 SetupGlobal(); 174 175 176 // ---------------------------------------------------------------------------- 177 // Boolean (first part of definition) 178 179 180 %SetCode($Boolean, function(x) { 181 if (%_IsConstructCall()) { 182 %_SetValueOf(this, ToBoolean(x)); 183 } else { 184 return ToBoolean(x); 185 } 186 }); 187 188 %FunctionSetPrototype($Boolean, new $Boolean(false)); 189 190 %SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM); 191 192 // ---------------------------------------------------------------------------- 193 // Object 194 195 $Object.prototype.constructor = $Object; 196 197 // ECMA-262 - 15.2.4.2 198 function ObjectToString() { 199 return "[object " + %_ClassOf(ToObject(this)) + "]"; 200 } 201 202 203 // ECMA-262 - 15.2.4.3 204 function ObjectToLocaleString() { 205 return this.toString(); 206 } 207 208 209 // ECMA-262 - 15.2.4.4 210 function ObjectValueOf() { 211 return ToObject(this); 212 } 213 214 215 // ECMA-262 - 15.2.4.5 216 function ObjectHasOwnProperty(V) { 217 return %HasLocalProperty(ToObject(this), ToString(V)); 218 } 219 220 221 // ECMA-262 - 15.2.4.6 222 function ObjectIsPrototypeOf(V) { 223 if (!IS_SPEC_OBJECT(V)) return false; 224 return %IsInPrototypeChain(this, V); 225 } 226 227 228 // ECMA-262 - 15.2.4.6 229 function ObjectPropertyIsEnumerable(V) { 230 return %IsPropertyEnumerable(ToObject(this), ToString(V)); 231 } 232 233 234 // Extensions for providing property getters and setters. 235 function ObjectDefineGetter(name, fun) { 236 if (this == null && !IS_UNDETECTABLE(this)) { 237 throw new $TypeError('Object.prototype.__defineGetter__: this is Null'); 238 } 239 if (!IS_FUNCTION(fun)) { 240 throw new $TypeError('Object.prototype.__defineGetter__: Expecting function'); 241 } 242 var desc = new PropertyDescriptor(); 243 desc.setGet(fun); 244 desc.setEnumerable(true); 245 desc.setConfigurable(true); 246 DefineOwnProperty(ToObject(this), ToString(name), desc, false); 247 } 248 249 250 function ObjectLookupGetter(name) { 251 if (this == null && !IS_UNDETECTABLE(this)) { 252 throw new $TypeError('Object.prototype.__lookupGetter__: this is Null'); 253 } 254 return %LookupAccessor(ToObject(this), ToString(name), GETTER); 255 } 256 257 258 function ObjectDefineSetter(name, fun) { 259 if (this == null && !IS_UNDETECTABLE(this)) { 260 throw new $TypeError('Object.prototype.__defineSetter__: this is Null'); 261 } 262 if (!IS_FUNCTION(fun)) { 263 throw new $TypeError( 264 'Object.prototype.__defineSetter__: Expecting function'); 265 } 266 var desc = new PropertyDescriptor(); 267 desc.setSet(fun); 268 desc.setEnumerable(true); 269 desc.setConfigurable(true); 270 DefineOwnProperty(ToObject(this), ToString(name), desc, false); 271 } 272 273 274 function ObjectLookupSetter(name) { 275 if (this == null && !IS_UNDETECTABLE(this)) { 276 throw new $TypeError('Object.prototype.__lookupSetter__: this is Null'); 277 } 278 return %LookupAccessor(ToObject(this), ToString(name), SETTER); 279 } 280 281 282 function ObjectKeys(obj) { 283 if (!IS_SPEC_OBJECT(obj)) 284 throw MakeTypeError("obj_ctor_property_non_object", ["keys"]); 285 return %LocalKeys(obj); 286 } 287 288 289 // ES5 8.10.1. 290 function IsAccessorDescriptor(desc) { 291 if (IS_UNDEFINED(desc)) return false; 292 return desc.hasGetter_ || desc.hasSetter_; 293 } 294 295 296 // ES5 8.10.2. 297 function IsDataDescriptor(desc) { 298 if (IS_UNDEFINED(desc)) return false; 299 return desc.hasValue_ || desc.hasWritable_; 300 } 301 302 303 // ES5 8.10.3. 304 function IsGenericDescriptor(desc) { 305 return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc)); 306 } 307 308 309 function IsInconsistentDescriptor(desc) { 310 return IsAccessorDescriptor(desc) && IsDataDescriptor(desc); 311 } 312 313 // ES5 8.10.4 314 function FromPropertyDescriptor(desc) { 315 if (IS_UNDEFINED(desc)) return desc; 316 var obj = new $Object(); 317 if (IsDataDescriptor(desc)) { 318 obj.value = desc.getValue(); 319 obj.writable = desc.isWritable(); 320 } 321 if (IsAccessorDescriptor(desc)) { 322 obj.get = desc.getGet(); 323 obj.set = desc.getSet(); 324 } 325 obj.enumerable = desc.isEnumerable(); 326 obj.configurable = desc.isConfigurable(); 327 return obj; 328 } 329 330 // ES5 8.10.5. 331 function ToPropertyDescriptor(obj) { 332 if (!IS_SPEC_OBJECT(obj)) { 333 throw MakeTypeError("property_desc_object", [obj]); 334 } 335 var desc = new PropertyDescriptor(); 336 337 if ("enumerable" in obj) { 338 desc.setEnumerable(ToBoolean(obj.enumerable)); 339 } 340 341 if ("configurable" in obj) { 342 desc.setConfigurable(ToBoolean(obj.configurable)); 343 } 344 345 if ("value" in obj) { 346 desc.setValue(obj.value); 347 } 348 349 if ("writable" in obj) { 350 desc.setWritable(ToBoolean(obj.writable)); 351 } 352 353 if ("get" in obj) { 354 var get = obj.get; 355 if (!IS_UNDEFINED(get) && !IS_FUNCTION(get)) { 356 throw MakeTypeError("getter_must_be_callable", [get]); 357 } 358 desc.setGet(get); 359 } 360 361 if ("set" in obj) { 362 var set = obj.set; 363 if (!IS_UNDEFINED(set) && !IS_FUNCTION(set)) { 364 throw MakeTypeError("setter_must_be_callable", [set]); 365 } 366 desc.setSet(set); 367 } 368 369 if (IsInconsistentDescriptor(desc)) { 370 throw MakeTypeError("value_and_accessor", [obj]); 371 } 372 return desc; 373 } 374 375 376 function PropertyDescriptor() { 377 // Initialize here so they are all in-object and have the same map. 378 // Default values from ES5 8.6.1. 379 this.value_ = void 0; 380 this.hasValue_ = false; 381 this.writable_ = false; 382 this.hasWritable_ = false; 383 this.enumerable_ = false; 384 this.hasEnumerable_ = false; 385 this.configurable_ = false; 386 this.hasConfigurable_ = false; 387 this.get_ = void 0; 388 this.hasGetter_ = false; 389 this.set_ = void 0; 390 this.hasSetter_ = false; 391 } 392 393 PropertyDescriptor.prototype.__proto__ = null; 394 PropertyDescriptor.prototype.toString = function() { 395 return "[object PropertyDescriptor]"; 396 }; 397 398 PropertyDescriptor.prototype.setValue = function(value) { 399 this.value_ = value; 400 this.hasValue_ = true; 401 } 402 403 404 PropertyDescriptor.prototype.getValue = function() { 405 return this.value_; 406 } 407 408 409 PropertyDescriptor.prototype.hasValue = function() { 410 return this.hasValue_; 411 } 412 413 414 PropertyDescriptor.prototype.setEnumerable = function(enumerable) { 415 this.enumerable_ = enumerable; 416 this.hasEnumerable_ = true; 417 } 418 419 420 PropertyDescriptor.prototype.isEnumerable = function () { 421 return this.enumerable_; 422 } 423 424 425 PropertyDescriptor.prototype.hasEnumerable = function() { 426 return this.hasEnumerable_; 427 } 428 429 430 PropertyDescriptor.prototype.setWritable = function(writable) { 431 this.writable_ = writable; 432 this.hasWritable_ = true; 433 } 434 435 436 PropertyDescriptor.prototype.isWritable = function() { 437 return this.writable_; 438 } 439 440 441 PropertyDescriptor.prototype.hasWritable = function() { 442 return this.hasWritable_; 443 } 444 445 446 PropertyDescriptor.prototype.setConfigurable = function(configurable) { 447 this.configurable_ = configurable; 448 this.hasConfigurable_ = true; 449 } 450 451 452 PropertyDescriptor.prototype.hasConfigurable = function() { 453 return this.hasConfigurable_; 454 } 455 456 457 PropertyDescriptor.prototype.isConfigurable = function() { 458 return this.configurable_; 459 } 460 461 462 PropertyDescriptor.prototype.setGet = function(get) { 463 this.get_ = get; 464 this.hasGetter_ = true; 465 } 466 467 468 PropertyDescriptor.prototype.getGet = function() { 469 return this.get_; 470 } 471 472 473 PropertyDescriptor.prototype.hasGetter = function() { 474 return this.hasGetter_; 475 } 476 477 478 PropertyDescriptor.prototype.setSet = function(set) { 479 this.set_ = set; 480 this.hasSetter_ = true; 481 } 482 483 484 PropertyDescriptor.prototype.getSet = function() { 485 return this.set_; 486 } 487 488 489 PropertyDescriptor.prototype.hasSetter = function() { 490 return this.hasSetter_; 491 } 492 493 494 // Converts an array returned from Runtime_GetOwnProperty to an actual 495 // property descriptor. For a description of the array layout please 496 // see the runtime.cc file. 497 function ConvertDescriptorArrayToDescriptor(desc_array) { 498 if (desc_array === false) { 499 throw 'Internal error: invalid desc_array'; 500 } 501 502 if (IS_UNDEFINED(desc_array)) { 503 return void 0; 504 } 505 506 var desc = new PropertyDescriptor(); 507 // This is an accessor. 508 if (desc_array[IS_ACCESSOR_INDEX]) { 509 desc.setGet(desc_array[GETTER_INDEX]); 510 desc.setSet(desc_array[SETTER_INDEX]); 511 } else { 512 desc.setValue(desc_array[VALUE_INDEX]); 513 desc.setWritable(desc_array[WRITABLE_INDEX]); 514 } 515 desc.setEnumerable(desc_array[ENUMERABLE_INDEX]); 516 desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]); 517 518 return desc; 519 } 520 521 522 // ES5 section 8.12.2. 523 function GetProperty(obj, p) { 524 var prop = GetOwnProperty(obj); 525 if (!IS_UNDEFINED(prop)) return prop; 526 var proto = obj.__proto__; 527 if (IS_NULL(proto)) return void 0; 528 return GetProperty(proto, p); 529 } 530 531 532 // ES5 section 8.12.6 533 function HasProperty(obj, p) { 534 var desc = GetProperty(obj, p); 535 return IS_UNDEFINED(desc) ? false : true; 536 } 537 538 539 // ES5 section 8.12.1. 540 function GetOwnProperty(obj, p) { 541 // GetOwnProperty returns an array indexed by the constants 542 // defined in macros.py. 543 // If p is not a property on obj undefined is returned. 544 var props = %GetOwnProperty(ToObject(obj), ToString(p)); 545 546 // A false value here means that access checks failed. 547 if (props === false) return void 0; 548 549 return ConvertDescriptorArrayToDescriptor(props); 550 } 551 552 553 // ES5 8.12.9. 554 function DefineOwnProperty(obj, p, desc, should_throw) { 555 var current_or_access = %GetOwnProperty(ToObject(obj), ToString(p)); 556 // A false value here means that access checks failed. 557 if (current_or_access === false) return void 0; 558 559 var current = ConvertDescriptorArrayToDescriptor(current_or_access); 560 var extensible = %IsExtensible(ToObject(obj)); 561 562 // Error handling according to spec. 563 // Step 3 564 if (IS_UNDEFINED(current) && !extensible) { 565 if (should_throw) { 566 throw MakeTypeError("define_disallowed", ["defineProperty"]); 567 } else { 568 return; 569 } 570 } 571 572 if (!IS_UNDEFINED(current)) { 573 // Step 5 and 6 574 if ((IsGenericDescriptor(desc) || 575 IsDataDescriptor(desc) == IsDataDescriptor(current)) && 576 (!desc.hasEnumerable() || 577 SameValue(desc.isEnumerable(), current.isEnumerable())) && 578 (!desc.hasConfigurable() || 579 SameValue(desc.isConfigurable(), current.isConfigurable())) && 580 (!desc.hasWritable() || 581 SameValue(desc.isWritable(), current.isWritable())) && 582 (!desc.hasValue() || 583 SameValue(desc.getValue(), current.getValue())) && 584 (!desc.hasGetter() || 585 SameValue(desc.getGet(), current.getGet())) && 586 (!desc.hasSetter() || 587 SameValue(desc.getSet(), current.getSet()))) { 588 return true; 589 } 590 if (!current.isConfigurable()) { 591 // Step 7 592 if (desc.isConfigurable() || 593 (desc.hasEnumerable() && 594 desc.isEnumerable() != current.isEnumerable())) { 595 if (should_throw) { 596 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 597 } else { 598 return; 599 } 600 } 601 // Step 8 602 if (!IsGenericDescriptor(desc)) { 603 // Step 9a 604 if (IsDataDescriptor(current) != IsDataDescriptor(desc)) { 605 if (should_throw) { 606 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 607 } else { 608 return; 609 } 610 } 611 // Step 10a 612 if (IsDataDescriptor(current) && IsDataDescriptor(desc)) { 613 if (!current.isWritable() && desc.isWritable()) { 614 if (should_throw) { 615 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 616 } else { 617 return; 618 } 619 } 620 if (!current.isWritable() && desc.hasValue() && 621 !SameValue(desc.getValue(), current.getValue())) { 622 if (should_throw) { 623 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 624 } else { 625 return; 626 } 627 } 628 } 629 // Step 11 630 if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) { 631 if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) { 632 if (should_throw) { 633 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 634 } else { 635 return; 636 } 637 } 638 if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) { 639 if (should_throw) { 640 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 641 } else { 642 return; 643 } 644 } 645 } 646 } 647 } 648 } 649 650 // Send flags - enumerable and configurable are common - writable is 651 // only send to the data descriptor. 652 // Take special care if enumerable and configurable is not defined on 653 // desc (we need to preserve the existing values from current). 654 var flag = NONE; 655 if (desc.hasEnumerable()) { 656 flag |= desc.isEnumerable() ? 0 : DONT_ENUM; 657 } else if (!IS_UNDEFINED(current)) { 658 flag |= current.isEnumerable() ? 0 : DONT_ENUM; 659 } else { 660 flag |= DONT_ENUM; 661 } 662 663 if (desc.hasConfigurable()) { 664 flag |= desc.isConfigurable() ? 0 : DONT_DELETE; 665 } else if (!IS_UNDEFINED(current)) { 666 flag |= current.isConfigurable() ? 0 : DONT_DELETE; 667 } else 668 flag |= DONT_DELETE; 669 670 if (IsDataDescriptor(desc) || 671 (IsGenericDescriptor(desc) && 672 (IS_UNDEFINED(current) || IsDataDescriptor(current)))) { 673 // There are 3 cases that lead here: 674 // Step 4a - defining a new data property. 675 // Steps 9b & 12 - replacing an existing accessor property with a data 676 // property. 677 // Step 12 - updating an existing data property with a data or generic 678 // descriptor. 679 680 if (desc.hasWritable()) { 681 flag |= desc.isWritable() ? 0 : READ_ONLY; 682 } else if (!IS_UNDEFINED(current)) { 683 flag |= current.isWritable() ? 0 : READ_ONLY; 684 } else { 685 flag |= READ_ONLY; 686 } 687 688 var value = void 0; // Default value is undefined. 689 if (desc.hasValue()) { 690 value = desc.getValue(); 691 } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) { 692 value = current.getValue(); 693 } 694 695 %DefineOrRedefineDataProperty(obj, p, value, flag); 696 } else if (IsGenericDescriptor(desc)) { 697 // Step 12 - updating an existing accessor property with generic 698 // descriptor. Changing flags only. 699 %DefineOrRedefineAccessorProperty(obj, p, GETTER, current.getGet(), flag); 700 } else { 701 // There are 3 cases that lead here: 702 // Step 4b - defining a new accessor property. 703 // Steps 9c & 12 - replacing an existing data property with an accessor 704 // property. 705 // Step 12 - updating an existing accessor property with an accessor 706 // descriptor. 707 if (desc.hasGetter()) { 708 %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag); 709 } 710 if (desc.hasSetter()) { 711 %DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag); 712 } 713 } 714 return true; 715 } 716 717 718 // ES5 section 15.2.3.2. 719 function ObjectGetPrototypeOf(obj) { 720 if (!IS_SPEC_OBJECT(obj)) 721 throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]); 722 return obj.__proto__; 723 } 724 725 726 // ES5 section 15.2.3.3 727 function ObjectGetOwnPropertyDescriptor(obj, p) { 728 if (!IS_SPEC_OBJECT(obj)) 729 throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]); 730 var desc = GetOwnProperty(obj, p); 731 return FromPropertyDescriptor(desc); 732 } 733 734 735 // ES5 section 15.2.3.4. 736 function ObjectGetOwnPropertyNames(obj) { 737 if (!IS_SPEC_OBJECT(obj)) 738 throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]); 739 740 // Find all the indexed properties. 741 742 // Get the local element names. 743 var propertyNames = %GetLocalElementNames(obj); 744 745 // Get names for indexed interceptor properties. 746 if (%GetInterceptorInfo(obj) & 1) { 747 var indexedInterceptorNames = 748 %GetIndexedInterceptorElementNames(obj); 749 if (indexedInterceptorNames) 750 propertyNames = propertyNames.concat(indexedInterceptorNames); 751 } 752 753 // Find all the named properties. 754 755 // Get the local property names. 756 propertyNames = propertyNames.concat(%GetLocalPropertyNames(obj)); 757 758 // Get names for named interceptor properties if any. 759 760 if (%GetInterceptorInfo(obj) & 2) { 761 var namedInterceptorNames = 762 %GetNamedInterceptorPropertyNames(obj); 763 if (namedInterceptorNames) { 764 propertyNames = propertyNames.concat(namedInterceptorNames); 765 } 766 } 767 768 // Property names are expected to be unique strings. 769 var propertySet = {}; 770 var j = 0; 771 for (var i = 0; i < propertyNames.length; ++i) { 772 var name = ToString(propertyNames[i]); 773 // We need to check for the exact property value since for intrinsic 774 // properties like toString if(propertySet["toString"]) will always 775 // succeed. 776 if (propertySet[name] === true) 777 continue; 778 propertySet[name] = true; 779 propertyNames[j++] = name; 780 } 781 propertyNames.length = j; 782 783 return propertyNames; 784 } 785 786 787 // ES5 section 15.2.3.5. 788 function ObjectCreate(proto, properties) { 789 if (!IS_SPEC_OBJECT(proto) && proto !== null) { 790 throw MakeTypeError("proto_object_or_null", [proto]); 791 } 792 var obj = new $Object(); 793 obj.__proto__ = proto; 794 if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties); 795 return obj; 796 } 797 798 799 // ES5 section 15.2.3.6. 800 function ObjectDefineProperty(obj, p, attributes) { 801 if (!IS_SPEC_OBJECT(obj)) { 802 throw MakeTypeError("obj_ctor_property_non_object", ["defineProperty"]); 803 } 804 var name = ToString(p); 805 var desc = ToPropertyDescriptor(attributes); 806 DefineOwnProperty(obj, name, desc, true); 807 return obj; 808 } 809 810 811 // ES5 section 15.2.3.7. 812 function ObjectDefineProperties(obj, properties) { 813 if (!IS_SPEC_OBJECT(obj)) 814 throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]); 815 var props = ToObject(properties); 816 var key_values = []; 817 for (var key in props) { 818 if (%HasLocalProperty(props, key)) { 819 key_values.push(key); 820 var value = props[key]; 821 var desc = ToPropertyDescriptor(value); 822 key_values.push(desc); 823 } 824 } 825 for (var i = 0; i < key_values.length; i += 2) { 826 var key = key_values[i]; 827 var desc = key_values[i + 1]; 828 DefineOwnProperty(obj, key, desc, true); 829 } 830 return obj; 831 } 832 833 834 // ES5 section 15.2.3.8. 835 function ObjectSeal(obj) { 836 if (!IS_SPEC_OBJECT(obj)) { 837 throw MakeTypeError("obj_ctor_property_non_object", ["seal"]); 838 } 839 var names = ObjectGetOwnPropertyNames(obj); 840 for (var i = 0; i < names.length; i++) { 841 var name = names[i]; 842 var desc = GetOwnProperty(obj, name); 843 if (desc.isConfigurable()) desc.setConfigurable(false); 844 DefineOwnProperty(obj, name, desc, true); 845 } 846 return ObjectPreventExtension(obj); 847 } 848 849 850 // ES5 section 15.2.3.9. 851 function ObjectFreeze(obj) { 852 if (!IS_SPEC_OBJECT(obj)) { 853 throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]); 854 } 855 var names = ObjectGetOwnPropertyNames(obj); 856 for (var i = 0; i < names.length; i++) { 857 var name = names[i]; 858 var desc = GetOwnProperty(obj, name); 859 if (IsDataDescriptor(desc)) desc.setWritable(false); 860 if (desc.isConfigurable()) desc.setConfigurable(false); 861 DefineOwnProperty(obj, name, desc, true); 862 } 863 return ObjectPreventExtension(obj); 864 } 865 866 867 // ES5 section 15.2.3.10 868 function ObjectPreventExtension(obj) { 869 if (!IS_SPEC_OBJECT(obj)) { 870 throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]); 871 } 872 %PreventExtensions(obj); 873 return obj; 874 } 875 876 877 // ES5 section 15.2.3.11 878 function ObjectIsSealed(obj) { 879 if (!IS_SPEC_OBJECT(obj)) { 880 throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]); 881 } 882 var names = ObjectGetOwnPropertyNames(obj); 883 for (var i = 0; i < names.length; i++) { 884 var name = names[i]; 885 var desc = GetOwnProperty(obj, name); 886 if (desc.isConfigurable()) return false; 887 } 888 if (!ObjectIsExtensible(obj)) { 889 return true; 890 } 891 return false; 892 } 893 894 895 // ES5 section 15.2.3.12 896 function ObjectIsFrozen(obj) { 897 if (!IS_SPEC_OBJECT(obj)) { 898 throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]); 899 } 900 var names = ObjectGetOwnPropertyNames(obj); 901 for (var i = 0; i < names.length; i++) { 902 var name = names[i]; 903 var desc = GetOwnProperty(obj, name); 904 if (IsDataDescriptor(desc) && desc.isWritable()) return false; 905 if (desc.isConfigurable()) return false; 906 } 907 if (!ObjectIsExtensible(obj)) { 908 return true; 909 } 910 return false; 911 } 912 913 914 // ES5 section 15.2.3.13 915 function ObjectIsExtensible(obj) { 916 if (!IS_SPEC_OBJECT(obj)) { 917 throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]); 918 } 919 return %IsExtensible(obj); 920 } 921 922 923 %SetCode($Object, function(x) { 924 if (%_IsConstructCall()) { 925 if (x == null) return this; 926 return ToObject(x); 927 } else { 928 if (x == null) return { }; 929 return ToObject(x); 930 } 931 }); 932 933 %SetExpectedNumberOfProperties($Object, 4); 934 935 // ---------------------------------------------------------------------------- 936 937 938 function SetupObject() { 939 // Setup non-enumerable functions on the Object.prototype object. 940 InstallFunctions($Object.prototype, DONT_ENUM, $Array( 941 "toString", ObjectToString, 942 "toLocaleString", ObjectToLocaleString, 943 "valueOf", ObjectValueOf, 944 "hasOwnProperty", ObjectHasOwnProperty, 945 "isPrototypeOf", ObjectIsPrototypeOf, 946 "propertyIsEnumerable", ObjectPropertyIsEnumerable, 947 "__defineGetter__", ObjectDefineGetter, 948 "__lookupGetter__", ObjectLookupGetter, 949 "__defineSetter__", ObjectDefineSetter, 950 "__lookupSetter__", ObjectLookupSetter 951 )); 952 InstallFunctions($Object, DONT_ENUM, $Array( 953 "keys", ObjectKeys, 954 "create", ObjectCreate, 955 "defineProperty", ObjectDefineProperty, 956 "defineProperties", ObjectDefineProperties, 957 "freeze", ObjectFreeze, 958 "getPrototypeOf", ObjectGetPrototypeOf, 959 "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor, 960 "getOwnPropertyNames", ObjectGetOwnPropertyNames, 961 "isExtensible", ObjectIsExtensible, 962 "isFrozen", ObjectIsFrozen, 963 "isSealed", ObjectIsSealed, 964 "preventExtensions", ObjectPreventExtension, 965 "seal", ObjectSeal 966 )); 967 } 968 969 SetupObject(); 970 971 972 // ---------------------------------------------------------------------------- 973 // Boolean 974 975 function BooleanToString() { 976 // NOTE: Both Boolean objects and values can enter here as 977 // 'this'. This is not as dictated by ECMA-262. 978 var b = this; 979 if (!IS_BOOLEAN(b)) { 980 if (!IS_BOOLEAN_WRAPPER(b)) { 981 throw new $TypeError('Boolean.prototype.toString is not generic'); 982 } 983 b = %_ValueOf(b); 984 } 985 return b ? 'true' : 'false'; 986 } 987 988 989 function BooleanValueOf() { 990 // NOTE: Both Boolean objects and values can enter here as 991 // 'this'. This is not as dictated by ECMA-262. 992 if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) 993 throw new $TypeError('Boolean.prototype.valueOf is not generic'); 994 return %_ValueOf(this); 995 } 996 997 998 // ---------------------------------------------------------------------------- 999 1000 1001 function SetupBoolean() { 1002 InstallFunctions($Boolean.prototype, DONT_ENUM, $Array( 1003 "toString", BooleanToString, 1004 "valueOf", BooleanValueOf 1005 )); 1006 } 1007 1008 SetupBoolean(); 1009 1010 // ---------------------------------------------------------------------------- 1011 // Number 1012 1013 // Set the Number function and constructor. 1014 %SetCode($Number, function(x) { 1015 var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x); 1016 if (%_IsConstructCall()) { 1017 %_SetValueOf(this, value); 1018 } else { 1019 return value; 1020 } 1021 }); 1022 1023 %FunctionSetPrototype($Number, new $Number(0)); 1024 1025 // ECMA-262 section 15.7.4.2. 1026 function NumberToString(radix) { 1027 // NOTE: Both Number objects and values can enter here as 1028 // 'this'. This is not as dictated by ECMA-262. 1029 var number = this; 1030 if (!IS_NUMBER(this)) { 1031 if (!IS_NUMBER_WRAPPER(this)) 1032 throw new $TypeError('Number.prototype.toString is not generic'); 1033 // Get the value of this number in case it's an object. 1034 number = %_ValueOf(this); 1035 } 1036 // Fast case: Convert number in radix 10. 1037 if (IS_UNDEFINED(radix) || radix === 10) { 1038 return %_NumberToString(number); 1039 } 1040 1041 // Convert the radix to an integer and check the range. 1042 radix = TO_INTEGER(radix); 1043 if (radix < 2 || radix > 36) { 1044 throw new $RangeError('toString() radix argument must be between 2 and 36'); 1045 } 1046 // Convert the number to a string in the given radix. 1047 return %NumberToRadixString(number, radix); 1048 } 1049 1050 1051 // ECMA-262 section 15.7.4.3 1052 function NumberToLocaleString() { 1053 return this.toString(); 1054 } 1055 1056 1057 // ECMA-262 section 15.7.4.4 1058 function NumberValueOf() { 1059 // NOTE: Both Number objects and values can enter here as 1060 // 'this'. This is not as dictated by ECMA-262. 1061 if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) 1062 throw new $TypeError('Number.prototype.valueOf is not generic'); 1063 return %_ValueOf(this); 1064 } 1065 1066 1067 // ECMA-262 section 15.7.4.5 1068 function NumberToFixed(fractionDigits) { 1069 var f = TO_INTEGER(fractionDigits); 1070 if (f < 0 || f > 20) { 1071 throw new $RangeError("toFixed() digits argument must be between 0 and 20"); 1072 } 1073 var x = ToNumber(this); 1074 return %NumberToFixed(x, f); 1075 } 1076 1077 1078 // ECMA-262 section 15.7.4.6 1079 function NumberToExponential(fractionDigits) { 1080 var f = -1; 1081 if (!IS_UNDEFINED(fractionDigits)) { 1082 f = TO_INTEGER(fractionDigits); 1083 if (f < 0 || f > 20) { 1084 throw new $RangeError("toExponential() argument must be between 0 and 20"); 1085 } 1086 } 1087 var x = ToNumber(this); 1088 return %NumberToExponential(x, f); 1089 } 1090 1091 1092 // ECMA-262 section 15.7.4.7 1093 function NumberToPrecision(precision) { 1094 if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this)); 1095 var p = TO_INTEGER(precision); 1096 if (p < 1 || p > 21) { 1097 throw new $RangeError("toPrecision() argument must be between 1 and 21"); 1098 } 1099 var x = ToNumber(this); 1100 return %NumberToPrecision(x, p); 1101 } 1102 1103 1104 // ---------------------------------------------------------------------------- 1105 1106 function SetupNumber() { 1107 %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8); 1108 // Setup the constructor property on the Number prototype object. 1109 %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM); 1110 1111 %OptimizeObjectForAddingMultipleProperties($Number, 5); 1112 // ECMA-262 section 15.7.3.1. 1113 %SetProperty($Number, 1114 "MAX_VALUE", 1115 1.7976931348623157e+308, 1116 DONT_ENUM | DONT_DELETE | READ_ONLY); 1117 1118 // ECMA-262 section 15.7.3.2. 1119 %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY); 1120 1121 // ECMA-262 section 15.7.3.3. 1122 %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY); 1123 1124 // ECMA-262 section 15.7.3.4. 1125 %SetProperty($Number, 1126 "NEGATIVE_INFINITY", 1127 -1/0, 1128 DONT_ENUM | DONT_DELETE | READ_ONLY); 1129 1130 // ECMA-262 section 15.7.3.5. 1131 %SetProperty($Number, 1132 "POSITIVE_INFINITY", 1133 1/0, 1134 DONT_ENUM | DONT_DELETE | READ_ONLY); 1135 %ToFastProperties($Number); 1136 1137 // Setup non-enumerable functions on the Number prototype object. 1138 InstallFunctions($Number.prototype, DONT_ENUM, $Array( 1139 "toString", NumberToString, 1140 "toLocaleString", NumberToLocaleString, 1141 "valueOf", NumberValueOf, 1142 "toFixed", NumberToFixed, 1143 "toExponential", NumberToExponential, 1144 "toPrecision", NumberToPrecision 1145 )); 1146 } 1147 1148 SetupNumber(); 1149 1150 1151 // ---------------------------------------------------------------------------- 1152 // Function 1153 1154 $Function.prototype.constructor = $Function; 1155 1156 function FunctionSourceString(func) { 1157 if (!IS_FUNCTION(func)) { 1158 throw new $TypeError('Function.prototype.toString is not generic'); 1159 } 1160 1161 var source = %FunctionGetSourceCode(func); 1162 if (!IS_STRING(source) || %FunctionIsBuiltin(func)) { 1163 var name = %FunctionGetName(func); 1164 if (name) { 1165 // Mimic what KJS does. 1166 return 'function ' + name + '() { [native code] }'; 1167 } else { 1168 return 'function () { [native code] }'; 1169 } 1170 } 1171 1172 var name = %FunctionGetName(func); 1173 return 'function ' + name + source; 1174 } 1175 1176 1177 function FunctionToString() { 1178 return FunctionSourceString(this); 1179 } 1180 1181 1182 // ES5 15.3.4.5 1183 function FunctionBind(this_arg) { // Length is 1. 1184 if (!IS_FUNCTION(this)) { 1185 throw new $TypeError('Bind must be called on a function'); 1186 } 1187 // this_arg is not an argument that should be bound. 1188 var argc_bound = (%_ArgumentsLength() || 1) - 1; 1189 var fn = this; 1190 if (argc_bound == 0) { 1191 var result = function() { 1192 if (%_IsConstructCall()) { 1193 // %NewObjectFromBound implicitly uses arguments passed to this 1194 // function. We do not pass the arguments object explicitly to avoid 1195 // materializing it and guarantee that this function will be optimized. 1196 return %NewObjectFromBound(fn, null); 1197 } 1198 1199 return fn.apply(this_arg, arguments); 1200 }; 1201 } else { 1202 var bound_args = new InternalArray(argc_bound); 1203 for(var i = 0; i < argc_bound; i++) { 1204 bound_args[i] = %_Arguments(i+1); 1205 } 1206 1207 var result = function() { 1208 // If this is a construct call we use a special runtime method 1209 // to generate the actual object using the bound function. 1210 if (%_IsConstructCall()) { 1211 // %NewObjectFromBound implicitly uses arguments passed to this 1212 // function. We do not pass the arguments object explicitly to avoid 1213 // materializing it and guarantee that this function will be optimized. 1214 return %NewObjectFromBound(fn, bound_args); 1215 } 1216 1217 // Combine the args we got from the bind call with the args 1218 // given as argument to the invocation. 1219 var argc = %_ArgumentsLength(); 1220 var args = new InternalArray(argc + argc_bound); 1221 // Add bound arguments. 1222 for (var i = 0; i < argc_bound; i++) { 1223 args[i] = bound_args[i]; 1224 } 1225 // Add arguments from call. 1226 for (var i = 0; i < argc; i++) { 1227 args[argc_bound + i] = %_Arguments(i); 1228 } 1229 return fn.apply(this_arg, args); 1230 }; 1231 } 1232 1233 // We already have caller and arguments properties on functions, 1234 // which are non-configurable. It therefore makes no sence to 1235 // try to redefine these as defined by the spec. The spec says 1236 // that bind should make these throw a TypeError if get or set 1237 // is called and make them non-enumerable and non-configurable. 1238 // To be consistent with our normal functions we leave this as it is. 1239 1240 // Set the correct length. 1241 var length = (this.length - argc_bound) > 0 ? this.length - argc_bound : 0; 1242 %FunctionSetLength(result, length); 1243 1244 return result; 1245 } 1246 1247 1248 function NewFunction(arg1) { // length == 1 1249 var n = %_ArgumentsLength(); 1250 var p = ''; 1251 if (n > 1) { 1252 p = new InternalArray(n - 1); 1253 for (var i = 0; i < n - 1; i++) p[i] = %_Arguments(i); 1254 p = Join(p, n - 1, ',', NonStringToString); 1255 // If the formal parameters string include ) - an illegal 1256 // character - it may make the combined function expression 1257 // compile. We avoid this problem by checking for this early on. 1258 if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]); 1259 } 1260 var body = (n > 0) ? ToString(%_Arguments(n - 1)) : ''; 1261 var source = '(function(' + p + ') {\n' + body + '\n})'; 1262 1263 // The call to SetNewFunctionAttributes will ensure the prototype 1264 // property of the resulting function is enumerable (ECMA262, 15.3.5.2). 1265 var f = %CompileString(source)(); 1266 %FunctionSetName(f, "anonymous"); 1267 return %SetNewFunctionAttributes(f); 1268 } 1269 1270 %SetCode($Function, NewFunction); 1271 1272 // ---------------------------------------------------------------------------- 1273 1274 function SetupFunction() { 1275 InstallFunctions($Function.prototype, DONT_ENUM, $Array( 1276 "bind", FunctionBind, 1277 "toString", FunctionToString 1278 )); 1279 } 1280 1281 SetupFunction(); 1282