#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();
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();
}
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;
}
}
// 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