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