Rubber-stamped by Brady Eidson.
[webbrowser.git] / JavaScriptCore / runtime / Operations.h
bloba56893c891d04ba2995e298d858b933b58ceee48
1 /*
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.
22 #ifndef Operations_h
23 #define Operations_h
25 #include "Interpreter.h"
26 #include "JSImmediate.h"
27 #include "JSNumberCell.h"
28 #include "JSString.h"
30 namespace JSC {
32 NEVER_INLINE JSValue throwOutOfMemoryError(ExecState*);
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 if (!s1->length())
41 return s2;
42 if (!s2->length())
43 return s1;
45 unsigned ropeLength = s1->ropeLength() + s2->ropeLength();
46 JSGlobalData* globalData = &exec->globalData();
48 if (ropeLength <= JSString::s_maxInternalRopeLength)
49 return new (globalData) JSString(globalData, ropeLength, s1, s2);
51 unsigned index = 0;
52 RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength);
53 if (UNLIKELY(!rope))
54 return throwOutOfMemoryError(exec);
55 rope->append(index, s1);
56 rope->append(index, s2);
57 ASSERT(index == ropeLength);
58 return new (globalData) JSString(globalData, rope.release());
61 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2)
63 unsigned ropeLength = 1 + s2->ropeLength();
64 JSGlobalData* globalData = &exec->globalData();
66 if (ropeLength <= JSString::s_maxInternalRopeLength)
67 return new (globalData) JSString(globalData, ropeLength, u1, s2);
69 unsigned index = 0;
70 RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength);
71 if (UNLIKELY(!rope))
72 return throwOutOfMemoryError(exec);
73 rope->append(index, u1);
74 rope->append(index, s2);
75 ASSERT(index == ropeLength);
76 return new (globalData) JSString(globalData, rope.release());
79 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2)
81 unsigned ropeLength = s1->ropeLength() + 1;
82 JSGlobalData* globalData = &exec->globalData();
84 if (ropeLength <= JSString::s_maxInternalRopeLength)
85 return new (globalData) JSString(globalData, ropeLength, s1, u2);
87 unsigned index = 0;
88 RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength);
89 if (UNLIKELY(!rope))
90 return throwOutOfMemoryError(exec);
91 rope->append(index, s1);
92 rope->append(index, u2);
93 ASSERT(index == ropeLength);
94 return new (globalData) JSString(globalData, rope.release());
97 ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
99 ASSERT(count >= 3);
101 unsigned ropeLength = 0;
102 for (unsigned i = 0; i < count; ++i) {
103 JSValue v = strings[i].jsValue();
104 if (LIKELY(v.isString()))
105 ropeLength += asString(v)->ropeLength();
106 else
107 ++ropeLength;
110 JSGlobalData* globalData = &exec->globalData();
111 if (ropeLength == 3)
112 return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue());
114 RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength);
115 if (UNLIKELY(!rope))
116 return throwOutOfMemoryError(exec);
118 unsigned index = 0;
119 for (unsigned i = 0; i < count; ++i) {
120 JSValue v = strings[i].jsValue();
121 if (LIKELY(v.isString()))
122 rope->append(index, asString(v));
123 else
124 rope->append(index, v.toString(exec));
127 ASSERT(index == ropeLength);
128 return new (globalData) JSString(globalData, rope.release());
131 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args)
133 unsigned ropeLength = 0;
134 if (LIKELY(thisValue.isString()))
135 ropeLength += asString(thisValue)->ropeLength();
136 else
137 ++ropeLength;
138 for (unsigned i = 0; i < args.size(); ++i) {
139 JSValue v = args.at(i);
140 if (LIKELY(v.isString()))
141 ropeLength += asString(v)->ropeLength();
142 else
143 ++ropeLength;
146 RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength);
147 if (UNLIKELY(!rope))
148 return throwOutOfMemoryError(exec);
150 unsigned index = 0;
151 if (LIKELY(thisValue.isString()))
152 rope->append(index, asString(thisValue));
153 else
154 rope->append(index, thisValue.toString(exec));
155 for (unsigned i = 0; i < args.size(); ++i) {
156 JSValue v = args.at(i);
157 if (LIKELY(v.isString()))
158 rope->append(index, asString(v));
159 else
160 rope->append(index, v.toString(exec));
162 ASSERT(index == ropeLength);
164 JSGlobalData* globalData = &exec->globalData();
165 return new (globalData) JSString(globalData, rope.release());
168 // ECMA 11.9.3
169 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
171 if (v1.isInt32() && v2.isInt32())
172 return v1 == v2;
174 return equalSlowCase(exec, v1, v2);
177 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
179 do {
180 if (v1.isNumber() && v2.isNumber())
181 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
183 bool s1 = v1.isString();
184 bool s2 = v2.isString();
185 if (s1 && s2)
186 return asString(v1)->value(exec) == asString(v2)->value(exec);
188 if (v1.isUndefinedOrNull()) {
189 if (v2.isUndefinedOrNull())
190 return true;
191 if (!v2.isCell())
192 return false;
193 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
196 if (v2.isUndefinedOrNull()) {
197 if (!v1.isCell())
198 return false;
199 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
202 if (v1.isObject()) {
203 if (v2.isObject())
204 return v1 == v2;
205 JSValue p1 = v1.toPrimitive(exec);
206 if (exec->hadException())
207 return false;
208 v1 = p1;
209 if (v1.isInt32() && v2.isInt32())
210 return v1 == v2;
211 continue;
214 if (v2.isObject()) {
215 JSValue p2 = v2.toPrimitive(exec);
216 if (exec->hadException())
217 return false;
218 v2 = p2;
219 if (v1.isInt32() && v2.isInt32())
220 return v1 == v2;
221 continue;
224 if (s1 || s2) {
225 double d1 = v1.toNumber(exec);
226 double d2 = v2.toNumber(exec);
227 return d1 == d2;
230 if (v1.isBoolean()) {
231 if (v2.isNumber())
232 return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber();
233 } else if (v2.isBoolean()) {
234 if (v1.isNumber())
235 return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean());
238 return v1 == v2;
239 } while (true);
242 // ECMA 11.9.3
243 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
245 ASSERT(v1.isCell() && v2.isCell());
247 if (v1.asCell()->isString() && v2.asCell()->isString())
248 return asString(v1)->value(exec) == asString(v2)->value(exec);
250 return v1 == v2;
253 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
255 if (v1.isInt32() && v2.isInt32())
256 return v1 == v2;
258 if (v1.isNumber() && v2.isNumber())
259 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
261 if (!v1.isCell() || !v2.isCell())
262 return v1 == v2;
264 return strictEqualSlowCaseInline(exec, v1, v2);
267 inline bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
269 if (v1.isInt32() && v2.isInt32())
270 return v1.asInt32() < v2.asInt32();
272 double n1;
273 double n2;
274 if (v1.getNumber(n1) && v2.getNumber(n2))
275 return n1 < n2;
277 JSGlobalData* globalData = &callFrame->globalData();
278 if (isJSString(globalData, v1) && isJSString(globalData, v2))
279 return asString(v1)->value(callFrame) < asString(v2)->value(callFrame);
281 JSValue p1;
282 JSValue p2;
283 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
284 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
286 if (wasNotString1 | wasNotString2)
287 return n1 < n2;
289 return asString(p1)->value(callFrame) < asString(p2)->value(callFrame);
292 inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
294 if (v1.isInt32() && v2.isInt32())
295 return v1.asInt32() <= v2.asInt32();
297 double n1;
298 double n2;
299 if (v1.getNumber(n1) && v2.getNumber(n2))
300 return n1 <= n2;
302 JSGlobalData* globalData = &callFrame->globalData();
303 if (isJSString(globalData, v1) && isJSString(globalData, v2))
304 return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame));
306 JSValue p1;
307 JSValue p2;
308 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
309 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
311 if (wasNotString1 | wasNotString2)
312 return n1 <= n2;
314 return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame));
317 // Fast-path choices here are based on frequency data from SunSpider:
318 // <times> Add case: <t1> <t2>
319 // ---------------------------
320 // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
321 // 247412 Add case: 5 5
322 // 20900 Add case: 5 6
323 // 13962 Add case: 5 3
324 // 4000 Add case: 3 5
326 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
328 double left = 0.0, right;
329 if (v1.getNumber(left), v2.getNumber(right))
330 return jsNumber(callFrame, left + right);
332 if (v1.isString()) {
333 return v2.isString()
334 ? jsString(callFrame, asString(v1), asString(v2))
335 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame));
338 // All other cases are pretty uncommon
339 return jsAddSlowCase(callFrame, v1, v2);
342 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase)
344 JSCell* cell = asCell(base);
345 size_t count = 0;
347 while (slotBase != cell) {
348 JSValue v = cell->structure()->prototypeForLookup(callFrame);
350 // If we didn't find slotBase in base's prototype chain, then base
351 // must be a proxy for another object.
353 if (v.isNull())
354 return 0;
356 cell = asCell(v);
358 // Since we're accessing a prototype in a loop, it's a good bet that it
359 // should not be treated as a dictionary.
360 if (cell->structure()->isDictionary())
361 asObject(cell)->flattenDictionaryObject();
363 ++count;
366 ASSERT(count);
367 return count;
370 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
372 size_t count = 0;
373 while (1) {
374 JSValue v = base->structure()->prototypeForLookup(callFrame);
375 if (v.isNull())
376 return count;
378 base = asCell(v);
380 // Since we're accessing a prototype in a loop, it's a good bet that it
381 // should not be treated as a dictionary.
382 if (base->structure()->isDictionary())
383 asObject(base)->flattenDictionaryObject();
385 ++count;
389 ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
391 ScopeChainIterator iter = scopeChain->begin();
392 ScopeChainIterator next = iter;
393 ++next;
394 ScopeChainIterator end = scopeChain->end();
395 ASSERT(iter != end);
397 PropertySlot slot;
398 JSObject* base;
399 while (true) {
400 base = *iter;
401 if (next == end || base->getPropertySlot(callFrame, property, slot))
402 return base;
404 iter = next;
405 ++next;
408 ASSERT_NOT_REACHED();
409 return JSValue();
411 } // namespace JSC
413 #endif // Operations_h