v8natives.js revision d0582a6c46733687d045e4188a1bcd0123c758a1
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// Helper function used to install functions on objects. 48function InstallFunctions(object, attributes, functions) { 49 if (functions.length >= 8) { 50 %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1); 51 } 52 for (var i = 0; i < functions.length; i += 2) { 53 var key = functions[i]; 54 var f = functions[i + 1]; 55 %FunctionSetName(f, key); 56 %SetProperty(object, key, f, attributes); 57 } 58 %TransformToFastProperties(object); 59} 60 61// Emulates JSC by installing functions on a hidden prototype that 62// lies above the current object/prototype. This lets you override 63// functions on String.prototype etc. and then restore the old function 64// with delete. See http://code.google.com/p/chromium/issues/detail?id=1717 65function InstallFunctionsOnHiddenPrototype(object, attributes, functions) { 66 var hidden_prototype = new $Object(); 67 %SetHiddenPrototype(object, hidden_prototype); 68 InstallFunctions(hidden_prototype, attributes, functions); 69} 70 71 72// ---------------------------------------------------------------------------- 73 74 75// ECMA 262 - 15.1.4 76function GlobalIsNaN(number) { 77 var n = ToNumber(number); 78 return NUMBER_IS_NAN(n); 79} 80 81 82// ECMA 262 - 15.1.5 83function GlobalIsFinite(number) { 84 return %NumberIsFinite(ToNumber(number)); 85} 86 87 88// ECMA-262 - 15.1.2.2 89function GlobalParseInt(string, radix) { 90 if (radix === void 0) { 91 // Some people use parseInt instead of Math.floor. This 92 // optimization makes parseInt on a Smi 12 times faster (60ns 93 // vs 800ns). The following optimization makes parseInt on a 94 // non-Smi number 9 times faster (230ns vs 2070ns). Together 95 // they make parseInt on a string 1.4% slower (274ns vs 270ns). 96 if (%_IsSmi(string)) return string; 97 if (IS_NUMBER(string) && 98 ((0.01 < string && string < 1e9) || 99 (-1e9 < string && string < -0.01))) { 100 // Truncate number. 101 return string | 0; 102 } 103 radix = 0; 104 } else { 105 radix = TO_INT32(radix); 106 if (!(radix == 0 || (2 <= radix && radix <= 36))) 107 return $NaN; 108 } 109 return %StringParseInt(ToString(string), radix); 110} 111 112 113// ECMA-262 - 15.1.2.3 114function GlobalParseFloat(string) { 115 return %StringParseFloat(ToString(string)); 116} 117 118 119function GlobalEval(x) { 120 if (!IS_STRING(x)) return x; 121 122 var global_receiver = %GlobalReceiver(global); 123 var this_is_global_receiver = (this === global_receiver); 124 var global_is_detached = (global === global_receiver); 125 126 if (!this_is_global_receiver || global_is_detached) { 127 throw new $EvalError('The "this" object passed to eval must ' + 128 'be the global object from which eval originated'); 129 } 130 131 var f = %CompileString(x, false); 132 if (!IS_FUNCTION(f)) return f; 133 134 return f.call(this); 135} 136 137 138// execScript for IE compatibility. 139function GlobalExecScript(expr, lang) { 140 // NOTE: We don't care about the character casing. 141 if (!lang || /javascript/i.test(lang)) { 142 var f = %CompileString(ToString(expr), false); 143 f.call(%GlobalReceiver(global)); 144 } 145 return null; 146} 147 148 149// ---------------------------------------------------------------------------- 150 151 152function SetupGlobal() { 153 // ECMA 262 - 15.1.1.1. 154 %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE); 155 156 // ECMA-262 - 15.1.1.2. 157 %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE); 158 159 // ECMA-262 - 15.1.1.3. 160 %SetProperty(global, "undefined", void 0, DONT_ENUM | DONT_DELETE); 161 162 // Setup non-enumerable function on the global object. 163 InstallFunctions(global, DONT_ENUM, $Array( 164 "isNaN", GlobalIsNaN, 165 "isFinite", GlobalIsFinite, 166 "parseInt", GlobalParseInt, 167 "parseFloat", GlobalParseFloat, 168 "eval", GlobalEval, 169 "execScript", GlobalExecScript 170 )); 171} 172 173SetupGlobal(); 174 175 176// ---------------------------------------------------------------------------- 177// Boolean (first part of definition) 178 179 180%SetCode($Boolean, function(x) { 181 if (%_IsConstructCall()) { 182 %_SetValueOf(this, ToBoolean(x)); 183 } else { 184 return ToBoolean(x); 185 } 186}); 187 188%FunctionSetPrototype($Boolean, new $Boolean(false)); 189 190%SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM); 191 192// ---------------------------------------------------------------------------- 193// Object 194 195$Object.prototype.constructor = $Object; 196 197// ECMA-262 - 15.2.4.2 198function ObjectToString() { 199 return "[object " + %_ClassOf(this) + "]"; 200} 201 202 203// ECMA-262 - 15.2.4.3 204function ObjectToLocaleString() { 205 return this.toString(); 206} 207 208 209// ECMA-262 - 15.2.4.4 210function ObjectValueOf() { 211 return this; 212} 213 214 215// ECMA-262 - 15.2.4.5 216function ObjectHasOwnProperty(V) { 217 return %HasLocalProperty(ToObject(this), ToString(V)); 218} 219 220 221// ECMA-262 - 15.2.4.6 222function ObjectIsPrototypeOf(V) { 223 if (!IS_OBJECT(V) && !IS_FUNCTION(V)) return false; 224 return %IsInPrototypeChain(this, V); 225} 226 227 228// ECMA-262 - 15.2.4.6 229function ObjectPropertyIsEnumerable(V) { 230 if (this == null) return false; 231 if (!IS_OBJECT(this) && !IS_FUNCTION(this)) return false; 232 return %IsPropertyEnumerable(this, ToString(V)); 233} 234 235 236// Extensions for providing property getters and setters. 237function ObjectDefineGetter(name, fun) { 238 if (this == null) { 239 throw new $TypeError('Object.prototype.__defineGetter__: this is Null'); 240 } 241 if (!IS_FUNCTION(fun)) { 242 throw new $TypeError('Object.prototype.__defineGetter__: Expecting function'); 243 } 244 return %DefineAccessor(ToObject(this), ToString(name), GETTER, fun); 245} 246 247 248function ObjectLookupGetter(name) { 249 if (this == null) { 250 throw new $TypeError('Object.prototype.__lookupGetter__: this is Null'); 251 } 252 return %LookupAccessor(ToObject(this), ToString(name), GETTER); 253} 254 255 256function ObjectDefineSetter(name, fun) { 257 if (this == null) { 258 throw new $TypeError('Object.prototype.__defineSetter__: this is Null'); 259 } 260 if (!IS_FUNCTION(fun)) { 261 throw new $TypeError( 262 'Object.prototype.__defineSetter__: Expecting function'); 263 } 264 return %DefineAccessor(ToObject(this), ToString(name), SETTER, fun); 265} 266 267 268function ObjectLookupSetter(name) { 269 if (this == null) { 270 throw new $TypeError('Object.prototype.__lookupSetter__: this is Null'); 271 } 272 return %LookupAccessor(ToObject(this), ToString(name), SETTER); 273} 274 275 276function ObjectKeys(obj) { 277 if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj)) 278 throw MakeTypeError('object_keys_non_object', [obj]); 279 return %LocalKeys(obj); 280} 281 282 283%SetCode($Object, function(x) { 284 if (%_IsConstructCall()) { 285 if (x == null) return this; 286 return ToObject(x); 287 } else { 288 if (x == null) return { }; 289 return ToObject(x); 290 } 291}); 292 293 294// ---------------------------------------------------------------------------- 295 296 297function SetupObject() { 298 // Setup non-enumerable functions on the Object.prototype object. 299 InstallFunctions($Object.prototype, DONT_ENUM, $Array( 300 "toString", ObjectToString, 301 "toLocaleString", ObjectToLocaleString, 302 "valueOf", ObjectValueOf, 303 "hasOwnProperty", ObjectHasOwnProperty, 304 "isPrototypeOf", ObjectIsPrototypeOf, 305 "propertyIsEnumerable", ObjectPropertyIsEnumerable, 306 "__defineGetter__", ObjectDefineGetter, 307 "__lookupGetter__", ObjectLookupGetter, 308 "__defineSetter__", ObjectDefineSetter, 309 "__lookupSetter__", ObjectLookupSetter 310 )); 311 InstallFunctions($Object, DONT_ENUM, $Array( 312 "keys", ObjectKeys 313 )); 314} 315 316SetupObject(); 317 318 319// ---------------------------------------------------------------------------- 320// Boolean 321 322function BooleanToString() { 323 // NOTE: Both Boolean objects and values can enter here as 324 // 'this'. This is not as dictated by ECMA-262. 325 if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) 326 throw new $TypeError('Boolean.prototype.toString is not generic'); 327 return ToString(%_ValueOf(this)); 328} 329 330 331function BooleanValueOf() { 332 // NOTE: Both Boolean objects and values can enter here as 333 // 'this'. This is not as dictated by ECMA-262. 334 if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) 335 throw new $TypeError('Boolean.prototype.valueOf is not generic'); 336 return %_ValueOf(this); 337} 338 339 340function BooleanToJSON(key) { 341 return CheckJSONPrimitive(this.valueOf()); 342} 343 344 345// ---------------------------------------------------------------------------- 346 347 348function SetupBoolean() { 349 InstallFunctions($Boolean.prototype, DONT_ENUM, $Array( 350 "toString", BooleanToString, 351 "valueOf", BooleanValueOf, 352 "toJSON", BooleanToJSON 353 )); 354} 355 356SetupBoolean(); 357 358// ---------------------------------------------------------------------------- 359// Number 360 361// Set the Number function and constructor. 362%SetCode($Number, function(x) { 363 var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x); 364 if (%_IsConstructCall()) { 365 %_SetValueOf(this, value); 366 } else { 367 return value; 368 } 369}); 370 371%FunctionSetPrototype($Number, new $Number(0)); 372 373// ECMA-262 section 15.7.4.2. 374function NumberToString(radix) { 375 // NOTE: Both Number objects and values can enter here as 376 // 'this'. This is not as dictated by ECMA-262. 377 var number = this; 378 if (!IS_NUMBER(this)) { 379 if (!IS_NUMBER_WRAPPER(this)) 380 throw new $TypeError('Number.prototype.toString is not generic'); 381 // Get the value of this number in case it's an object. 382 number = %_ValueOf(this); 383 } 384 // Fast case: Convert number in radix 10. 385 if (IS_UNDEFINED(radix) || radix === 10) { 386 return ToString(number); 387 } 388 389 // Convert the radix to an integer and check the range. 390 radix = TO_INTEGER(radix); 391 if (radix < 2 || radix > 36) { 392 throw new $RangeError('toString() radix argument must be between 2 and 36'); 393 } 394 // Convert the number to a string in the given radix. 395 return %NumberToRadixString(number, radix); 396} 397 398 399// ECMA-262 section 15.7.4.3 400function NumberToLocaleString() { 401 return this.toString(); 402} 403 404 405// ECMA-262 section 15.7.4.4 406function NumberValueOf() { 407 // NOTE: Both Number objects and values can enter here as 408 // 'this'. This is not as dictated by ECMA-262. 409 if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) 410 throw new $TypeError('Number.prototype.valueOf is not generic'); 411 return %_ValueOf(this); 412} 413 414 415// ECMA-262 section 15.7.4.5 416function NumberToFixed(fractionDigits) { 417 var f = TO_INTEGER(fractionDigits); 418 if (f < 0 || f > 20) { 419 throw new $RangeError("toFixed() digits argument must be between 0 and 20"); 420 } 421 var x = ToNumber(this); 422 return %NumberToFixed(x, f); 423} 424 425 426// ECMA-262 section 15.7.4.6 427function NumberToExponential(fractionDigits) { 428 var f = -1; 429 if (!IS_UNDEFINED(fractionDigits)) { 430 f = TO_INTEGER(fractionDigits); 431 if (f < 0 || f > 20) { 432 throw new $RangeError("toExponential() argument must be between 0 and 20"); 433 } 434 } 435 var x = ToNumber(this); 436 return %NumberToExponential(x, f); 437} 438 439 440// ECMA-262 section 15.7.4.7 441function NumberToPrecision(precision) { 442 if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this)); 443 var p = TO_INTEGER(precision); 444 if (p < 1 || p > 21) { 445 throw new $RangeError("toPrecision() argument must be between 1 and 21"); 446 } 447 var x = ToNumber(this); 448 return %NumberToPrecision(x, p); 449} 450 451 452function CheckJSONPrimitive(val) { 453 if (!IsPrimitive(val)) 454 throw MakeTypeError('result_not_primitive', ['toJSON', val]); 455 return val; 456} 457 458 459function NumberToJSON(key) { 460 return CheckJSONPrimitive(this.valueOf()); 461} 462 463 464// ---------------------------------------------------------------------------- 465 466function SetupNumber() { 467 %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8); 468 // Setup the constructor property on the Number prototype object. 469 %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM); 470 471 %OptimizeObjectForAddingMultipleProperties($Number, 5); 472 // ECMA-262 section 15.7.3.1. 473 %SetProperty($Number, 474 "MAX_VALUE", 475 1.7976931348623157e+308, 476 DONT_ENUM | DONT_DELETE | READ_ONLY); 477 478 // ECMA-262 section 15.7.3.2. 479 %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY); 480 481 // ECMA-262 section 15.7.3.3. 482 %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY); 483 484 // ECMA-262 section 15.7.3.4. 485 %SetProperty($Number, 486 "NEGATIVE_INFINITY", 487 -1/0, 488 DONT_ENUM | DONT_DELETE | READ_ONLY); 489 490 // ECMA-262 section 15.7.3.5. 491 %SetProperty($Number, 492 "POSITIVE_INFINITY", 493 1/0, 494 DONT_ENUM | DONT_DELETE | READ_ONLY); 495 %TransformToFastProperties($Number); 496 497 // Setup non-enumerable functions on the Number prototype object. 498 InstallFunctions($Number.prototype, DONT_ENUM, $Array( 499 "toString", NumberToString, 500 "toLocaleString", NumberToLocaleString, 501 "valueOf", NumberValueOf, 502 "toFixed", NumberToFixed, 503 "toExponential", NumberToExponential, 504 "toPrecision", NumberToPrecision, 505 "toJSON", NumberToJSON 506 )); 507} 508 509SetupNumber(); 510 511 512 513// ---------------------------------------------------------------------------- 514// Function 515 516$Function.prototype.constructor = $Function; 517 518function FunctionSourceString(func) { 519 if (!IS_FUNCTION(func)) { 520 throw new $TypeError('Function.prototype.toString is not generic'); 521 } 522 523 var source = %FunctionGetSourceCode(func); 524 if (!IS_STRING(source) || %FunctionIsBuiltin(func)) { 525 var name = %FunctionGetName(func); 526 if (name) { 527 // Mimic what KJS does. 528 return 'function ' + name + '() { [native code] }'; 529 } else { 530 return 'function () { [native code] }'; 531 } 532 } 533 534 var name = %FunctionGetName(func); 535 return 'function ' + name + source; 536} 537 538 539function FunctionToString() { 540 return FunctionSourceString(this); 541} 542 543 544function NewFunction(arg1) { // length == 1 545 var n = %_ArgumentsLength(); 546 var p = ''; 547 if (n > 1) { 548 p = new $Array(n - 1); 549 // Explicitly convert all parameters to strings. 550 // Array.prototype.join replaces null with empty strings which is 551 // not appropriate. 552 for (var i = 0; i < n - 1; i++) p[i] = ToString(%_Arguments(i)); 553 p = p.join(','); 554 // If the formal parameters string include ) - an illegal 555 // character - it may make the combined function expression 556 // compile. We avoid this problem by checking for this early on. 557 if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]); 558 } 559 var body = (n > 0) ? ToString(%_Arguments(n - 1)) : ''; 560 var source = '(function(' + p + ') {\n' + body + '\n})'; 561 562 // The call to SetNewFunctionAttributes will ensure the prototype 563 // property of the resulting function is enumerable (ECMA262, 15.3.5.2). 564 var f = %CompileString(source, false)(); 565 %FunctionSetName(f, "anonymous"); 566 return %SetNewFunctionAttributes(f); 567} 568 569%SetCode($Function, NewFunction); 570 571// ---------------------------------------------------------------------------- 572 573function SetupFunction() { 574 InstallFunctions($Function.prototype, DONT_ENUM, $Array( 575 "toString", FunctionToString 576 )); 577} 578 579SetupFunction(); 580