]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - runtime/Operations.h
JavaScriptCore-1097.3.tar.gz
[apple/javascriptcore.git] / runtime / Operations.h
index c3aa0fa4f65a07302c7577dde378fce739699d94..b2081f3dd9bf89c83bb4d85cd431ac9c95034b9e 100644 (file)
 #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<JSString::Rope> 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<JSString::Rope> 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<JSString::Rope> 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<JSString::Rope> 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<JSString::Rope> 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<double>(v1.getBoolean()) == v2.uncheckedGetNumber();
+                    return static_cast<double>(v1.asBoolean()) == v2.asNumber();
             } else if (v2.isBoolean()) {
                 if (v1.isNumber())
-                    return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean());
+                    return v1.asNumber() == static_cast<double>(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<bool leftFirst>
     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<bool leftFirst>
+    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;