2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "ExceptionHelpers.h"
26 #include "Interpreter.h"
27 #include "JSImmediate.h"
28 #include "JSNumberCell.h"
33 NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
34 JSValue jsTypeStringForValue(CallFrame*, JSValue);
35 bool jsIsObjectType(JSValue);
36 bool jsIsFunctionType(JSValue);
38 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
40 unsigned length1 = s1->length();
43 unsigned length2 = s2->length();
46 if ((length1 + length2) < length1)
47 return throwOutOfMemoryError(exec);
49 unsigned fiberCount = s1->fiberCount() + s2->fiberCount();
50 JSGlobalData* globalData = &exec->globalData();
52 if (fiberCount <= JSString::s_maxInternalRopeLength)
53 return new (globalData) JSString(globalData, fiberCount, s1, s2);
55 JSString::RopeBuilder ropeBuilder(fiberCount);
56 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
57 return throwOutOfMemoryError(exec);
58 ropeBuilder.append(s1);
59 ropeBuilder.append(s2);
60 return new (globalData) JSString(globalData, ropeBuilder.release());
63 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2)
65 unsigned length1 = u1.length();
68 unsigned length2 = s2->length();
70 return jsString(exec, u1);
71 if ((length1 + length2) < length1)
72 return throwOutOfMemoryError(exec);
74 unsigned fiberCount = 1 + s2->fiberCount();
75 JSGlobalData* globalData = &exec->globalData();
77 if (fiberCount <= JSString::s_maxInternalRopeLength)
78 return new (globalData) JSString(globalData, fiberCount, u1, s2);
80 JSString::RopeBuilder ropeBuilder(fiberCount);
81 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
82 return throwOutOfMemoryError(exec);
83 ropeBuilder.append(u1);
84 ropeBuilder.append(s2);
85 return new (globalData) JSString(globalData, ropeBuilder.release());
88 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2)
90 unsigned length1 = s1->length();
92 return jsString(exec, u2);
93 unsigned length2 = u2.length();
96 if ((length1 + length2) < length1)
97 return throwOutOfMemoryError(exec);
99 unsigned fiberCount = s1->fiberCount() + 1;
100 JSGlobalData* globalData = &exec->globalData();
102 if (fiberCount <= JSString::s_maxInternalRopeLength)
103 return new (globalData) JSString(globalData, fiberCount, s1, u2);
105 JSString::RopeBuilder ropeBuilder(fiberCount);
106 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
107 return throwOutOfMemoryError(exec);
108 ropeBuilder.append(s1);
109 ropeBuilder.append(u2);
110 return new (globalData) JSString(globalData, ropeBuilder.release());
113 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2)
115 unsigned length1 = u1.length();
117 return jsString(exec, u2);
118 unsigned length2 = u2.length();
120 return jsString(exec, u1);
121 if ((length1 + length2) < length1)
122 return throwOutOfMemoryError(exec);
124 JSGlobalData* globalData = &exec->globalData();
125 return new (globalData) JSString(globalData, u1, u2);
128 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3)
130 unsigned length1 = u1.length();
131 unsigned length2 = u2.length();
132 unsigned length3 = u3.length();
134 return jsString(exec, u2, u3);
136 return jsString(exec, u1, u3);
138 return jsString(exec, u1, u2);
140 if ((length1 + length2) < length1)
141 return throwOutOfMemoryError(exec);
142 if ((length1 + length2 + length3) < length3)
143 return throwOutOfMemoryError(exec);
145 JSGlobalData* globalData = &exec->globalData();
146 return new (globalData) JSString(globalData, u1, u2, u3);
149 ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
153 unsigned fiberCount = 0;
154 for (unsigned i = 0; i < count; ++i) {
155 JSValue v = strings[i].jsValue();
156 if (LIKELY(v.isString()))
157 fiberCount += asString(v)->fiberCount();
162 JSGlobalData* globalData = &exec->globalData();
164 return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue());
166 JSString::RopeBuilder ropeBuilder(fiberCount);
167 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
168 return throwOutOfMemoryError(exec);
171 bool overflow = false;
173 for (unsigned i = 0; i < count; ++i) {
174 JSValue v = strings[i].jsValue();
175 if (LIKELY(v.isString()))
176 ropeBuilder.append(asString(v));
178 ropeBuilder.append(v.toString(exec));
180 unsigned newLength = ropeBuilder.length();
181 if (newLength < length)
187 return throwOutOfMemoryError(exec);
189 return new (globalData) JSString(globalData, ropeBuilder.release());
192 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue)
194 unsigned fiberCount = 0;
195 if (LIKELY(thisValue.isString()))
196 fiberCount += asString(thisValue)->fiberCount();
199 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
200 JSValue v = exec->argument(i);
201 if (LIKELY(v.isString()))
202 fiberCount += asString(v)->fiberCount();
207 JSString::RopeBuilder ropeBuilder(fiberCount);
208 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
209 return throwOutOfMemoryError(exec);
211 if (LIKELY(thisValue.isString()))
212 ropeBuilder.append(asString(thisValue));
214 ropeBuilder.append(thisValue.toString(exec));
217 bool overflow = false;
219 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
220 JSValue v = exec->argument(i);
221 if (LIKELY(v.isString()))
222 ropeBuilder.append(asString(v));
224 ropeBuilder.append(v.toString(exec));
226 unsigned newLength = ropeBuilder.length();
227 if (newLength < length)
233 return throwOutOfMemoryError(exec);
235 JSGlobalData* globalData = &exec->globalData();
236 return new (globalData) JSString(globalData, ropeBuilder.release());
240 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
242 if (v1.isInt32() && v2.isInt32())
245 return equalSlowCase(exec, v1, v2);
248 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
251 if (v1.isNumber() && v2.isNumber())
252 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
254 bool s1 = v1.isString();
255 bool s2 = v2.isString();
257 return asString(v1)->value(exec) == asString(v2)->value(exec);
259 if (v1.isUndefinedOrNull()) {
260 if (v2.isUndefinedOrNull())
264 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
267 if (v2.isUndefinedOrNull()) {
270 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
276 JSValue p1 = v1.toPrimitive(exec);
277 if (exec->hadException())
280 if (v1.isInt32() && v2.isInt32())
286 JSValue p2 = v2.toPrimitive(exec);
287 if (exec->hadException())
290 if (v1.isInt32() && v2.isInt32())
296 double d1 = v1.toNumber(exec);
297 double d2 = v2.toNumber(exec);
301 if (v1.isBoolean()) {
303 return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber();
304 } else if (v2.isBoolean()) {
306 return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean());
314 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
316 ASSERT(v1.isCell() && v2.isCell());
318 if (v1.asCell()->isString() && v2.asCell()->isString())
319 return asString(v1)->value(exec) == asString(v2)->value(exec);
324 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
326 if (v1.isInt32() && v2.isInt32())
329 if (v1.isNumber() && v2.isNumber())
330 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
332 if (!v1.isCell() || !v2.isCell())
335 return strictEqualSlowCaseInline(exec, v1, v2);
338 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
340 if (v1.isInt32() && v2.isInt32())
341 return v1.asInt32() < v2.asInt32();
345 if (v1.getNumber(n1) && v2.getNumber(n2))
348 JSGlobalData* globalData = &callFrame->globalData();
349 if (isJSString(globalData, v1) && isJSString(globalData, v2))
350 return asString(v1)->value(callFrame) < asString(v2)->value(callFrame);
354 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
355 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
357 if (wasNotString1 | wasNotString2)
360 return asString(p1)->value(callFrame) < asString(p2)->value(callFrame);
363 inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
365 if (v1.isInt32() && v2.isInt32())
366 return v1.asInt32() <= v2.asInt32();
370 if (v1.getNumber(n1) && v2.getNumber(n2))
373 JSGlobalData* globalData = &callFrame->globalData();
374 if (isJSString(globalData, v1) && isJSString(globalData, v2))
375 return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame));
379 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
380 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
382 if (wasNotString1 | wasNotString2)
385 return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame));
388 // Fast-path choices here are based on frequency data from SunSpider:
389 // <times> Add case: <t1> <t2>
390 // ---------------------------
391 // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
392 // 247412 Add case: 5 5
393 // 20900 Add case: 5 6
394 // 13962 Add case: 5 3
395 // 4000 Add case: 3 5
397 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
399 double left = 0.0, right;
400 if (v1.getNumber(left) && v2.getNumber(right))
401 return jsNumber(left + right);
405 ? jsString(callFrame, asString(v1), asString(v2))
406 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame));
409 // All other cases are pretty uncommon
410 return jsAddSlowCase(callFrame, v1, v2);
413 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
415 JSCell* cell = base.asCell();
418 while (slotBase != cell) {
419 JSValue v = cell->structure()->prototypeForLookup(callFrame);
421 // If we didn't find slotBase in base's prototype chain, then base
422 // must be a proxy for another object.
429 // Since we're accessing a prototype in a loop, it's a good bet that it
430 // should not be treated as a dictionary.
431 if (cell->structure()->isDictionary()) {
432 asObject(cell)->flattenDictionaryObject(callFrame->globalData());
433 if (slotBase == cell)
434 slotOffset = cell->structure()->get(propertyName);
444 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
448 JSValue v = base->structure()->prototypeForLookup(callFrame);
454 // Since we're accessing a prototype in a loop, it's a good bet that it
455 // should not be treated as a dictionary.
456 if (base->structure()->isDictionary())
457 asObject(base)->flattenDictionaryObject(callFrame->globalData());
463 ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain, bool isStrictPut)
465 ScopeChainIterator iter = scopeChain->begin();
466 ScopeChainIterator next = iter;
468 ScopeChainIterator end = scopeChain->end();
476 return isStrictPut ? JSValue() : base;
477 if (base->getPropertySlot(callFrame, property, slot))
484 ASSERT_NOT_REACHED();
489 #endif // Operations_h