]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - runtime/Operations.h
JavaScriptCore-554.1.tar.gz
[apple/javascriptcore.git] / runtime / Operations.h
index c6a7e7a303f6801f69ce9fa3590e5c12fcebf74c..f29154da830a27fa3c6b7425f87bcc8a065902f2 100644 (file)
 #ifndef Operations_h
 #define Operations_h
 
+#include "Interpreter.h"
 #include "JSImmediate.h"
 #include "JSNumberCell.h"
 #include "JSString.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);
+
     // ECMA 11.9.3
-    inline bool JSValuePtr::equal(ExecState* exec, JSValuePtr v1, JSValuePtr v2)
+    inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
     {
-        if (JSImmediate::areBothImmediateIntegerNumbers(v1, v2))
+        if (v1.isInt32() && v2.isInt32())
             return v1 == v2;
 
         return equalSlowCase(exec, v1, v2);
     }
 
-    ALWAYS_INLINE bool JSValuePtr::equalSlowCaseInline(ExecState* exec, JSValuePtr v1, JSValuePtr v2)
+    ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
     {
-        ASSERT(!JSImmediate::areBothImmediateIntegerNumbers(v1, v2));
-
         do {
             if (v1.isNumber() && v2.isNumber())
                 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
@@ -53,13 +58,13 @@ namespace JSC {
             if (v1.isUndefinedOrNull()) {
                 if (v2.isUndefinedOrNull())
                     return true;
-                if (JSImmediate::isImmediate(v2))
+                if (!v2.isCell())
                     return false;
                 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
             }
 
             if (v2.isUndefinedOrNull()) {
-                if (JSImmediate::isImmediate(v1))
+                if (!v1.isCell())
                     return false;
                 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
             }
@@ -67,21 +72,21 @@ namespace JSC {
             if (v1.isObject()) {
                 if (v2.isObject())
                     return v1 == v2;
-                JSValuePtr p1 = v1.toPrimitive(exec);
+                JSValue p1 = v1.toPrimitive(exec);
                 if (exec->hadException())
                     return false;
                 v1 = p1;
-                if (JSImmediate::areBothImmediateIntegerNumbers(v1, v2))
+                if (v1.isInt32() && v2.isInt32())
                     return v1 == v2;
                 continue;
             }
 
             if (v2.isObject()) {
-                JSValuePtr p2 = v2.toPrimitive(exec);
+                JSValue p2 = v2.toPrimitive(exec);
                 if (exec->hadException())
                     return false;
                 v2 = p2;
-                if (JSImmediate::areBothImmediateIntegerNumbers(v1, v2))
+                if (v1.isInt32() && v2.isInt32())
                     return v1 == v2;
                 continue;
             }
@@ -105,31 +110,226 @@ namespace JSC {
     }
 
     // ECMA 11.9.3
-    inline bool JSValuePtr::strictEqual(JSValuePtr v1, JSValuePtr v2)
+    ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(JSValue v1, JSValue v2)
+    {
+        ASSERT(v1.isCell() && v2.isCell());
+
+        if (v1.asCell()->isString() && v2.asCell()->isString())
+            return asString(v1)->value() == asString(v2)->value();
+
+        return v1 == v2;
+    }
+
+    inline bool JSValue::strictEqual(JSValue v1, JSValue v2)
     {
-        if (JSImmediate::areBothImmediateIntegerNumbers(v1, v2))
+        if (v1.isInt32() && v2.isInt32())
             return v1 == v2;
 
         if (v1.isNumber() && v2.isNumber())
             return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
 
-        if (JSImmediate::isEitherImmediate(v1, v2))
+        if (!v1.isCell() || !v2.isCell())
             return v1 == v2;
 
-        return strictEqualSlowCase(v1, v2);
+        return strictEqualSlowCaseInline(v1, v2);
     }
 
-    ALWAYS_INLINE bool JSValuePtr::strictEqualSlowCaseInline(JSValuePtr v1, JSValuePtr v2)
+    inline bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
     {
-        ASSERT(!JSImmediate::isEitherImmediate(v1, v2));
+        if (v1.isInt32() && v2.isInt32())
+            return v1.asInt32() < v2.asInt32();
 
-        if (v1.asCell()->isString() && v2.asCell()->isString())
-            return asString(v1)->value() == asString(v2)->value();
+        double n1;
+        double n2;
+        if (v1.getNumber(n1) && v2.getNumber(n2))
+            return n1 < n2;
 
-        return v1 == v2;
+        JSGlobalData* globalData = &callFrame->globalData();
+        if (isJSString(globalData, v1) && isJSString(globalData, v2))
+            return asString(v1)->value() < asString(v2)->value();
+
+        JSValue p1;
+        JSValue p2;
+        bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
+        bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
+
+        if (wasNotString1 | wasNotString2)
+            return n1 < n2;
+
+        return asString(p1)->value() < asString(p2)->value();
+    }
+
+    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;
+
+        JSGlobalData* globalData = &callFrame->globalData();
+        if (isJSString(globalData, v1) && isJSString(globalData, v2))
+            return !(asString(v2)->value() < asString(v1)->value());
+
+        JSValue p1;
+        JSValue p2;
+        bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
+        bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
+
+        if (wasNotString1 | wasNotString2)
+            return n1 <= n2;
+
+        return !(asString(p2)->value() < asString(p1)->value());
+    }
+
+    // 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)
+    {
+        double left;
+        double right = 0.0;
+
+        bool rightIsNumber = v2.getNumber(right);
+        if (rightIsNumber && v1.getNumber(left))
+            return jsNumber(callFrame, left + right);
+        
+        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());
+        }
+
+        // All other cases are pretty uncommon
+        return jsAddSlowCase(callFrame, v1, v2);
+    }
+
+    inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
+    {
+        JSCell* cell = asCell(base);
+        size_t count = 0;
+
+        while (slotBase != cell) {
+            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 (v.isNull())
+                return 0;
+
+            cell = asCell(v);
+
+            // 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();
+                if (slotBase == cell)
+                    slotOffset = cell->structure()->get(propertyName); 
+            }
+
+            ++count;
+        }
+        
+        ASSERT(count);
+        return count;
+    }
+
+    ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
+    {
+        ScopeChainIterator iter = scopeChain->begin();
+        ScopeChainIterator next = iter;
+        ++next;
+        ScopeChainIterator end = scopeChain->end();
+        ASSERT(iter != end);
+
+        PropertySlot slot;
+        JSObject* base;
+        while (true) {
+            base = *iter;
+            if (next == end || base->getPropertySlot(callFrame, property, slot))
+                return base;
+
+            iter = next;
+            ++next;
+        }
+
+        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);
     }
 
-    JSValuePtr throwOutOfMemoryError(ExecState*);
+} // namespace JSC
 
-}
-#endif
+#endif // Operations_h