X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/ba379fdc102753d6be2c4d937058fe40257329fe..6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174:/runtime/Operations.h diff --git a/runtime/Operations.h b/runtime/Operations.h index f29154d..b2081f3 100644 --- a/runtime/Operations.h +++ b/runtime/Operations.h @@ -22,19 +22,93 @@ #ifndef Operations_h #define Operations_h +#include "ExceptionHelpers.h" #include "Interpreter.h" -#include "JSImmediate.h" -#include "JSNumberCell.h" #include "JSString.h" +#include "JSValueInlineMethods.h" namespace JSC { - NEVER_INLINE JSValue throwOutOfMemoryError(ExecState*); NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue); JSValue jsTypeStringForValue(CallFrame*, JSValue); bool jsIsObjectType(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) + return throwOutOfMemoryError(exec); + + return JSRopeString::create(globalData, s1, s2); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& 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)); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) + { + JSGlobalData* globalData = &exec->globalData(); + JSRopeString::RopeBuilder ropeBuilder(*globalData); + + 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 ropeBuilder.release(); + } + + ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue) + { + JSGlobalData* globalData = &exec->globalData(); + JSRopeString::RopeBuilder ropeBuilder(*globalData); + ropeBuilder.append(thisValue.toString(exec)); + + 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); + } + + return ropeBuilder.release(); + } + // ECMA 11.9.3 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) { @@ -48,12 +122,12 @@ namespace JSC { { do { if (v1.isNumber() && v2.isNumber()) - return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); + return v1.asNumber() == v2.asNumber(); bool s1 = v1.isString(); bool s2 = v2.isString(); if (s1 && s2) - return asString(v1)->value() == asString(v2)->value(); + return asString(v1)->value(exec) == asString(v2)->value(exec); if (v1.isUndefinedOrNull()) { if (v2.isUndefinedOrNull()) @@ -99,10 +173,10 @@ namespace JSC { if (v1.isBoolean()) { if (v2.isNumber()) - return static_cast(v1.getBoolean()) == v2.uncheckedGetNumber(); + return static_cast(v1.asBoolean()) == v2.asNumber(); } else if (v2.isBoolean()) { if (v1.isNumber()) - return v1.uncheckedGetNumber() == static_cast(v2.getBoolean()); + return v1.asNumber() == static_cast(v2.asBoolean()); } return v1 == v2; @@ -110,78 +184,96 @@ namespace JSC { } // ECMA 11.9.3 - ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(JSValue v1, JSValue v2) + 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() == asString(v2)->value(); + return asString(v1)->value(exec) == asString(v2)->value(exec); return v1 == v2; } - inline bool JSValue::strictEqual(JSValue v1, JSValue 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.uncheckedGetNumber() == v2.uncheckedGetNumber(); + return v1.asNumber() == v2.asNumber(); if (!v1.isCell() || !v2.isCell()) return v1 == v2; - return strictEqualSlowCaseInline(v1, v2); + return strictEqualSlowCaseInline(exec, v1, v2); } - inline bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) + // 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 + ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) { if (v1.isInt32() && v2.isInt32()) return v1.asInt32() < v2.asInt32(); - double n1; - double n2; - if (v1.getNumber(n1) && v2.getNumber(n2)) - return n1 < n2; + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() < v2.asNumber(); - JSGlobalData* globalData = &callFrame->globalData(); - if (isJSString(globalData, v1) && isJSString(globalData, v2)) - return asString(v1)->value() < asString(v2)->value(); + if (isJSString(v1) && isJSString(v2)) + return asString(v1)->value(callFrame) < asString(v2)->value(callFrame); + double n1; + double n2; JSValue p1; JSValue p2; - bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); - bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, 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 asString(p1)->value() < asString(p2)->value(); + return asString(p1)->value(callFrame) < asString(p2)->value(callFrame); } - inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) + // 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 + ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) { if (v1.isInt32() && v2.isInt32()) return v1.asInt32() <= v2.asInt32(); - double n1; - double n2; - if (v1.getNumber(n1) && v2.getNumber(n2)) - return n1 <= n2; + if (v1.isNumber() && v2.isNumber()) + return v1.asNumber() <= v2.asNumber(); - JSGlobalData* globalData = &callFrame->globalData(); - if (isJSString(globalData, v1) && isJSString(globalData, v2)) - return !(asString(v2)->value() < asString(v1)->value()); + if (isJSString(v1) && isJSString(v2)) + return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame)); + double n1; + double n2; JSValue p1; JSValue p2; - bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); - bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, 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 !(asString(p2)->value() < asString(p1)->value()); + return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame)); } // Fast-path choices here are based on frequency data from SunSpider: @@ -195,30 +287,11 @@ namespace JSC { ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) { - double left; - double right = 0.0; - - bool rightIsNumber = v2.getNumber(right); - if (rightIsNumber && v1.getNumber(left)) - return jsNumber(callFrame, left + right); + if (v1.isNumber() && v2.isNumber()) + return jsNumber(v1.asNumber() + v2.asNumber()); - bool leftIsString = v1.isString(); - if (leftIsString && v2.isString()) { - RefPtr value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep()); - if (!value) - return throwOutOfMemoryError(callFrame); - return jsString(callFrame, value.release()); - } - - if (rightIsNumber & leftIsString) { - RefPtr value = v2.isInt32() ? - concatenate(asString(v1)->value().rep(), v2.asInt32()) : - concatenate(asString(v1)->value().rep(), right); - - if (!value) - return throwOutOfMemoryError(callFrame); - return jsString(callFrame, value.release()); - } + if (v1.isString() && !v2.isObject()) + return jsString(callFrame, asString(v1), v2.toString(callFrame)); // All other cases are pretty uncommon return jsAddSlowCase(callFrame, v1, v2); @@ -226,7 +299,7 @@ namespace JSC { inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset) { - JSCell* cell = asCell(base); + JSCell* cell = base.asCell(); size_t count = 0; while (slotBase != cell) { @@ -238,14 +311,14 @@ namespace JSC { if (v.isNull()) return 0; - cell = asCell(v); + 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(); + asObject(cell)->flattenDictionaryObject(callFrame->globalData()); if (slotBase == cell) - slotOffset = cell->structure()->get(propertyName); + slotOffset = cell->structure()->get(callFrame->globalData(), propertyName); } ++count; @@ -255,7 +328,26 @@ namespace JSC { return count; } - ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain) + inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base) + { + size_t count = 0; + while (1) { + JSValue v = base->structure()->prototypeForLookup(callFrame); + if (v.isNull()) + return count; + + 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()); + + ++count; + } + } + + ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain, bool isStrictPut) { ScopeChainIterator iter = scopeChain->begin(); ScopeChainIterator next = iter; @@ -266,8 +358,13 @@ namespace JSC { PropertySlot slot; JSObject* base; while (true) { - base = *iter; - if (next == end || base->getPropertySlot(callFrame, property, slot)) + base = iter->get(); + if (next == end) { + if (isStrictPut && !base->getPropertySlot(callFrame, property, slot)) + return JSValue(); + return base; + } + if (base->getPropertySlot(callFrame, property, slot)) return base; iter = next; @@ -277,59 +374,6 @@ namespace JSC { ASSERT_NOT_REACHED(); return JSValue(); } - - ALWAYS_INLINE JSValue concatenateStrings(CallFrame* callFrame, Register* strings, unsigned count) - { - ASSERT(count >= 3); - - // Estimate the amount of space required to hold the entire string. If all - // arguments are strings, we can easily calculate the exact amount of space - // required. For any other arguments, for now let's assume they may require - // 11 UChars of storage. This is enouch to hold any int, and likely is also - // reasonable for the other immediates. We may want to come back and tune - // this value at some point. - unsigned bufferSize = 0; - for (unsigned i = 0; i < count; ++i) { - JSValue v = strings[i].jsValue(); - if (LIKELY(v.isString())) - bufferSize += asString(v)->value().size(); - else - bufferSize += 11; - } - - // Allocate an output string to store the result. - // If the first argument is a String, and if it has the capacity (or can grow - // its capacity) to hold the entire result then use this as a base to concatenate - // onto. Otherwise, allocate a new empty output buffer. - JSValue firstValue = strings[0].jsValue(); - RefPtr resultRep; - if (firstValue.isString() && (resultRep = asString(firstValue)->value().rep())->reserveCapacity(bufferSize)) { - // We're going to concatenate onto the first string - remove it from the list of items to be appended. - ++strings; - --count; - } else - resultRep = UString::Rep::createEmptyBuffer(bufferSize); - UString result(resultRep); - - // Loop over the openards, writing them into the output buffer. - for (unsigned i = 0; i < count; ++i) { - JSValue v = strings[i].jsValue(); - if (LIKELY(v.isString())) - result.append(asString(v)->value()); - else if (v.isInt32()) - result.appendNumeric(v.asInt32()); - else { - double d; - if (v.getNumber(d)) - result.appendNumeric(d); - else - result.append(v.toString(callFrame)); - } - } - - return jsString(callFrame, result); - } - } // namespace JSC #endif // Operations_h