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 ((string < -0.01 && -1e9 < string) || 99 (0.01 < string && string < 1e9))) { 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 var c = %_ClassOf(this); 200 // Hide Arguments from the outside. 201 if (c === 'Arguments') c = 'Object'; 202 return "[object " + c + "]"; 203} 204 205 206// ECMA-262 - 15.2.4.3 207function ObjectToLocaleString() { 208 return this.toString(); 209} 210 211 212// ECMA-262 - 15.2.4.4 213function ObjectValueOf() { 214 return this; 215} 216 217 218// ECMA-262 - 15.2.4.5 219function ObjectHasOwnProperty(V) { 220 return %HasLocalProperty(ToObject(this), ToString(V)); 221} 222 223 224// ECMA-262 - 15.2.4.6 225function ObjectIsPrototypeOf(V) { 226 if (!IS_OBJECT(V) && !IS_FUNCTION(V)) return false; 227 return %IsInPrototypeChain(this, V); 228} 229 230 231// ECMA-262 - 15.2.4.6 232function ObjectPropertyIsEnumerable(V) { 233 if (this == null) return false; 234 if (!IS_OBJECT(this) && !IS_FUNCTION(this)) return false; 235 return %IsPropertyEnumerable(this, ToString(V)); 236} 237 238 239// Extensions for providing property getters and setters. 240function ObjectDefineGetter(name, fun) { 241 if (this == null) { 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) { 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) { 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) { 273 throw new $TypeError('Object.prototype.__lookupSetter__: this is Null'); 274 } 275 return %LookupAccessor(ToObject(this), ToString(name), SETTER); 276} 277 278 279%SetCode($Object, function(x) { 280 if (%_IsConstructCall()) { 281 if (x == null) return this; 282 return ToObject(x); 283 } else { 284 if (x == null) return { }; 285 return ToObject(x); 286 } 287}); 288 289 290// ---------------------------------------------------------------------------- 291 292 293function SetupObject() { 294 // Setup non-enumerable functions on the Object.prototype object. 295 InstallFunctions($Object.prototype, DONT_ENUM, $Array( 296 "toString", ObjectToString, 297 "toLocaleString", ObjectToLocaleString, 298 "valueOf", ObjectValueOf, 299 "hasOwnProperty", ObjectHasOwnProperty, 300 "isPrototypeOf", ObjectIsPrototypeOf, 301 "propertyIsEnumerable", ObjectPropertyIsEnumerable, 302 "__defineGetter__", ObjectDefineGetter, 303 "__lookupGetter__", ObjectLookupGetter, 304 "__defineSetter__", ObjectDefineSetter, 305 "__lookupSetter__", ObjectLookupSetter 306 )); 307} 308 309SetupObject(); 310 311 312// ---------------------------------------------------------------------------- 313// Boolean 314 315function BooleanToString() { 316 // NOTE: Both Boolean objects and values can enter here as 317 // 'this'. This is not as dictated by ECMA-262. 318 if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) 319 throw new $TypeError('Boolean.prototype.toString is not generic'); 320 return ToString(%_ValueOf(this)); 321} 322 323 324function BooleanValueOf() { 325 // NOTE: Both Boolean objects and values can enter here as 326 // 'this'. This is not as dictated by ECMA-262. 327 if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) 328 throw new $TypeError('Boolean.prototype.valueOf is not generic'); 329 return %_ValueOf(this); 330} 331 332 333function BooleanToJSON(key) { 334 return CheckJSONPrimitive(this.valueOf()); 335} 336 337 338// ---------------------------------------------------------------------------- 339 340 341function SetupBoolean() { 342 InstallFunctions($Boolean.prototype, DONT_ENUM, $Array( 343 "toString", BooleanToString, 344 "valueOf", BooleanValueOf, 345 "toJSON", BooleanToJSON 346 )); 347} 348 349SetupBoolean(); 350 351// ---------------------------------------------------------------------------- 352// Number 353 354// Set the Number function and constructor. 355%SetCode($Number, function(x) { 356 var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x); 357 if (%_IsConstructCall()) { 358 %_SetValueOf(this, value); 359 } else { 360 return value; 361 } 362}); 363 364%FunctionSetPrototype($Number, new $Number(0)); 365 366// ECMA-262 section 15.7.4.2. 367function NumberToString(radix) { 368 // NOTE: Both Number objects and values can enter here as 369 // 'this'. This is not as dictated by ECMA-262. 370 var number = this; 371 if (!IS_NUMBER(this)) { 372 if (!IS_NUMBER_WRAPPER(this)) 373 throw new $TypeError('Number.prototype.toString is not generic'); 374 // Get the value of this number in case it's an object. 375 number = %_ValueOf(this); 376 } 377 // Fast case: Convert number in radix 10. 378 if (IS_UNDEFINED(radix) || radix === 10) { 379 return ToString(number); 380 } 381 382 // Convert the radix to an integer and check the range. 383 radix = TO_INTEGER(radix); 384 if (radix < 2 || radix > 36) { 385 throw new $RangeError('toString() radix argument must be between 2 and 36'); 386 } 387 // Convert the number to a string in the given radix. 388 return %NumberToRadixString(number, radix); 389} 390 391 392// ECMA-262 section 15.7.4.3 393function NumberToLocaleString() { 394 return this.toString(); 395} 396 397 398// ECMA-262 section 15.7.4.4 399function NumberValueOf() { 400 // NOTE: Both Number objects and values can enter here as 401 // 'this'. This is not as dictated by ECMA-262. 402 if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) 403 throw new $TypeError('Number.prototype.valueOf is not generic'); 404 return %_ValueOf(this); 405} 406 407 408// ECMA-262 section 15.7.4.5 409function NumberToFixed(fractionDigits) { 410 var f = TO_INTEGER(fractionDigits); 411 if (f < 0 || f > 20) { 412 throw new $RangeError("toFixed() digits argument must be between 0 and 20"); 413 } 414 var x = ToNumber(this); 415 return %NumberToFixed(x, f); 416} 417 418 419// ECMA-262 section 15.7.4.6 420function NumberToExponential(fractionDigits) { 421 var f = -1; 422 if (!IS_UNDEFINED(fractionDigits)) { 423 f = TO_INTEGER(fractionDigits); 424 if (f < 0 || f > 20) { 425 throw new $RangeError("toExponential() argument must be between 0 and 20"); 426 } 427 } 428 var x = ToNumber(this); 429 return %NumberToExponential(x, f); 430} 431 432 433// ECMA-262 section 15.7.4.7 434function NumberToPrecision(precision) { 435 if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this)); 436 var p = TO_INTEGER(precision); 437 if (p < 1 || p > 21) { 438 throw new $RangeError("toPrecision() argument must be between 1 and 21"); 439 } 440 var x = ToNumber(this); 441 return %NumberToPrecision(x, p); 442} 443 444 445function CheckJSONPrimitive(val) { 446 if (!IsPrimitive(val)) 447 throw MakeTypeError('result_not_primitive', ['toJSON', val]); 448 return val; 449} 450 451 452function NumberToJSON(key) { 453 return CheckJSONPrimitive(this.valueOf()); 454} 455 456 457// ---------------------------------------------------------------------------- 458 459function SetupNumber() { 460 %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8); 461 // Setup the constructor property on the Number prototype object. 462 %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM); 463 464 %OptimizeObjectForAddingMultipleProperties($Number, 5); 465 // ECMA-262 section 15.7.3.1. 466 %SetProperty($Number, 467 "MAX_VALUE", 468 1.7976931348623157e+308, 469 DONT_ENUM | DONT_DELETE | READ_ONLY); 470 471 // ECMA-262 section 15.7.3.2. 472 %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY); 473 474 // ECMA-262 section 15.7.3.3. 475 %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY); 476 477 // ECMA-262 section 15.7.3.4. 478 %SetProperty($Number, 479 "NEGATIVE_INFINITY", 480 -1/0, 481 DONT_ENUM | DONT_DELETE | READ_ONLY); 482 483 // ECMA-262 section 15.7.3.5. 484 %SetProperty($Number, 485 "POSITIVE_INFINITY", 486 1/0, 487 DONT_ENUM | DONT_DELETE | READ_ONLY); 488 %TransformToFastProperties($Number); 489 490 // Setup non-enumerable functions on the Number prototype object. 491 InstallFunctions($Number.prototype, DONT_ENUM, $Array( 492 "toString", NumberToString, 493 "toLocaleString", NumberToLocaleString, 494 "valueOf", NumberValueOf, 495 "toFixed", NumberToFixed, 496 "toExponential", NumberToExponential, 497 "toPrecision", NumberToPrecision, 498 "toJSON", NumberToJSON 499 )); 500} 501 502SetupNumber(); 503 504 505 506// ---------------------------------------------------------------------------- 507// Function 508 509$Function.prototype.constructor = $Function; 510 511function FunctionSourceString(func) { 512 if (!IS_FUNCTION(func)) { 513 throw new $TypeError('Function.prototype.toString is not generic'); 514 } 515 516 var source = %FunctionGetSourceCode(func); 517 if (!IS_STRING(source)) { 518 var name = %FunctionGetName(func); 519 if (name) { 520 // Mimic what KJS does. 521 return 'function ' + name + '() { [native code] }'; 522 } else { 523 return 'function () { [native code] }'; 524 } 525 } 526 527 // Censor occurrences of internal calls. We do that for all 528 // functions and don't cache under the assumption that people rarly 529 // convert functions to strings. Note that we (apparently) can't 530 // use regular expression literals in natives files. 531 var regexp = ORIGINAL_REGEXP("%(\\w+\\()", "gm"); 532 if (source.match(regexp)) source = source.replace(regexp, "$1"); 533 var name = %FunctionGetName(func); 534 return 'function ' + name + source; 535} 536 537 538function FunctionToString() { 539 return FunctionSourceString(this); 540} 541 542 543function NewFunction(arg1) { // length == 1 544 var n = %_ArgumentsLength(); 545 var p = ''; 546 if (n > 1) { 547 p = new $Array(n - 1); 548 // Explicitly convert all parameters to strings. 549 // Array.prototype.join replaces null with empty strings which is 550 // not appropriate. 551 for (var i = 0; i < n - 1; i++) p[i] = ToString(%_Arguments(i)); 552 p = p.join(','); 553 // If the formal parameters string include ) - an illegal 554 // character - it may make the combined function expression 555 // compile. We avoid this problem by checking for this early on. 556 if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]); 557 } 558 var body = (n > 0) ? ToString(%_Arguments(n - 1)) : ''; 559 var source = '(function(' + p + ') {\n' + body + '\n})'; 560 561 // The call to SetNewFunctionAttributes will ensure the prototype 562 // property of the resulting function is enumerable (ECMA262, 15.3.5.2). 563 var f = %CompileString(source, false)(); 564 %FunctionSetName(f, "anonymous"); 565 return %SetNewFunctionAttributes(f); 566} 567 568%SetCode($Function, NewFunction); 569 570// ---------------------------------------------------------------------------- 571 572function SetupFunction() { 573 InstallFunctions($Function.prototype, DONT_ENUM, $Array( 574 "toString", FunctionToString 575 )); 576} 577 578SetupFunction(); 579