// Copyright 2006-2008 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This files contains runtime support implemented in JavaScript. // CAUTION: Some of the functions specified in this file are called // directly from compiled code. These are the functions with names in // ALL CAPS. The compiled code passes the first argument in 'this'. /* ----------------------------------- - - - C o m p a r i s o n - - - ----------------------------------- */ // The following declarations are shared with other native JS files. // They are all declared at this one spot to avoid redeclaration errors. var EQUALS; var STRICT_EQUALS; var COMPARE; var COMPARE_STRONG; var ADD; var ADD_STRONG; var STRING_ADD_LEFT; var STRING_ADD_LEFT_STRONG; var STRING_ADD_RIGHT; var STRING_ADD_RIGHT_STRONG; var SUB; var SUB_STRONG; var MUL; var MUL_STRONG; var DIV; var DIV_STRONG; var MOD; var MOD_STRONG; var BIT_OR; var BIT_OR_STRONG; var BIT_AND; var BIT_AND_STRONG; var BIT_XOR; var BIT_XOR_STRONG; var SHL; var SHL_STRONG; var SAR; var SAR_STRONG; var SHR; var SHR_STRONG; var DELETE; var IN; var INSTANCE_OF; var CALL_NON_FUNCTION; var CALL_NON_FUNCTION_AS_CONSTRUCTOR; var CALL_FUNCTION_PROXY; var CALL_FUNCTION_PROXY_AS_CONSTRUCTOR; var CONCAT_ITERABLE_TO_ARRAY; var APPLY_PREPARE; var REFLECT_APPLY_PREPARE; var REFLECT_CONSTRUCT_PREPARE; var STACK_OVERFLOW; var TO_OBJECT; var TO_NUMBER; var TO_STRING; var TO_NAME; var $defaultNumber; var $defaultString; var $NaN; var $nonNumberToNumber; var $nonStringToString; var $sameValue; var $sameValueZero; var $toBoolean; var $toInt32; var $toInteger; var $toLength; var $toName; var $toNumber; var $toObject; var $toPositiveInteger; var $toPrimitive; var $toString; var $toUint32; (function(global, utils) { %CheckIsBootstrapping(); var GlobalArray = global.Array; var GlobalBoolean = global.Boolean; var GlobalString = global.String; var GlobalNumber = global.Number; // ---------------------------------------------------------------------------- // ECMA-262 Section 11.9.3. EQUALS = function EQUALS(y) { if (IS_STRING(this) && IS_STRING(y)) return %StringEquals(this, y); var x = this; while (true) { if (IS_NUMBER(x)) { while (true) { if (IS_NUMBER(y)) return %NumberEquals(x, y); if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal if (IS_SYMBOL(y) || IS_FLOAT32X4(y)) return 1; // not equal if (!IS_SPEC_OBJECT(y)) { // String or boolean. return %NumberEquals(x, %$toNumber(y)); } y = %$toPrimitive(y, NO_HINT); } } else if (IS_STRING(x)) { while (true) { if (IS_STRING(y)) return %StringEquals(x, y); if (IS_SYMBOL(y) || IS_FLOAT32X4(y)) return 1; // not equal if (IS_NUMBER(y)) return %NumberEquals(%$toNumber(x), y); if (IS_BOOLEAN(y)) return %NumberEquals(%$toNumber(x), %$toNumber(y)); if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal y = %$toPrimitive(y, NO_HINT); } } else if (IS_SYMBOL(x)) { if (IS_SYMBOL(y)) return %_ObjectEquals(x, y) ? 0 : 1; return 1; // not equal } else if (IS_BOOLEAN(x)) { if (IS_BOOLEAN(y)) return %_ObjectEquals(x, y) ? 0 : 1; if (IS_NULL_OR_UNDEFINED(y)) return 1; if (IS_NUMBER(y)) return %NumberEquals(%$toNumber(x), y); if (IS_STRING(y)) return %NumberEquals(%$toNumber(x), %$toNumber(y)); if (IS_SYMBOL(y) || IS_FLOAT32X4(y)) return 1; // not equal // y is object. x = %$toNumber(x); y = %$toPrimitive(y, NO_HINT); } else if (IS_NULL_OR_UNDEFINED(x)) { return IS_NULL_OR_UNDEFINED(y) ? 0 : 1; } else if (IS_FLOAT32X4(x)) { if (IS_FLOAT32X4(y)) return %Float32x4Equals(x, y); return 1; // not equal } else { // x is an object. if (IS_SPEC_OBJECT(y)) { return %_ObjectEquals(x, y) ? 0 : 1; } if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal if (IS_SYMBOL(y) || IS_FLOAT32X4(y)) return 1; // not equal if (IS_BOOLEAN(y)) y = %$toNumber(y); x = %$toPrimitive(x, NO_HINT); } } } // ECMA-262, section 11.9.4, page 56. STRICT_EQUALS = function STRICT_EQUALS(x) { if (IS_STRING(this)) { if (!IS_STRING(x)) return 1; // not equal return %StringEquals(this, x); } if (IS_NUMBER(this)) { if (!IS_NUMBER(x)) return 1; // not equal return %NumberEquals(this, x); } if (IS_FLOAT32X4(this) && IS_FLOAT32X4(x)) return %Float32x4Equals(this, x); // If anything else gets here, we just do simple identity check. // Objects (including functions), null, undefined and booleans were // checked in the CompareStub, so there should be nothing left. return %_ObjectEquals(this, x) ? 0 : 1; } // ECMA-262, section 11.8.5, page 53. The 'ncr' parameter is used as // the result when either (or both) the operands are NaN. COMPARE = function COMPARE(x, ncr) { var left; var right; // Fast cases for string, numbers and undefined compares. if (IS_STRING(this)) { if (IS_STRING(x)) return %_StringCompare(this, x); if (IS_UNDEFINED(x)) return ncr; left = this; } else if (IS_NUMBER(this)) { if (IS_NUMBER(x)) return %NumberCompare(this, x, ncr); if (IS_UNDEFINED(x)) return ncr; left = this; } else if (IS_UNDEFINED(this)) { if (!IS_UNDEFINED(x)) { %$toPrimitive(x, NUMBER_HINT); } return ncr; } else if (IS_UNDEFINED(x)) { %$toPrimitive(this, NUMBER_HINT); return ncr; } else { left = %$toPrimitive(this, NUMBER_HINT); } right = %$toPrimitive(x, NUMBER_HINT); if (IS_STRING(left) && IS_STRING(right)) { return %_StringCompare(left, right); } else { var left_number = %$toNumber(left); var right_number = %$toNumber(right); if (NUMBER_IS_NAN(left_number) || NUMBER_IS_NAN(right_number)) return ncr; return %NumberCompare(left_number, right_number, ncr); } } // Strong mode COMPARE throws if an implicit conversion would be performed COMPARE_STRONG = function COMPARE_STRONG(x, ncr) { if (IS_STRING(this) && IS_STRING(x)) return %_StringCompare(this, x); if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberCompare(this, x, ncr); throw %MakeTypeError(kStrongImplicitConversion); } /* ----------------------------------- - - - A r i t h m e t i c - - - ----------------------------------- */ // ECMA-262, section 11.6.1, page 50. ADD = function ADD(x) { // Fast case: Check for number operands and do the addition. if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x); if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x); // Default implementation. var a = %$toPrimitive(this, NO_HINT); var b = %$toPrimitive(x, NO_HINT); if (IS_STRING(a)) { return %_StringAdd(a, %$toString(b)); } else if (IS_STRING(b)) { return %_StringAdd(%$nonStringToString(a), b); } else { return %NumberAdd(%$toNumber(a), %$toNumber(b)); } } // Strong mode ADD throws if an implicit conversion would be performed ADD_STRONG = function ADD_STRONG(x) { if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x); if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x); throw %MakeTypeError(kStrongImplicitConversion); } // Left operand (this) is already a string. STRING_ADD_LEFT = function STRING_ADD_LEFT(y) { if (!IS_STRING(y)) { if (IS_STRING_WRAPPER(y) && %_IsStringWrapperSafeForDefaultValueOf(y)) { y = %_ValueOf(y); } else { y = IS_NUMBER(y) ? %_NumberToString(y) : %$toString(%$toPrimitive(y, NO_HINT)); } } return %_StringAdd(this, y); } // Left operand (this) is already a string. STRING_ADD_LEFT_STRONG = function STRING_ADD_LEFT_STRONG(y) { if (IS_STRING(y)) { return %_StringAdd(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // Right operand (y) is already a string. STRING_ADD_RIGHT = function STRING_ADD_RIGHT(y) { var x = this; if (!IS_STRING(x)) { if (IS_STRING_WRAPPER(x) && %_IsStringWrapperSafeForDefaultValueOf(x)) { x = %_ValueOf(x); } else { x = IS_NUMBER(x) ? %_NumberToString(x) : %$toString(%$toPrimitive(x, NO_HINT)); } } return %_StringAdd(x, y); } // Right operand (y) is already a string. STRING_ADD_RIGHT_STRONG = function STRING_ADD_RIGHT_STRONG(y) { if (IS_STRING(this)) { return %_StringAdd(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.6.2, page 50. SUB = function SUB(y) { var x = IS_NUMBER(this) ? this : %$nonNumberToNumber(this); if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); return %NumberSub(x, y); } // Strong mode SUB throws if an implicit conversion would be performed SUB_STRONG = function SUB_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberSub(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.5.1, page 48. MUL = function MUL(y) { var x = IS_NUMBER(this) ? this : %$nonNumberToNumber(this); if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); return %NumberMul(x, y); } // Strong mode MUL throws if an implicit conversion would be performed MUL_STRONG = function MUL_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberMul(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.5.2, page 49. DIV = function DIV(y) { var x = IS_NUMBER(this) ? this : %$nonNumberToNumber(this); if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); return %NumberDiv(x, y); } // Strong mode DIV throws if an implicit conversion would be performed DIV_STRONG = function DIV_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberDiv(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.5.3, page 49. MOD = function MOD(y) { var x = IS_NUMBER(this) ? this : %$nonNumberToNumber(this); if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); return %NumberMod(x, y); } // Strong mode MOD throws if an implicit conversion would be performed MOD_STRONG = function MOD_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberMod(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } /* ------------------------------------------- - - - B i t o p e r a t i o n s - - - ------------------------------------------- */ // ECMA-262, section 11.10, page 57. BIT_OR = function BIT_OR(y) { var x = IS_NUMBER(this) ? this : %$nonNumberToNumber(this); if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); return %NumberOr(x, y); } // Strong mode BIT_OR throws if an implicit conversion would be performed BIT_OR_STRONG = function BIT_OR_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberOr(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.10, page 57. BIT_AND = function BIT_AND(y) { var x; if (IS_NUMBER(this)) { x = this; if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); } else { x = %$nonNumberToNumber(this); // Make sure to convert the right operand to a number before // bailing out in the fast case, but after converting the // left operand. This ensures that valueOf methods on the right // operand are always executed. if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); // Optimize for the case where we end up AND'ing a value // that doesn't convert to a number. This is common in // certain benchmarks. if (NUMBER_IS_NAN(x)) return 0; } return %NumberAnd(x, y); } // Strong mode BIT_AND throws if an implicit conversion would be performed BIT_AND_STRONG = function BIT_AND_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberAnd(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.10, page 57. BIT_XOR = function BIT_XOR(y) { var x = IS_NUMBER(this) ? this : %$nonNumberToNumber(this); if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); return %NumberXor(x, y); } // Strong mode BIT_XOR throws if an implicit conversion would be performed BIT_XOR_STRONG = function BIT_XOR_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberXor(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.7.1, page 51. SHL = function SHL(y) { var x = IS_NUMBER(this) ? this : %$nonNumberToNumber(this); if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); return %NumberShl(x, y); } // Strong mode SHL throws if an implicit conversion would be performed SHL_STRONG = function SHL_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberShl(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.7.2, page 51. SAR = function SAR(y) { var x; if (IS_NUMBER(this)) { x = this; if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); } else { x = %$nonNumberToNumber(this); // Make sure to convert the right operand to a number before // bailing out in the fast case, but after converting the // left operand. This ensures that valueOf methods on the right // operand are always executed. if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); // Optimize for the case where we end up shifting a value // that doesn't convert to a number. This is common in // certain benchmarks. if (NUMBER_IS_NAN(x)) return 0; } return %NumberSar(x, y); } // Strong mode SAR throws if an implicit conversion would be performed SAR_STRONG = function SAR_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberSar(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } // ECMA-262, section 11.7.3, page 52. SHR = function SHR(y) { var x = IS_NUMBER(this) ? this : %$nonNumberToNumber(this); if (!IS_NUMBER(y)) y = %$nonNumberToNumber(y); return %NumberShr(x, y); } // Strong mode SHR throws if an implicit conversion would be performed SHR_STRONG = function SHR_STRONG(y) { if (IS_NUMBER(this) && IS_NUMBER(y)) { return %NumberShr(this, y); } throw %MakeTypeError(kStrongImplicitConversion); } /* ----------------------------- - - - H e l p e r s - - - ----------------------------- */ // ECMA-262, section 11.4.1, page 46. DELETE = function DELETE(key, language_mode) { return %DeleteProperty(%$toObject(this), key, language_mode); } // ECMA-262, section 11.8.7, page 54. IN = function IN(x) { if (!IS_SPEC_OBJECT(x)) { throw %MakeTypeError(kInvalidInOperatorUse, this, x); } if (%_IsNonNegativeSmi(this)) { if (IS_ARRAY(x) && %_HasFastPackedElements(x)) { return this < x.length; } return %HasElement(x, this); } return %HasProperty(x, %$toName(this)); } // ECMA-262, section 11.8.6, page 54. To make the implementation more // efficient, the return value should be zero if the 'this' is an // instance of F, and non-zero if not. This makes it possible to avoid // an expensive ToBoolean conversion in the generated code. INSTANCE_OF = function INSTANCE_OF(F) { var V = this; if (!IS_SPEC_FUNCTION(F)) { throw %MakeTypeError(kInstanceofFunctionExpected, F); } // If V is not an object, return false. if (!IS_SPEC_OBJECT(V)) { return 1; } // Check if function is bound, if so, get [[BoundFunction]] from it // and use that instead of F. var bindings = %BoundFunctionGetBindings(F); if (bindings) { F = bindings[kBoundFunctionIndex]; // Always a non-bound function. } // Get the prototype of F; if it is not an object, throw an error. var O = F.prototype; if (!IS_SPEC_OBJECT(O)) { throw %MakeTypeError(kInstanceofNonobjectProto, O); } // Return whether or not O is in the prototype chain of V. return %IsInPrototypeChain(O, V) ? 0 : 1; } CALL_NON_FUNCTION = function CALL_NON_FUNCTION() { var delegate = %GetFunctionDelegate(this); if (!IS_FUNCTION(delegate)) { var callsite = %RenderCallSite(); if (callsite == "") callsite = typeof this; throw %MakeTypeError(kCalledNonCallable, callsite); } return %Apply(delegate, this, arguments, 0, %_ArgumentsLength()); } CALL_NON_FUNCTION_AS_CONSTRUCTOR = function CALL_NON_FUNCTION_AS_CONSTRUCTOR() { var delegate = %GetConstructorDelegate(this); if (!IS_FUNCTION(delegate)) { var callsite = %RenderCallSite(); if (callsite == "") callsite = typeof this; throw %MakeTypeError(kCalledNonCallable, callsite); } return %Apply(delegate, this, arguments, 0, %_ArgumentsLength()); } CALL_FUNCTION_PROXY = function CALL_FUNCTION_PROXY() { var arity = %_ArgumentsLength() - 1; var proxy = %_Arguments(arity); // The proxy comes in as an additional arg. var trap = %GetCallTrap(proxy); return %Apply(trap, this, arguments, 0, arity); } CALL_FUNCTION_PROXY_AS_CONSTRUCTOR = function CALL_FUNCTION_PROXY_AS_CONSTRUCTOR () { var proxy = this; var trap = %GetConstructTrap(proxy); return %Apply(trap, this, arguments, 0, %_ArgumentsLength()); } APPLY_PREPARE = function APPLY_PREPARE(args) { var length; // First check whether length is a positive Smi and args is an // array. This is the fast case. If this fails, we do the slow case // that takes care of more eventualities. if (IS_ARRAY(args)) { length = args.length; if (%_IsSmi(length) && length >= 0 && length < kSafeArgumentsLength && IS_SPEC_FUNCTION(this)) { return length; } } length = (args == null) ? 0 : %$toUint32(args.length); // We can handle any number of apply arguments if the stack is // big enough, but sanity check the value to avoid overflow when // multiplying with pointer size. if (length > kSafeArgumentsLength) throw %MakeRangeError(kStackOverflow); if (!IS_SPEC_FUNCTION(this)) { throw %MakeTypeError(kApplyNonFunction, %$toString(this), typeof this); } // Make sure the arguments list has the right type. if (args != null && !IS_SPEC_OBJECT(args)) { throw %MakeTypeError(kWrongArgs, "Function.prototype.apply"); } // Return the length which is the number of arguments to copy to the // stack. It is guaranteed to be a small integer at this point. return length; } REFLECT_APPLY_PREPARE = function REFLECT_APPLY_PREPARE(args) { var length; // First check whether length is a positive Smi and args is an // array. This is the fast case. If this fails, we do the slow case // that takes care of more eventualities. if (IS_ARRAY(args)) { length = args.length; if (%_IsSmi(length) && length >= 0 && length < kSafeArgumentsLength && IS_SPEC_FUNCTION(this)) { return length; } } if (!IS_SPEC_FUNCTION(this)) { throw %MakeTypeError(kCalledNonCallable, %$toString(this)); } if (!IS_SPEC_OBJECT(args)) { throw %MakeTypeError(kWrongArgs, "Reflect.apply"); } length = %$toLength(args.length); // We can handle any number of apply arguments if the stack is // big enough, but sanity check the value to avoid overflow when // multiplying with pointer size. if (length > kSafeArgumentsLength) throw %MakeRangeError(kStackOverflow); // Return the length which is the number of arguments to copy to the // stack. It is guaranteed to be a small integer at this point. return length; } REFLECT_CONSTRUCT_PREPARE = function REFLECT_CONSTRUCT_PREPARE( args, newTarget) { var length; var ctorOk = IS_SPEC_FUNCTION(this) && %IsConstructor(this); var newTargetOk = IS_SPEC_FUNCTION(newTarget) && %IsConstructor(newTarget); // First check whether length is a positive Smi and args is an // array. This is the fast case. If this fails, we do the slow case // that takes care of more eventualities. if (IS_ARRAY(args)) { length = args.length; if (%_IsSmi(length) && length >= 0 && length < kSafeArgumentsLength && ctorOk && newTargetOk) { return length; } } if (!ctorOk) { if (!IS_SPEC_FUNCTION(this)) { throw %MakeTypeError(kCalledNonCallable, %$toString(this)); } else { throw %MakeTypeError(kNotConstructor, %$toString(this)); } } if (!newTargetOk) { if (!IS_SPEC_FUNCTION(newTarget)) { throw %MakeTypeError(kCalledNonCallable, %$toString(newTarget)); } else { throw %MakeTypeError(kNotConstructor, %$toString(newTarget)); } } if (!IS_SPEC_OBJECT(args)) { throw %MakeTypeError(kWrongArgs, "Reflect.construct"); } length = %$toLength(args.length); // We can handle any number of apply arguments if the stack is // big enough, but sanity check the value to avoid overflow when // multiplying with pointer size. if (length > kSafeArgumentsLength) throw %MakeRangeError(kStackOverflow); // Return the length which is the number of arguments to copy to the // stack. It is guaranteed to be a small integer at this point. return length; } CONCAT_ITERABLE_TO_ARRAY = function CONCAT_ITERABLE_TO_ARRAY(iterable) { return %$concatIterableToArray(this, iterable); }; STACK_OVERFLOW = function STACK_OVERFLOW(length) { throw %MakeRangeError(kStackOverflow); } // Convert the receiver to an object - forward to ToObject. TO_OBJECT = function TO_OBJECT() { return %$toObject(this); } // Convert the receiver to a number - forward to ToNumber. TO_NUMBER = function TO_NUMBER() { return %$toNumber(this); } // Convert the receiver to a string - forward to ToString. TO_STRING = function TO_STRING() { return %$toString(this); } // Convert the receiver to a string or symbol - forward to ToName. TO_NAME = function TO_NAME() { return %$toName(this); } /* ------------------------------------- - - - C o n v e r s i o n s - - - ------------------------------------- */ // ECMA-262, section 9.1, page 30. Use null/undefined for no hint, // (1) for number hint, and (2) for string hint. function ToPrimitive(x, hint) { // Fast case check. if (IS_STRING(x)) return x; // Normal behavior. if (!IS_SPEC_OBJECT(x)) return x; if (IS_FLOAT32X4(x)) return x; if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT; return (hint == NUMBER_HINT) ? DefaultNumber(x) : DefaultString(x); } // ECMA-262, section 9.2, page 30 function ToBoolean(x) { if (IS_BOOLEAN(x)) return x; if (IS_STRING(x)) return x.length != 0; if (x == null) return false; if (IS_NUMBER(x)) return !((x == 0) || NUMBER_IS_NAN(x)); return true; } // ECMA-262, section 9.3, page 31. function ToNumber(x) { if (IS_NUMBER(x)) return x; if (IS_STRING(x)) { return %_HasCachedArrayIndex(x) ? %_GetCachedArrayIndex(x) : %StringToNumber(x); } if (IS_BOOLEAN(x)) return x ? 1 : 0; if (IS_UNDEFINED(x)) return NAN; // Types that can't be converted to number are caught in DefaultNumber. return (IS_NULL(x)) ? 0 : ToNumber(DefaultNumber(x)); } function NonNumberToNumber(x) { if (IS_STRING(x)) { return %_HasCachedArrayIndex(x) ? %_GetCachedArrayIndex(x) : %StringToNumber(x); } if (IS_BOOLEAN(x)) return x ? 1 : 0; if (IS_UNDEFINED(x)) return NAN; // Types that can't be converted to number are caught in DefaultNumber. return (IS_NULL(x)) ? 0 : ToNumber(DefaultNumber(x)); } // ECMA-262, section 9.8, page 35. function ToString(x) { if (IS_STRING(x)) return x; if (IS_NUMBER(x)) return %_NumberToString(x); if (IS_BOOLEAN(x)) return x ? 'true' : 'false'; if (IS_UNDEFINED(x)) return 'undefined'; // Types that can't be converted to string are caught in DefaultString. return (IS_NULL(x)) ? 'null' : ToString(DefaultString(x)); } function NonStringToString(x) { if (IS_NUMBER(x)) return %_NumberToString(x); if (IS_BOOLEAN(x)) return x ? 'true' : 'false'; if (IS_UNDEFINED(x)) return 'undefined'; // Types that can't be converted to string are caught in DefaultString. return (IS_NULL(x)) ? 'null' : ToString(DefaultString(x)); } // ES6 symbols function ToName(x) { return IS_SYMBOL(x) ? x : ToString(x); } // ECMA-262, section 9.9, page 36. function ToObject(x) { if (IS_STRING(x)) return new GlobalString(x); if (IS_NUMBER(x)) return new GlobalNumber(x); if (IS_BOOLEAN(x)) return new GlobalBoolean(x); if (IS_SYMBOL(x)) return %NewSymbolWrapper(x); if (IS_FLOAT32X4(x)) return %NewFloat32x4Wrapper(x); if (IS_NULL_OR_UNDEFINED(x) && !IS_UNDETECTABLE(x)) { throw MakeTypeError(kUndefinedOrNullToObject); } return x; } // ECMA-262, section 9.4, page 34. function ToInteger(x) { if (%_IsSmi(x)) return x; return %NumberToInteger(ToNumber(x)); } // ES6, draft 08-24-14, section 7.1.15 function ToLength(arg) { arg = ToInteger(arg); if (arg < 0) return 0; return arg < GlobalNumber.MAX_SAFE_INTEGER ? arg : GlobalNumber.MAX_SAFE_INTEGER; } // ECMA-262, section 9.6, page 34. function ToUint32(x) { if (%_IsSmi(x) && x >= 0) return x; return %NumberToJSUint32(ToNumber(x)); } // ECMA-262, section 9.5, page 34 function ToInt32(x) { if (%_IsSmi(x)) return x; return %NumberToJSInt32(ToNumber(x)); } // ES5, section 9.12 function SameValue(x, y) { if (typeof x != typeof y) return false; if (IS_NUMBER(x)) { if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true; // x is +0 and y is -0 or vice versa. if (x === 0 && y === 0 && %_IsMinusZero(x) != %_IsMinusZero(y)) { return false; } } if (IS_FLOAT32X4(x)) { return %Float32x4SameValue(x, y); } return x === y; } // ES6, section 7.2.4 function SameValueZero(x, y) { if (typeof x != typeof y) return false; if (IS_NUMBER(x)) { if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true; } if (IS_FLOAT32X4(x)) { return %Float32x4SameValueZero(x, y); } return x === y; } function ConcatIterableToArray(target, iterable) { var index = target.length; for (var element of iterable) { %AddElement(target, index++, element); } return target; } /* --------------------------------- - - - U t i l i t i e s - - - --------------------------------- */ // Returns if the given x is a primitive value - not an object or a // function. function IsPrimitive(x) { // Even though the type of null is "object", null is still // considered a primitive value. IS_SPEC_OBJECT handles this correctly // (i.e., it will return false if x is null). return !IS_SPEC_OBJECT(x); } // ES6, draft 10-14-14, section 22.1.3.1.1 function IsConcatSpreadable(O) { if (!IS_SPEC_OBJECT(O)) return false; var spreadable = O[symbolIsConcatSpreadable]; if (IS_UNDEFINED(spreadable)) return IS_ARRAY(O); return ToBoolean(spreadable); } // ECMA-262, section 8.6.2.6, page 28. function DefaultNumber(x) { var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf)) { var v = %_CallFunction(x, valueOf); if (IS_SYMBOL(v)) throw MakeTypeError(kSymbolToNumber); if (IS_FLOAT32X4(v)) throw MakeTypeError(kSimdToNumber); if (IsPrimitive(v)) return v; } var toString = x.toString; if (IS_SPEC_FUNCTION(toString)) { var s = %_CallFunction(x, toString); if (IsPrimitive(s)) return s; } throw MakeTypeError(kCannotConvertToPrimitive); } // ECMA-262, section 8.6.2.6, page 28. function DefaultString(x) { if (!IS_SYMBOL_WRAPPER(x)) { if (IS_SYMBOL(x)) throw MakeTypeError(kSymbolToString); var toString = x.toString; if (IS_SPEC_FUNCTION(toString)) { var s = %_CallFunction(x, toString); if (IsPrimitive(s)) return s; } var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf)) { var v = %_CallFunction(x, valueOf); if (IsPrimitive(v)) return v; } } throw MakeTypeError(kCannotConvertToPrimitive); } function ToPositiveInteger(x, rangeErrorIndex) { var i = TO_INTEGER_MAP_MINUS_ZERO(x); if (i < 0) throw MakeRangeError(rangeErrorIndex); return i; } //---------------------------------------------------------------------------- // NOTE: Setting the prototype for Array must take place as early as // possible due to code generation for array literals. When // generating code for a array literal a boilerplate array is created // that is cloned when running the code. It is essential that the // boilerplate gets the right prototype. %FunctionSetPrototype(GlobalArray, new GlobalArray(0)); //---------------------------------------------------------------------------- $concatIterableToArray = ConcatIterableToArray; $defaultNumber = DefaultNumber; $defaultString = DefaultString; $NaN = %GetRootNaN(); $nonNumberToNumber = NonNumberToNumber; $nonStringToString = NonStringToString; $sameValue = SameValue; $sameValueZero = SameValueZero; $toBoolean = ToBoolean; $toInt32 = ToInt32; $toInteger = ToInteger; $toLength = ToLength; $toName = ToName; $toNumber = ToNumber; $toObject = ToObject; $toPositiveInteger = ToPositiveInteger; $toPrimitive = ToPrimitive; $toString = ToString; $toUint32 = ToUint32; })