v8natives.js revision 3e5fa29ddb82551500b118e9bf37af3966277b70
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 = ToNumber(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)) { 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 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 f.call(this); 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 f.call(%GlobalReceiver(global)); 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 495// ES5 section 8.12.1. 496function GetOwnProperty(obj, p) { 497 var desc = new PropertyDescriptor(); 498 499 // GetOwnProperty returns an array indexed by the constants 500 // defined in macros.py. 501 // If p is not a property on obj undefined is returned. 502 var props = %GetOwnProperty(ToObject(obj), ToString(p)); 503 504 if (IS_UNDEFINED(props)) return void 0; 505 506 // This is an accessor 507 if (props[IS_ACCESSOR_INDEX]) { 508 desc.setGet(props[GETTER_INDEX]); 509 desc.setSet(props[SETTER_INDEX]); 510 } else { 511 desc.setValue(props[VALUE_INDEX]); 512 desc.setWritable(props[WRITABLE_INDEX]); 513 } 514 desc.setEnumerable(props[ENUMERABLE_INDEX]); 515 desc.setConfigurable(props[CONFIGURABLE_INDEX]); 516 517 return desc; 518} 519 520 521// ES5 section 8.12.2. 522function GetProperty(obj, p) { 523 var prop = GetOwnProperty(obj); 524 if (!IS_UNDEFINED(prop)) return prop; 525 var proto = obj.__proto__; 526 if (IS_NULL(proto)) return void 0; 527 return GetProperty(proto, p); 528} 529 530 531// ES5 section 8.12.6 532function HasProperty(obj, p) { 533 var desc = GetProperty(obj, p); 534 return IS_UNDEFINED(desc) ? false : true; 535} 536 537 538// ES5 8.12.9. 539function DefineOwnProperty(obj, p, desc, should_throw) { 540 var current = GetOwnProperty(obj, p); 541 var extensible = %IsExtensible(ToObject(obj)); 542 543 // Error handling according to spec. 544 // Step 3 545 if (IS_UNDEFINED(current) && !extensible) 546 throw MakeTypeError("define_disallowed", ["defineProperty"]); 547 548 if (!IS_UNDEFINED(current) && !current.isConfigurable()) { 549 // Step 5 and 6 550 if ((!desc.hasEnumerable() || 551 SameValue(desc.isEnumerable() && current.isEnumerable())) && 552 (!desc.hasConfigurable() || 553 SameValue(desc.isConfigurable(), current.isConfigurable())) && 554 (!desc.hasWritable() || 555 SameValue(desc.isWritable(), current.isWritable())) && 556 (!desc.hasValue() || 557 SameValue(desc.getValue(), current.getValue())) && 558 (!desc.hasGetter() || 559 SameValue(desc.getGet(), current.getGet())) && 560 (!desc.hasSetter() || 561 SameValue(desc.getSet(), current.getSet()))) { 562 return true; 563 } 564 565 // Step 7 566 if (desc.isConfigurable() || desc.isEnumerable() != current.isEnumerable()) 567 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 568 // Step 9 569 if (IsDataDescriptor(current) != IsDataDescriptor(desc)) 570 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 571 // Step 10 572 if (IsDataDescriptor(current) && IsDataDescriptor(desc)) { 573 if (!current.isWritable() && desc.isWritable()) 574 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 575 if (!current.isWritable() && desc.hasValue() && 576 !SameValue(desc.getValue(), current.getValue())) { 577 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 578 } 579 } 580 // Step 11 581 if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) { 582 if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())){ 583 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 584 } 585 if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) 586 throw MakeTypeError("redefine_disallowed", ["defineProperty"]); 587 } 588 } 589 590 // Send flags - enumerable and configurable are common - writable is 591 // only send to the data descriptor. 592 // Take special care if enumerable and configurable is not defined on 593 // desc (we need to preserve the existing values from current). 594 var flag = NONE; 595 if (desc.hasEnumerable()) { 596 flag |= desc.isEnumerable() ? 0 : DONT_ENUM; 597 } else if (!IS_UNDEFINED(current)) { 598 flag |= current.isEnumerable() ? 0 : DONT_ENUM; 599 } else { 600 flag |= DONT_ENUM; 601 } 602 603 if (desc.hasConfigurable()) { 604 flag |= desc.isConfigurable() ? 0 : DONT_DELETE; 605 } else if (!IS_UNDEFINED(current)) { 606 flag |= current.isConfigurable() ? 0 : DONT_DELETE; 607 } else 608 flag |= DONT_DELETE; 609 610 if (IsDataDescriptor(desc) || IsGenericDescriptor(desc)) { 611 if (desc.hasWritable()) { 612 flag |= desc.isWritable() ? 0 : READ_ONLY; 613 } else if (!IS_UNDEFINED(current)) { 614 flag |= current.isWritable() ? 0 : READ_ONLY; 615 } else { 616 flag |= READ_ONLY; 617 } 618 %DefineOrRedefineDataProperty(obj, p, desc.getValue(), flag); 619 } else { 620 if (desc.hasGetter() && IS_FUNCTION(desc.getGet())) { 621 %DefineOrRedefineAccessorProperty(obj, p, GETTER, desc.getGet(), flag); 622 } 623 if (desc.hasSetter() && IS_FUNCTION(desc.getSet())) { 624 %DefineOrRedefineAccessorProperty(obj, p, SETTER, desc.getSet(), flag); 625 } 626 } 627 return true; 628} 629 630 631// ES5 section 15.2.3.2. 632function ObjectGetPrototypeOf(obj) { 633 if (!IS_SPEC_OBJECT(obj)) 634 throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]); 635 return obj.__proto__; 636} 637 638 639// ES5 section 15.2.3.3 640function ObjectGetOwnPropertyDescriptor(obj, p) { 641 if (!IS_SPEC_OBJECT(obj)) 642 throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]); 643 var desc = GetOwnProperty(obj, p); 644 return FromPropertyDescriptor(desc); 645} 646 647 648// ES5 section 15.2.3.4. 649function ObjectGetOwnPropertyNames(obj) { 650 if (!IS_SPEC_OBJECT(obj)) 651 throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]); 652 653 // Find all the indexed properties. 654 655 // Get the local element names. 656 var propertyNames = %GetLocalElementNames(obj); 657 658 // Get names for indexed interceptor properties. 659 if (%GetInterceptorInfo(obj) & 1) { 660 var indexedInterceptorNames = 661 %GetIndexedInterceptorElementNames(obj); 662 if (indexedInterceptorNames) 663 propertyNames = propertyNames.concat(indexedInterceptorNames); 664 } 665 666 // Find all the named properties. 667 668 // Get the local property names. 669 propertyNames = propertyNames.concat(%GetLocalPropertyNames(obj)); 670 671 // Get names for named interceptor properties if any. 672 673 if (%GetInterceptorInfo(obj) & 2) { 674 var namedInterceptorNames = 675 %GetNamedInterceptorPropertyNames(obj); 676 if (namedInterceptorNames) { 677 propertyNames = propertyNames.concat(namedInterceptorNames); 678 } 679 } 680 681 // Property names are expected to be unique strings. 682 var propertySet = {}; 683 var j = 0; 684 for (var i = 0; i < propertyNames.length; ++i) { 685 var name = ToString(propertyNames[i]); 686 // We need to check for the exact property value since for intrinsic 687 // properties like toString if(propertySet["toString"]) will always 688 // succeed. 689 if (propertySet[name] === true) 690 continue; 691 propertySet[name] = true; 692 propertyNames[j++] = name; 693 } 694 propertyNames.length = j; 695 696 return propertyNames; 697} 698 699 700// ES5 section 15.2.3.5. 701function ObjectCreate(proto, properties) { 702 if (!IS_SPEC_OBJECT(proto) && proto !== null) { 703 throw MakeTypeError("proto_object_or_null", [proto]); 704 } 705 var obj = new $Object(); 706 obj.__proto__ = proto; 707 if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties); 708 return obj; 709} 710 711 712// ES5 section 15.2.3.6. 713function ObjectDefineProperty(obj, p, attributes) { 714 if (!IS_SPEC_OBJECT(obj)) { 715 throw MakeTypeError("obj_ctor_property_non_object", ["defineProperty"]); 716 } 717 var name = ToString(p); 718 var desc = ToPropertyDescriptor(attributes); 719 DefineOwnProperty(obj, name, desc, true); 720 return obj; 721} 722 723 724// ES5 section 15.2.3.7. 725function ObjectDefineProperties(obj, properties) { 726 if (!IS_SPEC_OBJECT(obj)) 727 throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]); 728 var props = ToObject(properties); 729 var key_values = []; 730 for (var key in props) { 731 if (%HasLocalProperty(props, key)) { 732 key_values.push(key); 733 var value = props[key]; 734 var desc = ToPropertyDescriptor(value); 735 key_values.push(desc); 736 } 737 } 738 for (var i = 0; i < key_values.length; i += 2) { 739 var key = key_values[i]; 740 var desc = key_values[i + 1]; 741 DefineOwnProperty(obj, key, desc, true); 742 } 743 return obj; 744} 745 746 747// ES5 section 15.2.3.8. 748function ObjectSeal(obj) { 749 if (!IS_SPEC_OBJECT(obj)) { 750 throw MakeTypeError("obj_ctor_property_non_object", ["seal"]); 751 } 752 var names = ObjectGetOwnPropertyNames(obj); 753 for (var i = 0; i < names.length; i++) { 754 var name = names[i]; 755 var desc = GetOwnProperty(obj, name); 756 if (desc.isConfigurable()) desc.setConfigurable(false); 757 DefineOwnProperty(obj, name, desc, true); 758 } 759 return ObjectPreventExtension(obj); 760} 761 762 763// ES5 section 15.2.3.9. 764function ObjectFreeze(obj) { 765 if (!IS_SPEC_OBJECT(obj)) { 766 throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]); 767 } 768 var names = ObjectGetOwnPropertyNames(obj); 769 for (var i = 0; i < names.length; i++) { 770 var name = names[i]; 771 var desc = GetOwnProperty(obj, name); 772 if (IsDataDescriptor(desc)) desc.setWritable(false); 773 if (desc.isConfigurable()) desc.setConfigurable(false); 774 DefineOwnProperty(obj, name, desc, true); 775 } 776 return ObjectPreventExtension(obj); 777} 778 779 780// ES5 section 15.2.3.10 781function ObjectPreventExtension(obj) { 782 if (!IS_SPEC_OBJECT(obj)) { 783 throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]); 784 } 785 %PreventExtensions(obj); 786 return obj; 787} 788 789 790// ES5 section 15.2.3.11 791function ObjectIsSealed(obj) { 792 if (!IS_SPEC_OBJECT(obj)) { 793 throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]); 794 } 795 var names = ObjectGetOwnPropertyNames(obj); 796 for (var i = 0; i < names.length; i++) { 797 var name = names[i]; 798 var desc = GetOwnProperty(obj, name); 799 if (desc.isConfigurable()) return false; 800 } 801 if (!ObjectIsExtensible(obj)) { 802 return true; 803 } 804 return false; 805} 806 807 808// ES5 section 15.2.3.12 809function ObjectIsFrozen(obj) { 810 if (!IS_SPEC_OBJECT(obj)) { 811 throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]); 812 } 813 var names = ObjectGetOwnPropertyNames(obj); 814 for (var i = 0; i < names.length; i++) { 815 var name = names[i]; 816 var desc = GetOwnProperty(obj, name); 817 if (IsDataDescriptor(desc) && desc.isWritable()) return false; 818 if (desc.isConfigurable()) return false; 819 } 820 if (!ObjectIsExtensible(obj)) { 821 return true; 822 } 823 return false; 824} 825 826 827// ES5 section 15.2.3.13 828function ObjectIsExtensible(obj) { 829 if (!IS_SPEC_OBJECT(obj)) { 830 throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]); 831 } 832 return %IsExtensible(obj); 833} 834 835 836%SetCode($Object, function(x) { 837 if (%_IsConstructCall()) { 838 if (x == null) return this; 839 return ToObject(x); 840 } else { 841 if (x == null) return { }; 842 return ToObject(x); 843 } 844}); 845 846%SetExpectedNumberOfProperties($Object, 4); 847 848// ---------------------------------------------------------------------------- 849 850 851function SetupObject() { 852 // Setup non-enumerable functions on the Object.prototype object. 853 InstallFunctions($Object.prototype, DONT_ENUM, $Array( 854 "toString", ObjectToString, 855 "toLocaleString", ObjectToLocaleString, 856 "valueOf", ObjectValueOf, 857 "hasOwnProperty", ObjectHasOwnProperty, 858 "isPrototypeOf", ObjectIsPrototypeOf, 859 "propertyIsEnumerable", ObjectPropertyIsEnumerable, 860 "__defineGetter__", ObjectDefineGetter, 861 "__lookupGetter__", ObjectLookupGetter, 862 "__defineSetter__", ObjectDefineSetter, 863 "__lookupSetter__", ObjectLookupSetter 864 )); 865 InstallFunctions($Object, DONT_ENUM, $Array( 866 "keys", ObjectKeys, 867 "create", ObjectCreate, 868 "defineProperty", ObjectDefineProperty, 869 "defineProperties", ObjectDefineProperties, 870 "freeze", ObjectFreeze, 871 "getPrototypeOf", ObjectGetPrototypeOf, 872 "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor, 873 "getOwnPropertyNames", ObjectGetOwnPropertyNames, 874 "isExtensible", ObjectIsExtensible, 875 "isFrozen", ObjectIsFrozen, 876 "isSealed", ObjectIsSealed, 877 "preventExtensions", ObjectPreventExtension, 878 "seal", ObjectSeal 879 )); 880} 881 882SetupObject(); 883 884 885// ---------------------------------------------------------------------------- 886// Boolean 887 888function BooleanToString() { 889 // NOTE: Both Boolean objects and values can enter here as 890 // 'this'. This is not as dictated by ECMA-262. 891 if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) 892 throw new $TypeError('Boolean.prototype.toString is not generic'); 893 return ToString(%_ValueOf(this)); 894} 895 896 897function BooleanValueOf() { 898 // NOTE: Both Boolean objects and values can enter here as 899 // 'this'. This is not as dictated by ECMA-262. 900 if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) 901 throw new $TypeError('Boolean.prototype.valueOf is not generic'); 902 return %_ValueOf(this); 903} 904 905 906function BooleanToJSON(key) { 907 return CheckJSONPrimitive(this.valueOf()); 908} 909 910 911// ---------------------------------------------------------------------------- 912 913 914function SetupBoolean() { 915 InstallFunctions($Boolean.prototype, DONT_ENUM, $Array( 916 "toString", BooleanToString, 917 "valueOf", BooleanValueOf, 918 "toJSON", BooleanToJSON 919 )); 920} 921 922SetupBoolean(); 923 924// ---------------------------------------------------------------------------- 925// Number 926 927// Set the Number function and constructor. 928%SetCode($Number, function(x) { 929 var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x); 930 if (%_IsConstructCall()) { 931 %_SetValueOf(this, value); 932 } else { 933 return value; 934 } 935}); 936 937%FunctionSetPrototype($Number, new $Number(0)); 938 939// ECMA-262 section 15.7.4.2. 940function NumberToString(radix) { 941 // NOTE: Both Number objects and values can enter here as 942 // 'this'. This is not as dictated by ECMA-262. 943 var number = this; 944 if (!IS_NUMBER(this)) { 945 if (!IS_NUMBER_WRAPPER(this)) 946 throw new $TypeError('Number.prototype.toString is not generic'); 947 // Get the value of this number in case it's an object. 948 number = %_ValueOf(this); 949 } 950 // Fast case: Convert number in radix 10. 951 if (IS_UNDEFINED(radix) || radix === 10) { 952 return ToString(number); 953 } 954 955 // Convert the radix to an integer and check the range. 956 radix = TO_INTEGER(radix); 957 if (radix < 2 || radix > 36) { 958 throw new $RangeError('toString() radix argument must be between 2 and 36'); 959 } 960 // Convert the number to a string in the given radix. 961 return %NumberToRadixString(number, radix); 962} 963 964 965// ECMA-262 section 15.7.4.3 966function NumberToLocaleString() { 967 return this.toString(); 968} 969 970 971// ECMA-262 section 15.7.4.4 972function NumberValueOf() { 973 // NOTE: Both Number objects and values can enter here as 974 // 'this'. This is not as dictated by ECMA-262. 975 if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) 976 throw new $TypeError('Number.prototype.valueOf is not generic'); 977 return %_ValueOf(this); 978} 979 980 981// ECMA-262 section 15.7.4.5 982function NumberToFixed(fractionDigits) { 983 var f = TO_INTEGER(fractionDigits); 984 if (f < 0 || f > 20) { 985 throw new $RangeError("toFixed() digits argument must be between 0 and 20"); 986 } 987 var x = ToNumber(this); 988 return %NumberToFixed(x, f); 989} 990 991 992// ECMA-262 section 15.7.4.6 993function NumberToExponential(fractionDigits) { 994 var f = -1; 995 if (!IS_UNDEFINED(fractionDigits)) { 996 f = TO_INTEGER(fractionDigits); 997 if (f < 0 || f > 20) { 998 throw new $RangeError("toExponential() argument must be between 0 and 20"); 999 } 1000 } 1001 var x = ToNumber(this); 1002 return %NumberToExponential(x, f); 1003} 1004 1005 1006// ECMA-262 section 15.7.4.7 1007function NumberToPrecision(precision) { 1008 if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this)); 1009 var p = TO_INTEGER(precision); 1010 if (p < 1 || p > 21) { 1011 throw new $RangeError("toPrecision() argument must be between 1 and 21"); 1012 } 1013 var x = ToNumber(this); 1014 return %NumberToPrecision(x, p); 1015} 1016 1017 1018function CheckJSONPrimitive(val) { 1019 if (!IsPrimitive(val)) 1020 throw MakeTypeError('result_not_primitive', ['toJSON', val]); 1021 return val; 1022} 1023 1024 1025function NumberToJSON(key) { 1026 return CheckJSONPrimitive(this.valueOf()); 1027} 1028 1029 1030// ---------------------------------------------------------------------------- 1031 1032function SetupNumber() { 1033 %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8); 1034 // Setup the constructor property on the Number prototype object. 1035 %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM); 1036 1037 %OptimizeObjectForAddingMultipleProperties($Number, 5); 1038 // ECMA-262 section 15.7.3.1. 1039 %SetProperty($Number, 1040 "MAX_VALUE", 1041 1.7976931348623157e+308, 1042 DONT_ENUM | DONT_DELETE | READ_ONLY); 1043 1044 // ECMA-262 section 15.7.3.2. 1045 %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY); 1046 1047 // ECMA-262 section 15.7.3.3. 1048 %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY); 1049 1050 // ECMA-262 section 15.7.3.4. 1051 %SetProperty($Number, 1052 "NEGATIVE_INFINITY", 1053 -1/0, 1054 DONT_ENUM | DONT_DELETE | READ_ONLY); 1055 1056 // ECMA-262 section 15.7.3.5. 1057 %SetProperty($Number, 1058 "POSITIVE_INFINITY", 1059 1/0, 1060 DONT_ENUM | DONT_DELETE | READ_ONLY); 1061 %ToFastProperties($Number); 1062 1063 // Setup non-enumerable functions on the Number prototype object. 1064 InstallFunctions($Number.prototype, DONT_ENUM, $Array( 1065 "toString", NumberToString, 1066 "toLocaleString", NumberToLocaleString, 1067 "valueOf", NumberValueOf, 1068 "toFixed", NumberToFixed, 1069 "toExponential", NumberToExponential, 1070 "toPrecision", NumberToPrecision, 1071 "toJSON", NumberToJSON 1072 )); 1073} 1074 1075SetupNumber(); 1076 1077 1078 1079// ---------------------------------------------------------------------------- 1080// Function 1081 1082$Function.prototype.constructor = $Function; 1083 1084function FunctionSourceString(func) { 1085 if (!IS_FUNCTION(func)) { 1086 throw new $TypeError('Function.prototype.toString is not generic'); 1087 } 1088 1089 var source = %FunctionGetSourceCode(func); 1090 if (!IS_STRING(source) || %FunctionIsBuiltin(func)) { 1091 var name = %FunctionGetName(func); 1092 if (name) { 1093 // Mimic what KJS does. 1094 return 'function ' + name + '() { [native code] }'; 1095 } else { 1096 return 'function () { [native code] }'; 1097 } 1098 } 1099 1100 var name = %FunctionGetName(func); 1101 return 'function ' + name + source; 1102} 1103 1104 1105function FunctionToString() { 1106 return FunctionSourceString(this); 1107} 1108 1109 1110// ES5 15.3.4.5 1111function FunctionBind(this_arg) { // Length is 1. 1112 if (!IS_FUNCTION(this)) { 1113 throw new $TypeError('Bind must be called on a function'); 1114 } 1115 // this_arg is not an argument that should be bound. 1116 var argc_bound = (%_ArgumentsLength() || 1) - 1; 1117 if (argc_bound > 0) { 1118 var bound_args = new $Array(argc_bound); 1119 for(var i = 0; i < argc_bound; i++) { 1120 bound_args[i] = %_Arguments(i+1); 1121 } 1122 } 1123 var fn = this; 1124 var result = function() { 1125 // Combine the args we got from the bind call with the args 1126 // given as argument to the invocation. 1127 var argc = %_ArgumentsLength(); 1128 var args = new $Array(argc + argc_bound); 1129 // Add bound arguments. 1130 for (var i = 0; i < argc_bound; i++) { 1131 args[i] = bound_args[i]; 1132 } 1133 // Add arguments from call. 1134 for (var i = 0; i < argc; i++) { 1135 args[argc_bound + i] = %_Arguments(i); 1136 } 1137 // If this is a construct call we use a special runtime method 1138 // to generate the actual object using the bound function. 1139 if (%_IsConstructCall()) { 1140 return %NewObjectFromBound(fn, args); 1141 } 1142 return fn.apply(this_arg, args); 1143 }; 1144 1145 // We already have caller and arguments properties on functions, 1146 // which are non-configurable. It therefore makes no sence to 1147 // try to redefine these as defined by the spec. The spec says 1148 // that bind should make these throw a TypeError if get or set 1149 // is called and make them non-enumerable and non-configurable. 1150 // To be consistent with our normal functions we leave this as it is. 1151 1152 // Set the correct length. 1153 var length = (this.length - argc_bound) > 0 ? this.length - argc_bound : 0; 1154 %FunctionSetLength(result, length); 1155 1156 return result; 1157} 1158 1159 1160function NewFunction(arg1) { // length == 1 1161 var n = %_ArgumentsLength(); 1162 var p = ''; 1163 if (n > 1) { 1164 p = new $Array(n - 1); 1165 // Explicitly convert all parameters to strings. 1166 // Array.prototype.join replaces null with empty strings which is 1167 // not appropriate. 1168 for (var i = 0; i < n - 1; i++) p[i] = ToString(%_Arguments(i)); 1169 p = p.join(','); 1170 // If the formal parameters string include ) - an illegal 1171 // character - it may make the combined function expression 1172 // compile. We avoid this problem by checking for this early on. 1173 if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]); 1174 } 1175 var body = (n > 0) ? ToString(%_Arguments(n - 1)) : ''; 1176 var source = '(function(' + p + ') {\n' + body + '\n})'; 1177 1178 // The call to SetNewFunctionAttributes will ensure the prototype 1179 // property of the resulting function is enumerable (ECMA262, 15.3.5.2). 1180 var f = %CompileString(source)(); 1181 %FunctionSetName(f, "anonymous"); 1182 return %SetNewFunctionAttributes(f); 1183} 1184 1185%SetCode($Function, NewFunction); 1186 1187// ---------------------------------------------------------------------------- 1188 1189function SetupFunction() { 1190 InstallFunctions($Function.prototype, DONT_ENUM, $Array( 1191 "bind", FunctionBind, 1192 "toString", FunctionToString 1193 )); 1194} 1195 1196SetupFunction(); 1197