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