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