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