]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Operations.h
c3aa0fa4f65a07302c7577dde378fce739699d94
[apple/javascriptcore.git] / runtime / Operations.h
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.
4 *
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.
9 *
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.
14 *
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.
19 *
20 */
21
22 #ifndef Operations_h
23 #define Operations_h
24
25 #include "Interpreter.h"
26 #include "JSImmediate.h"
27 #include "JSNumberCell.h"
28 #include "JSString.h"
29
30 namespace JSC {
31
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);
37
38 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
39 {
40 if (!s1->length())
41 return s2;
42 if (!s2->length())
43 return s1;
44
45 unsigned ropeLength = s1->ropeLength() + s2->ropeLength();
46 JSGlobalData* globalData = &exec->globalData();
47
48 if (ropeLength <= JSString::s_maxInternalRopeLength)
49 return new (globalData) JSString(globalData, ropeLength, s1, s2);
50
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());
59 }
60
61 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2)
62 {
63 unsigned ropeLength = 1 + s2->ropeLength();
64 JSGlobalData* globalData = &exec->globalData();
65
66 if (ropeLength <= JSString::s_maxInternalRopeLength)
67 return new (globalData) JSString(globalData, ropeLength, u1, s2);
68
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());
77 }
78
79 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2)
80 {
81 unsigned ropeLength = s1->ropeLength() + 1;
82 JSGlobalData* globalData = &exec->globalData();
83
84 if (ropeLength <= JSString::s_maxInternalRopeLength)
85 return new (globalData) JSString(globalData, ropeLength, s1, u2);
86
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());
95 }
96
97 ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
98 {
99 ASSERT(count >= 3);
100
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;
108 }
109
110 JSGlobalData* globalData = &exec->globalData();
111 if (ropeLength == 3)
112 return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue());
113
114 RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength);
115 if (UNLIKELY(!rope))
116 return throwOutOfMemoryError(exec);
117
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));
125 }
126
127 ASSERT(index == ropeLength);
128 return new (globalData) JSString(globalData, rope.release());
129 }
130
131 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args)
132 {
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;
144 }
145
146 RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength);
147 if (UNLIKELY(!rope))
148 return throwOutOfMemoryError(exec);
149
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));
161 }
162 ASSERT(index == ropeLength);
163
164 JSGlobalData* globalData = &exec->globalData();
165 return new (globalData) JSString(globalData, rope.release());
166 }
167
168 // ECMA 11.9.3
169 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
170 {
171 if (v1.isInt32() && v2.isInt32())
172 return v1 == v2;
173
174 return equalSlowCase(exec, v1, v2);
175 }
176
177 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
178 {
179 do {
180 if (v1.isNumber() && v2.isNumber())
181 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
182
183 bool s1 = v1.isString();
184 bool s2 = v2.isString();
185 if (s1 && s2)
186 return asString(v1)->value(exec) == asString(v2)->value(exec);
187
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();
194 }
195
196 if (v2.isUndefinedOrNull()) {
197 if (!v1.isCell())
198 return false;
199 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
200 }
201
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;
212 }
213
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;
222 }
223
224 if (s1 || s2) {
225 double d1 = v1.toNumber(exec);
226 double d2 = v2.toNumber(exec);
227 return d1 == d2;
228 }
229
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());
236 }
237
238 return v1 == v2;
239 } while (true);
240 }
241
242 // ECMA 11.9.3
243 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
244 {
245 ASSERT(v1.isCell() && v2.isCell());
246
247 if (v1.asCell()->isString() && v2.asCell()->isString())
248 return asString(v1)->value(exec) == asString(v2)->value(exec);
249
250 return v1 == v2;
251 }
252
253 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
254 {
255 if (v1.isInt32() && v2.isInt32())
256 return v1 == v2;
257
258 if (v1.isNumber() && v2.isNumber())
259 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
260
261 if (!v1.isCell() || !v2.isCell())
262 return v1 == v2;
263
264 return strictEqualSlowCaseInline(exec, v1, v2);
265 }
266
267 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
268 {
269 if (v1.isInt32() && v2.isInt32())
270 return v1.asInt32() < v2.asInt32();
271
272 double n1;
273 double n2;
274 if (v1.getNumber(n1) && v2.getNumber(n2))
275 return n1 < n2;
276
277 JSGlobalData* globalData = &callFrame->globalData();
278 if (isJSString(globalData, v1) && isJSString(globalData, v2))
279 return asString(v1)->value(callFrame) < asString(v2)->value(callFrame);
280
281 JSValue p1;
282 JSValue p2;
283 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
284 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
285
286 if (wasNotString1 | wasNotString2)
287 return n1 < n2;
288
289 return asString(p1)->value(callFrame) < asString(p2)->value(callFrame);
290 }
291
292 inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
293 {
294 if (v1.isInt32() && v2.isInt32())
295 return v1.asInt32() <= v2.asInt32();
296
297 double n1;
298 double n2;
299 if (v1.getNumber(n1) && v2.getNumber(n2))
300 return n1 <= n2;
301
302 JSGlobalData* globalData = &callFrame->globalData();
303 if (isJSString(globalData, v1) && isJSString(globalData, v2))
304 return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame));
305
306 JSValue p1;
307 JSValue p2;
308 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
309 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
310
311 if (wasNotString1 | wasNotString2)
312 return n1 <= n2;
313
314 return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame));
315 }
316
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
325
326 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
327 {
328 double left = 0.0, right;
329 if (v1.getNumber(left) && v2.getNumber(right))
330 return jsNumber(callFrame, left + right);
331
332 if (v1.isString()) {
333 return v2.isString()
334 ? jsString(callFrame, asString(v1), asString(v2))
335 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame));
336 }
337
338 // All other cases are pretty uncommon
339 return jsAddSlowCase(callFrame, v1, v2);
340 }
341
342 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
343 {
344 JSCell* cell = asCell(base);
345 size_t count = 0;
346
347 while (slotBase != cell) {
348 JSValue v = cell->structure()->prototypeForLookup(callFrame);
349
350 // If we didn't find slotBase in base's prototype chain, then base
351 // must be a proxy for another object.
352
353 if (v.isNull())
354 return 0;
355
356 cell = asCell(v);
357
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();
362 if (slotBase == cell)
363 slotOffset = cell->structure()->get(propertyName);
364 }
365
366 ++count;
367 }
368
369 ASSERT(count);
370 return count;
371 }
372
373 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
374 {
375 size_t count = 0;
376 while (1) {
377 JSValue v = base->structure()->prototypeForLookup(callFrame);
378 if (v.isNull())
379 return count;
380
381 base = asCell(v);
382
383 // Since we're accessing a prototype in a loop, it's a good bet that it
384 // should not be treated as a dictionary.
385 if (base->structure()->isDictionary())
386 asObject(base)->flattenDictionaryObject();
387
388 ++count;
389 }
390 }
391
392 ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
393 {
394 ScopeChainIterator iter = scopeChain->begin();
395 ScopeChainIterator next = iter;
396 ++next;
397 ScopeChainIterator end = scopeChain->end();
398 ASSERT(iter != end);
399
400 PropertySlot slot;
401 JSObject* base;
402 while (true) {
403 base = *iter;
404 if (next == end || base->getPropertySlot(callFrame, property, slot))
405 return base;
406
407 iter = next;
408 ++next;
409 }
410
411 ASSERT_NOT_REACHED();
412 return JSValue();
413 }
414 } // namespace JSC
415
416 #endif // Operations_h