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