diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/Operations.h')
-rw-r--r-- | Source/JavaScriptCore/runtime/Operations.h | 526 |
1 files changed, 215 insertions, 311 deletions
diff --git a/Source/JavaScriptCore/runtime/Operations.h b/Source/JavaScriptCore/runtime/Operations.h index 8e0a0a393..afac13000 100644 --- a/Source/JavaScriptCore/runtime/Operations.h +++ b/Source/JavaScriptCore/runtime/Operations.h @@ -24,359 +24,263 @@ #include "ExceptionHelpers.h" #include "Interpreter.h" +#include "JSCJSValueInlines.h" +#include "JSFunctionInlines.h" #include "JSProxy.h" #include "JSString.h" -#include "JSValueInlines.h" +#include "StructureInlines.h" namespace JSC { - NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue); - JSValue jsTypeStringForValue(CallFrame*, JSValue); - bool jsIsObjectType(CallFrame*, JSValue); - bool jsIsFunctionType(JSValue); - - ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) - { - JSGlobalData& globalData = exec->globalData(); - - unsigned length1 = s1->length(); - if (!length1) - return s2; - unsigned length2 = s2->length(); - if (!length2) - return s1; - if ((length1 + length2) < length1) +NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue); +JSValue jsTypeStringForValue(CallFrame*, JSValue); +JSValue jsTypeStringForValue(VM&, JSGlobalObject*, JSValue); +bool jsIsObjectType(CallFrame*, JSValue); +bool jsIsFunctionType(JSValue); + +ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) +{ + VM& vm = exec->vm(); + + unsigned length1 = s1->length(); + if (!length1) + return s2; + unsigned length2 = s2->length(); + if (!length2) + return s1; + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + return JSRopeString::create(vm, s1, s2); +} + +ALWAYS_INLINE JSValue jsString(ExecState* exec, const String& u1, const String& u2, const String& u3) +{ + VM* vm = &exec->vm(); + + unsigned length1 = u1.length(); + unsigned length2 = u2.length(); + unsigned length3 = u3.length(); + if (!length1) + return jsString(exec, jsString(vm, u2), jsString(vm, u3)); + if (!length2) + return jsString(exec, jsString(vm, u1), jsString(vm, u3)); + if (!length3) + return jsString(exec, jsString(vm, u1), jsString(vm, u2)); + + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + if ((length1 + length2 + length3) < length3) + return throwOutOfMemoryError(exec); + + return JSRopeString::create(exec->vm(), jsString(vm, u1), jsString(vm, u2), jsString(vm, u3)); +} + +ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) +{ + VM* vm = &exec->vm(); + JSRopeString::RopeBuilder ropeBuilder(*vm); + + unsigned oldLength = 0; + + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[i].jsValue(); + ropeBuilder.append(v.toString(exec)); + + if (ropeBuilder.length() < oldLength) // True for overflow return throwOutOfMemoryError(exec); - - return JSRopeString::create(globalData, s1, s2); - } - - ALWAYS_INLINE JSValue jsString(ExecState* exec, const String& u1, const String& u2, const String& u3) - { - JSGlobalData* globalData = &exec->globalData(); - - unsigned length1 = u1.length(); - unsigned length2 = u2.length(); - unsigned length3 = u3.length(); - if (!length1) - return jsString(exec, jsString(globalData, u2), jsString(globalData, u3)); - if (!length2) - return jsString(exec, jsString(globalData, u1), jsString(globalData, u3)); - if (!length3) - return jsString(exec, jsString(globalData, u1), jsString(globalData, u2)); - - if ((length1 + length2) < length1) - return throwOutOfMemoryError(exec); - if ((length1 + length2 + length3) < length3) - return throwOutOfMemoryError(exec); - - return JSRopeString::create(exec->globalData(), jsString(globalData, u1), jsString(globalData, u2), jsString(globalData, u3)); + oldLength = ropeBuilder.length(); } - ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) - { - JSGlobalData* globalData = &exec->globalData(); - JSRopeString::RopeBuilder ropeBuilder(*globalData); + return ropeBuilder.release(); +} - unsigned oldLength = 0; - - for (unsigned i = 0; i < count; ++i) { - JSValue v = strings[i].jsValue(); - ropeBuilder.append(v.toString(exec)); - - if (ropeBuilder.length() < oldLength) // True for overflow - return throwOutOfMemoryError(exec); - oldLength = ropeBuilder.length(); - } - - return ropeBuilder.release(); - } +ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue) +{ + VM* vm = &exec->vm(); + JSRopeString::RopeBuilder ropeBuilder(*vm); + ropeBuilder.append(thisValue.toString(exec)); - ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue) - { - JSGlobalData* globalData = &exec->globalData(); - JSRopeString::RopeBuilder ropeBuilder(*globalData); - ropeBuilder.append(thisValue.toString(exec)); + unsigned oldLength = 0; - unsigned oldLength = 0; - - for (unsigned i = 0; i < exec->argumentCount(); ++i) { - JSValue v = exec->argument(i); - ropeBuilder.append(v.toString(exec)); - - if (ropeBuilder.length() < oldLength) // True for overflow - return throwOutOfMemoryError(exec); - oldLength = ropeBuilder.length(); - } - - return ropeBuilder.release(); - } - - // ECMA 11.9.3 - inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) - { - if (v1.isInt32() && v2.isInt32()) - return v1 == v2; - - return equalSlowCase(exec, v1, v2); - } - - ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) - { - do { - if (v1.isNumber() && v2.isNumber()) - return v1.asNumber() == v2.asNumber(); - - bool s1 = v1.isString(); - bool s2 = v2.isString(); - if (s1 && s2) - return asString(v1)->value(exec) == asString(v2)->value(exec); - - if (v1.isUndefinedOrNull()) { - if (v2.isUndefinedOrNull()) - return true; - if (!v2.isCell()) - return false; - return v2.asCell()->structure()->masqueradesAsUndefined(exec->lexicalGlobalObject()); - } - - if (v2.isUndefinedOrNull()) { - if (!v1.isCell()) - return false; - return v1.asCell()->structure()->masqueradesAsUndefined(exec->lexicalGlobalObject()); - } - - if (v1.isObject()) { - if (v2.isObject()) - return v1 == v2; - JSValue p1 = v1.toPrimitive(exec); - if (exec->hadException()) - return false; - v1 = p1; - if (v1.isInt32() && v2.isInt32()) - return v1 == v2; - continue; - } - - if (v2.isObject()) { - JSValue p2 = v2.toPrimitive(exec); - if (exec->hadException()) - return false; - v2 = p2; - if (v1.isInt32() && v2.isInt32()) - return v1 == v2; - continue; - } - - if (s1 || s2) { - double d1 = v1.toNumber(exec); - double d2 = v2.toNumber(exec); - return d1 == d2; - } - - if (v1.isBoolean()) { - if (v2.isNumber()) - return static_cast<double>(v1.asBoolean()) == v2.asNumber(); - } else if (v2.isBoolean()) { - if (v1.isNumber()) - return v1.asNumber() == static_cast<double>(v2.asBoolean()); - } - - return v1 == v2; - } while (true); - } + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + JSValue v = exec->argument(i); + ropeBuilder.append(v.toString(exec)); - // ECMA 11.9.3 - ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) - { - ASSERT(v1.isCell() && v2.isCell()); - - if (v1.asCell()->isString() && v2.asCell()->isString()) - return asString(v1)->value(exec) == asString(v2)->value(exec); - - return v1 == v2; - } - - inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) - { - if (v1.isInt32() && v2.isInt32()) - return v1 == v2; - - if (v1.isNumber() && v2.isNumber()) - return v1.asNumber() == v2.asNumber(); - - if (!v1.isCell() || !v2.isCell()) - return v1 == v2; - - return strictEqualSlowCaseInline(exec, v1, v2); + if (ropeBuilder.length() < oldLength) // True for overflow + return throwOutOfMemoryError(exec); + oldLength = ropeBuilder.length(); } - // See ES5 11.8.1/11.8.2/11.8.5 for definition of leftFirst, this value ensures correct - // evaluation ordering for argument conversions for '<' and '>'. For '<' pass the value - // true, for leftFirst, for '>' pass the value false (and reverse operand order). - template<bool leftFirst> - ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) - { - if (v1.isInt32() && v2.isInt32()) - return v1.asInt32() < v2.asInt32(); - - if (v1.isNumber() && v2.isNumber()) - return v1.asNumber() < v2.asNumber(); - - if (isJSString(v1) && isJSString(v2)) - return codePointCompareLessThan(asString(v1)->value(callFrame), asString(v2)->value(callFrame)); - - double n1; - double n2; - JSValue p1; - JSValue p2; - bool wasNotString1; - bool wasNotString2; - if (leftFirst) { - wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); - wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); - } else { - wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); - wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); - } - - if (wasNotString1 | wasNotString2) - return n1 < n2; - return codePointCompareLessThan(asString(p1)->value(callFrame), asString(p2)->value(callFrame)); + return ropeBuilder.release(); +} + +// See ES5 11.8.1/11.8.2/11.8.5 for definition of leftFirst, this value ensures correct +// evaluation ordering for argument conversions for '<' and '>'. For '<' pass the value +// true, for leftFirst, for '>' pass the value false (and reverse operand order). +template<bool leftFirst> +ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) +{ + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() < v2.asInt32(); + + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() < v2.asNumber(); + + if (isJSString(v1) && isJSString(v2)) + return codePointCompareLessThan(asString(v1)->value(callFrame), asString(v2)->value(callFrame)); + + double n1; + double n2; + JSValue p1; + JSValue p2; + bool wasNotString1; + bool wasNotString2; + if (leftFirst) { + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + } else { + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); } - // See ES5 11.8.3/11.8.4/11.8.5 for definition of leftFirst, this value ensures correct - // evaluation ordering for argument conversions for '<=' and '=>'. For '<=' pass the - // value true, for leftFirst, for '=>' pass the value false (and reverse operand order). - template<bool leftFirst> - ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) - { - if (v1.isInt32() && v2.isInt32()) - return v1.asInt32() <= v2.asInt32(); - - if (v1.isNumber() && v2.isNumber()) - return v1.asNumber() <= v2.asNumber(); - - if (isJSString(v1) && isJSString(v2)) - return !codePointCompareLessThan(asString(v2)->value(callFrame), asString(v1)->value(callFrame)); - - double n1; - double n2; - JSValue p1; - JSValue p2; - bool wasNotString1; - bool wasNotString2; - if (leftFirst) { - wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); - wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); - } else { - wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); - wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); - } - - if (wasNotString1 | wasNotString2) - return n1 <= n2; - return !codePointCompareLessThan(asString(p2)->value(callFrame), asString(p1)->value(callFrame)); + if (wasNotString1 | wasNotString2) + return n1 < n2; + return codePointCompareLessThan(asString(p1)->value(callFrame), asString(p2)->value(callFrame)); +} + +// See ES5 11.8.3/11.8.4/11.8.5 for definition of leftFirst, this value ensures correct +// evaluation ordering for argument conversions for '<=' and '=>'. For '<=' pass the +// value true, for leftFirst, for '=>' pass the value false (and reverse operand order). +template<bool leftFirst> +ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) +{ + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() <= v2.asInt32(); + + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() <= v2.asNumber(); + + if (isJSString(v1) && isJSString(v2)) + return !codePointCompareLessThan(asString(v2)->value(callFrame), asString(v1)->value(callFrame)); + + double n1; + double n2; + JSValue p1; + JSValue p2; + bool wasNotString1; + bool wasNotString2; + if (leftFirst) { + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + } else { + wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); + wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); } - // Fast-path choices here are based on frequency data from SunSpider: - // <times> Add case: <t1> <t2> - // --------------------------- - // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values) - // 247412 Add case: 5 5 - // 20900 Add case: 5 6 - // 13962 Add case: 5 3 - // 4000 Add case: 3 5 - - ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) - { - if (v1.isNumber() && v2.isNumber()) - return jsNumber(v1.asNumber() + v2.asNumber()); + if (wasNotString1 | wasNotString2) + return n1 <= n2; + return !codePointCompareLessThan(asString(p2)->value(callFrame), asString(p1)->value(callFrame)); +} + +// Fast-path choices here are based on frequency data from SunSpider: +// <times> Add case: <t1> <t2> +// --------------------------- +// 5626160 Add case: 3 3 (of these, 3637690 are for immediate values) +// 247412 Add case: 5 5 +// 20900 Add case: 5 6 +// 13962 Add case: 5 3 +// 4000 Add case: 3 5 + +ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) +{ + if (v1.isNumber() && v2.isNumber()) + return jsNumber(v1.asNumber() + v2.asNumber()); - if (v1.isString() && !v2.isObject()) - return jsString(callFrame, asString(v1), v2.toString(callFrame)); + if (v1.isString() && !v2.isObject()) + return jsString(callFrame, asString(v1), v2.toString(callFrame)); - // All other cases are pretty uncommon - return jsAddSlowCase(callFrame, v1, v2); - } + // All other cases are pretty uncommon + return jsAddSlowCase(callFrame, v1, v2); +} #define InvalidPrototypeChain (std::numeric_limits<size_t>::max()) - inline size_t normalizePrototypeChainForChainAccess(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, PropertyOffset& slotOffset) - { - JSCell* cell = base.asCell(); - size_t count = 0; +inline size_t normalizePrototypeChainForChainAccess(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, PropertyOffset& slotOffset) +{ + JSCell* cell = base.asCell(); + size_t count = 0; - while (slotBase != cell) { - if (cell->isProxy()) - return InvalidPrototypeChain; + while (slotBase != cell) { + if (cell->isProxy()) + return InvalidPrototypeChain; - if (cell->structure()->typeInfo().hasImpureGetOwnPropertySlot()) - return InvalidPrototypeChain; + if (cell->structure()->typeInfo().hasImpureGetOwnPropertySlot()) + return InvalidPrototypeChain; - JSValue v = cell->structure()->prototypeForLookup(callFrame); + JSValue v = cell->structure()->prototypeForLookup(callFrame); - // If we didn't find slotBase in base's prototype chain, then base - // must be a proxy for another object. + // If we didn't find slotBase in base's prototype chain, then base + // must be a proxy for another object. - if (v.isNull()) - return InvalidPrototypeChain; + if (v.isNull()) + return InvalidPrototypeChain; - cell = v.asCell(); + cell = v.asCell(); - // Since we're accessing a prototype in a loop, it's a good bet that it - // should not be treated as a dictionary. - if (cell->structure()->isDictionary()) { - asObject(cell)->flattenDictionaryObject(callFrame->globalData()); - if (slotBase == cell) - slotOffset = cell->structure()->get(callFrame->globalData(), propertyName); - } - - ++count; + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (cell->structure()->isDictionary()) { + asObject(cell)->flattenDictionaryObject(callFrame->vm()); + if (slotBase == cell) + slotOffset = cell->structure()->get(callFrame->vm(), propertyName); } - - ASSERT(count); - return count; + + ++count; } - - inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base) - { - size_t count = 0; - while (1) { - if (base->isProxy()) - return InvalidPrototypeChain; + + ASSERT(count); + return count; +} + +inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base) +{ + size_t count = 0; + while (1) { + if (base->isProxy()) + return InvalidPrototypeChain; - JSValue v = base->structure()->prototypeForLookup(callFrame); - if (v.isNull()) - return count; + JSValue v = base->structure()->prototypeForLookup(callFrame); + if (v.isNull()) + return count; - base = v.asCell(); + base = v.asCell(); - // Since we're accessing a prototype in a loop, it's a good bet that it - // should not be treated as a dictionary. - if (base->structure()->isDictionary()) - asObject(base)->flattenDictionaryObject(callFrame->globalData()); + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (base->structure()->isDictionary()) + asObject(base)->flattenDictionaryObject(callFrame->vm()); - ++count; - } + ++count; } +} - inline bool isPrototypeChainNormalized(JSGlobalObject* globalObject, Structure* structure) - { - for (;;) { - if (structure->typeInfo().type() == ProxyType) - return false; +inline bool isPrototypeChainNormalized(JSGlobalObject* globalObject, Structure* structure) +{ + for (;;) { + if (structure->typeInfo().type() == ProxyType) + return false; - JSValue v = structure->prototypeForLookup(globalObject); - if (v.isNull()) - return true; + JSValue v = structure->prototypeForLookup(globalObject); + if (v.isNull()) + return true; - structure = v.asCell()->structure(); + structure = v.asCell()->structure(); - if (structure->isDictionary()) - return false; - } + if (structure->isDictionary()) + return false; } +} } // namespace JSC |