]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - runtime/Operations.h
JavaScriptCore-1097.3.tar.gz
[apple/javascriptcore.git] / runtime / Operations.h
index f29154da830a27fa3c6b7425f87bcc8a065902f2..b2081f3dd9bf89c83bb4d85cd431ac9c95034b9e 100644 (file)
 #ifndef Operations_h
 #define Operations_h
 
 #ifndef Operations_h
 #define Operations_h
 
+#include "ExceptionHelpers.h"
 #include "Interpreter.h"
 #include "Interpreter.h"
-#include "JSImmediate.h"
-#include "JSNumberCell.h"
 #include "JSString.h"
 #include "JSString.h"
+#include "JSValueInlineMethods.h"
 
 namespace JSC {
 
 
 namespace JSC {
 
-    NEVER_INLINE JSValue throwOutOfMemoryError(ExecState*);
     NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
     JSValue jsTypeStringForValue(CallFrame*, JSValue);
     bool jsIsObjectType(JSValue);
     bool jsIsFunctionType(JSValue);
 
     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)
     {
     // 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())
     {
         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)
 
             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())
 
             if (v1.isUndefinedOrNull()) {
                 if (v2.isUndefinedOrNull())
@@ -99,10 +173,10 @@ namespace JSC {
 
             if (v1.isBoolean()) {
                 if (v2.isNumber())
 
             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())
             } 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;
             }
 
             return v1 == v2;
@@ -110,78 +184,96 @@ namespace JSC {
     }
 
     // ECMA 11.9.3
     }
 
     // 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())
     {
         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;
     }
 
 
         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())
     {
         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;
 
 
         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<bool leftFirst>
+    ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
     {
         if (v1.isInt32() && v2.isInt32())
             return v1.asInt32() < v2.asInt32();
 
     {
         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;
         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;
 
         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<bool leftFirst>
+    ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
     {
         if (v1.isInt32() && v2.isInt32())
             return v1.asInt32() <= v2.asInt32();
 
     {
         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;
         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;
 
         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:
     }
 
     // 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)
     {
 
     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<UString::Rep> value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep());
-            if (!value)
-                return throwOutOfMemoryError(callFrame);
-            return jsString(callFrame, value.release());
-        }
-
-        if (rightIsNumber & leftIsString) {
-            RefPtr<UString::Rep> 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);
 
         // 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)
     {
 
     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) {
         size_t count = 0;
 
         while (slotBase != cell) {
@@ -238,14 +311,14 @@ namespace JSC {
             if (v.isNull())
                 return 0;
 
             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()) {
 
             // 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)
                 if (slotBase == cell)
-                    slotOffset = cell->structure()->get(propertyName); 
+                    slotOffset = cell->structure()->get(callFrame->globalData(), propertyName); 
             }
 
             ++count;
             }
 
             ++count;
@@ -255,7 +328,26 @@ namespace JSC {
         return count;
     }
 
         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;
     {
         ScopeChainIterator iter = scopeChain->begin();
         ScopeChainIterator next = iter;
@@ -266,8 +358,13 @@ namespace JSC {
         PropertySlot slot;
         JSObject* base;
         while (true) {
         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;
                 return base;
 
             iter = next;
@@ -277,59 +374,6 @@ namespace JSC {
         ASSERT_NOT_REACHED();
         return JSValue();
     }
         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<UString::Rep> 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
 } // namespace JSC
 
 #endif // Operations_h