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