X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/f9bf01c6616d5ddcf65b13b33cedf9e387ff7a63..a253471d7f8e4d91bf6ebabab00155c3b387d3d0:/runtime/Operations.h diff --git a/runtime/Operations.h b/runtime/Operations.h index c3aa0fa..b2081f3 100644 --- a/runtime/Operations.h +++ b/runtime/Operations.h @@ -22,14 +22,13 @@ #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); @@ -37,132 +36,77 @@ namespace JSC { ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) { - if (!s1->length()) + JSGlobalData& globalData = exec->globalData(); + + unsigned length1 = s1->length(); + if (!length1) return s2; - if (!s2->length()) + unsigned length2 = s2->length(); + if (!length2) return s1; - - unsigned ropeLength = s1->ropeLength() + s2->ropeLength(); - JSGlobalData* globalData = &exec->globalData(); - - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, s1, s2); - - unsigned index = 0; - RefPtr rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + if ((length1 + length2) < length1) return throwOutOfMemoryError(exec); - rope->append(index, s1); - rope->append(index, s2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); - } - ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2) - { - unsigned ropeLength = 1 + s2->ropeLength(); - JSGlobalData* globalData = &exec->globalData(); - - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, u1, s2); - - unsigned index = 0; - RefPtr rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) - return throwOutOfMemoryError(exec); - rope->append(index, u1); - rope->append(index, s2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + return JSRopeString::create(globalData, s1, s2); } - ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2) + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3) { - unsigned ropeLength = s1->ropeLength() + 1; JSGlobalData* globalData = &exec->globalData(); - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, s1, u2); - - unsigned index = 0; - RefPtr rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + 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); - rope->append(index, s1); - rope->append(index, u2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + 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) { - ASSERT(count >= 3); - - unsigned ropeLength = 0; - for (unsigned i = 0; i < count; ++i) { - JSValue v = strings[i].jsValue(); - if (LIKELY(v.isString())) - ropeLength += asString(v)->ropeLength(); - else - ++ropeLength; - } - JSGlobalData* globalData = &exec->globalData(); - if (ropeLength == 3) - return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue()); + JSRopeString::RopeBuilder ropeBuilder(*globalData); - RefPtr rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) - return throwOutOfMemoryError(exec); + unsigned oldLength = 0; - unsigned index = 0; for (unsigned i = 0; i < count; ++i) { JSValue v = strings[i].jsValue(); - if (LIKELY(v.isString())) - rope->append(index, asString(v)); - else - rope->append(index, v.toString(exec)); + ropeBuilder.append(v.toString(exec)); + + if (ropeBuilder.length() < oldLength) // True for overflow + return throwOutOfMemoryError(exec); } - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + return ropeBuilder.release(); } - ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args) + ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue) { - unsigned ropeLength = 0; - if (LIKELY(thisValue.isString())) - ropeLength += asString(thisValue)->ropeLength(); - else - ++ropeLength; - for (unsigned i = 0; i < args.size(); ++i) { - JSValue v = args.at(i); - if (LIKELY(v.isString())) - ropeLength += asString(v)->ropeLength(); - else - ++ropeLength; - } + JSGlobalData* globalData = &exec->globalData(); + JSRopeString::RopeBuilder ropeBuilder(*globalData); + ropeBuilder.append(thisValue.toString(exec)); - RefPtr rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) - return throwOutOfMemoryError(exec); + unsigned oldLength = 0; + + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + JSValue v = exec->argument(i); + ropeBuilder.append(v.toString(exec)); - unsigned index = 0; - if (LIKELY(thisValue.isString())) - rope->append(index, asString(thisValue)); - else - rope->append(index, thisValue.toString(exec)); - for (unsigned i = 0; i < args.size(); ++i) { - JSValue v = args.at(i); - if (LIKELY(v.isString())) - rope->append(index, asString(v)); - else - rope->append(index, v.toString(exec)); + if (ropeBuilder.length() < oldLength) // True for overflow + return throwOutOfMemoryError(exec); } - ASSERT(index == ropeLength); - JSGlobalData* globalData = &exec->globalData(); - return new (globalData) JSString(globalData, rope.release()); + return ropeBuilder.release(); } // ECMA 11.9.3 @@ -178,7 +122,7 @@ 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(); @@ -229,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; @@ -256,7 +200,7 @@ namespace JSC { 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; @@ -264,53 +208,71 @@ namespace JSC { return strictEqualSlowCaseInline(exec, v1, 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)) + 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(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)) + 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(callFrame) < asString(p1)->value(callFrame)); } @@ -325,15 +287,11 @@ namespace JSC { ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) { - double left = 0.0, right; - if (v1.getNumber(left) && v2.getNumber(right)) - return jsNumber(callFrame, left + right); + if (v1.isNumber() && v2.isNumber()) + return jsNumber(v1.asNumber() + v2.asNumber()); - if (v1.isString()) { - return v2.isString() - ? jsString(callFrame, asString(v1), asString(v2)) - : jsString(callFrame, asString(v1), v2.toPrimitiveString(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); @@ -341,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) { @@ -353,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; @@ -378,18 +336,18 @@ namespace JSC { if (v.isNull()) return count; - base = asCell(v); + 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(); + asObject(base)->flattenDictionaryObject(callFrame->globalData()); ++count; } } - ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain) + ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain, bool isStrictPut) { ScopeChainIterator iter = scopeChain->begin(); ScopeChainIterator next = iter; @@ -400,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;