]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Operations.h
JavaScriptCore-903.tar.gz
[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 "ExceptionHelpers.h"
26 #include "Interpreter.h"
27 #include "JSString.h"
28 #include "JSValueInlineMethods.h"
29
30 namespace JSC {
31
32 NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
33 JSValue jsTypeStringForValue(CallFrame*, JSValue);
34 bool jsIsObjectType(JSValue);
35 bool jsIsFunctionType(JSValue);
36
37 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
38 {
39 unsigned length1 = s1->length();
40 if (!length1)
41 return s2;
42 unsigned length2 = s2->length();
43 if (!length2)
44 return s1;
45 if ((length1 + length2) < length1)
46 return throwOutOfMemoryError(exec);
47
48 unsigned fiberCount = s1->fiberCount() + s2->fiberCount();
49 JSGlobalData* globalData = &exec->globalData();
50
51 if (fiberCount <= JSString::s_maxInternalRopeLength)
52 return new (globalData) JSString(globalData, fiberCount, s1, s2);
53
54 JSString::RopeBuilder ropeBuilder(fiberCount);
55 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
56 return throwOutOfMemoryError(exec);
57 ropeBuilder.append(s1);
58 ropeBuilder.append(s2);
59 return new (globalData) JSString(globalData, ropeBuilder.release());
60 }
61
62 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2)
63 {
64 unsigned length1 = u1.length();
65 if (!length1)
66 return s2;
67 unsigned length2 = s2->length();
68 if (!length2)
69 return jsString(exec, u1);
70 if ((length1 + length2) < length1)
71 return throwOutOfMemoryError(exec);
72
73 unsigned fiberCount = 1 + s2->fiberCount();
74 JSGlobalData* globalData = &exec->globalData();
75
76 if (fiberCount <= JSString::s_maxInternalRopeLength)
77 return new (globalData) JSString(globalData, fiberCount, u1, s2);
78
79 JSString::RopeBuilder ropeBuilder(fiberCount);
80 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
81 return throwOutOfMemoryError(exec);
82 ropeBuilder.append(u1);
83 ropeBuilder.append(s2);
84 return new (globalData) JSString(globalData, ropeBuilder.release());
85 }
86
87 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2)
88 {
89 unsigned length1 = s1->length();
90 if (!length1)
91 return jsString(exec, u2);
92 unsigned length2 = u2.length();
93 if (!length2)
94 return s1;
95 if ((length1 + length2) < length1)
96 return throwOutOfMemoryError(exec);
97
98 unsigned fiberCount = s1->fiberCount() + 1;
99 JSGlobalData* globalData = &exec->globalData();
100
101 if (fiberCount <= JSString::s_maxInternalRopeLength)
102 return new (globalData) JSString(globalData, fiberCount, s1, u2);
103
104 JSString::RopeBuilder ropeBuilder(fiberCount);
105 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
106 return throwOutOfMemoryError(exec);
107 ropeBuilder.append(s1);
108 ropeBuilder.append(u2);
109 return new (globalData) JSString(globalData, ropeBuilder.release());
110 }
111
112 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2)
113 {
114 unsigned length1 = u1.length();
115 if (!length1)
116 return jsString(exec, u2);
117 unsigned length2 = u2.length();
118 if (!length2)
119 return jsString(exec, u1);
120 if ((length1 + length2) < length1)
121 return throwOutOfMemoryError(exec);
122
123 JSGlobalData* globalData = &exec->globalData();
124 return new (globalData) JSString(globalData, u1, u2);
125 }
126
127 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3)
128 {
129 unsigned length1 = u1.length();
130 unsigned length2 = u2.length();
131 unsigned length3 = u3.length();
132 if (!length1)
133 return jsString(exec, u2, u3);
134 if (!length2)
135 return jsString(exec, u1, u3);
136 if (!length3)
137 return jsString(exec, u1, u2);
138
139 if ((length1 + length2) < length1)
140 return throwOutOfMemoryError(exec);
141 if ((length1 + length2 + length3) < length3)
142 return throwOutOfMemoryError(exec);
143
144 JSGlobalData* globalData = &exec->globalData();
145 return new (globalData) JSString(globalData, u1, u2, u3);
146 }
147
148 ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
149 {
150 ASSERT(count >= 3);
151
152 unsigned fiberCount = 0;
153 for (unsigned i = 0; i < count; ++i) {
154 JSValue v = strings[i].jsValue();
155 if (LIKELY(v.isString()))
156 fiberCount += asString(v)->fiberCount();
157 else
158 ++fiberCount;
159 }
160
161 JSGlobalData* globalData = &exec->globalData();
162 if (fiberCount == 3)
163 return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue());
164
165 JSString::RopeBuilder ropeBuilder(fiberCount);
166 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
167 return throwOutOfMemoryError(exec);
168
169 unsigned length = 0;
170 bool overflow = false;
171
172 for (unsigned i = 0; i < count; ++i) {
173 JSValue v = strings[i].jsValue();
174 if (LIKELY(v.isString()))
175 ropeBuilder.append(asString(v));
176 else
177 ropeBuilder.append(v.toString(exec));
178
179 unsigned newLength = ropeBuilder.length();
180 if (newLength < length)
181 overflow = true;
182 length = newLength;
183 }
184
185 if (overflow)
186 return throwOutOfMemoryError(exec);
187
188 return new (globalData) JSString(globalData, ropeBuilder.release());
189 }
190
191 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue)
192 {
193 unsigned fiberCount = 0;
194 if (LIKELY(thisValue.isString()))
195 fiberCount += asString(thisValue)->fiberCount();
196 else
197 ++fiberCount;
198 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
199 JSValue v = exec->argument(i);
200 if (LIKELY(v.isString()))
201 fiberCount += asString(v)->fiberCount();
202 else
203 ++fiberCount;
204 }
205
206 JSString::RopeBuilder ropeBuilder(fiberCount);
207 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
208 return throwOutOfMemoryError(exec);
209
210 if (LIKELY(thisValue.isString()))
211 ropeBuilder.append(asString(thisValue));
212 else
213 ropeBuilder.append(thisValue.toString(exec));
214
215 unsigned length = 0;
216 bool overflow = false;
217
218 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
219 JSValue v = exec->argument(i);
220 if (LIKELY(v.isString()))
221 ropeBuilder.append(asString(v));
222 else
223 ropeBuilder.append(v.toString(exec));
224
225 unsigned newLength = ropeBuilder.length();
226 if (newLength < length)
227 overflow = true;
228 length = newLength;
229 }
230
231 if (overflow)
232 return throwOutOfMemoryError(exec);
233
234 JSGlobalData* globalData = &exec->globalData();
235 return new (globalData) JSString(globalData, ropeBuilder.release());
236 }
237
238 // ECMA 11.9.3
239 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
240 {
241 if (v1.isInt32() && v2.isInt32())
242 return v1 == v2;
243
244 return equalSlowCase(exec, v1, v2);
245 }
246
247 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
248 {
249 do {
250 if (v1.isNumber() && v2.isNumber())
251 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
252
253 bool s1 = v1.isString();
254 bool s2 = v2.isString();
255 if (s1 && s2)
256 return asString(v1)->value(exec) == asString(v2)->value(exec);
257
258 if (v1.isUndefinedOrNull()) {
259 if (v2.isUndefinedOrNull())
260 return true;
261 if (!v2.isCell())
262 return false;
263 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
264 }
265
266 if (v2.isUndefinedOrNull()) {
267 if (!v1.isCell())
268 return false;
269 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
270 }
271
272 if (v1.isObject()) {
273 if (v2.isObject())
274 return v1 == v2;
275 JSValue p1 = v1.toPrimitive(exec);
276 if (exec->hadException())
277 return false;
278 v1 = p1;
279 if (v1.isInt32() && v2.isInt32())
280 return v1 == v2;
281 continue;
282 }
283
284 if (v2.isObject()) {
285 JSValue p2 = v2.toPrimitive(exec);
286 if (exec->hadException())
287 return false;
288 v2 = p2;
289 if (v1.isInt32() && v2.isInt32())
290 return v1 == v2;
291 continue;
292 }
293
294 if (s1 || s2) {
295 double d1 = v1.toNumber(exec);
296 double d2 = v2.toNumber(exec);
297 return d1 == d2;
298 }
299
300 if (v1.isBoolean()) {
301 if (v2.isNumber())
302 return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber();
303 } else if (v2.isBoolean()) {
304 if (v1.isNumber())
305 return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean());
306 }
307
308 return v1 == v2;
309 } while (true);
310 }
311
312 // ECMA 11.9.3
313 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
314 {
315 ASSERT(v1.isCell() && v2.isCell());
316
317 if (v1.asCell()->isString() && v2.asCell()->isString())
318 return asString(v1)->value(exec) == asString(v2)->value(exec);
319
320 return v1 == v2;
321 }
322
323 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
324 {
325 if (v1.isInt32() && v2.isInt32())
326 return v1 == v2;
327
328 if (v1.isNumber() && v2.isNumber())
329 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
330
331 if (!v1.isCell() || !v2.isCell())
332 return v1 == v2;
333
334 return strictEqualSlowCaseInline(exec, v1, v2);
335 }
336
337 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
338 {
339 if (v1.isInt32() && v2.isInt32())
340 return v1.asInt32() < v2.asInt32();
341
342 double n1;
343 double n2;
344 if (v1.getNumber(n1) && v2.getNumber(n2))
345 return n1 < n2;
346
347 JSGlobalData* globalData = &callFrame->globalData();
348 if (isJSString(globalData, v1) && isJSString(globalData, v2))
349 return asString(v1)->value(callFrame) < asString(v2)->value(callFrame);
350
351 JSValue p1;
352 JSValue p2;
353 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
354 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
355
356 if (wasNotString1 | wasNotString2)
357 return n1 < n2;
358
359 return asString(p1)->value(callFrame) < asString(p2)->value(callFrame);
360 }
361
362 inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
363 {
364 if (v1.isInt32() && v2.isInt32())
365 return v1.asInt32() <= v2.asInt32();
366
367 double n1;
368 double n2;
369 if (v1.getNumber(n1) && v2.getNumber(n2))
370 return n1 <= n2;
371
372 JSGlobalData* globalData = &callFrame->globalData();
373 if (isJSString(globalData, v1) && isJSString(globalData, v2))
374 return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame));
375
376 JSValue p1;
377 JSValue p2;
378 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
379 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
380
381 if (wasNotString1 | wasNotString2)
382 return n1 <= n2;
383
384 return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame));
385 }
386
387 // Fast-path choices here are based on frequency data from SunSpider:
388 // <times> Add case: <t1> <t2>
389 // ---------------------------
390 // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
391 // 247412 Add case: 5 5
392 // 20900 Add case: 5 6
393 // 13962 Add case: 5 3
394 // 4000 Add case: 3 5
395
396 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
397 {
398 double left = 0.0, right;
399 if (v1.getNumber(left) && v2.getNumber(right))
400 return jsNumber(left + right);
401
402 if (v1.isString()) {
403 return v2.isString()
404 ? jsString(callFrame, asString(v1), asString(v2))
405 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame));
406 }
407
408 // All other cases are pretty uncommon
409 return jsAddSlowCase(callFrame, v1, v2);
410 }
411
412 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
413 {
414 JSCell* cell = base.asCell();
415 size_t count = 0;
416
417 while (slotBase != cell) {
418 JSValue v = cell->structure()->prototypeForLookup(callFrame);
419
420 // If we didn't find slotBase in base's prototype chain, then base
421 // must be a proxy for another object.
422
423 if (v.isNull())
424 return 0;
425
426 cell = v.asCell();
427
428 // Since we're accessing a prototype in a loop, it's a good bet that it
429 // should not be treated as a dictionary.
430 if (cell->structure()->isDictionary()) {
431 asObject(cell)->flattenDictionaryObject(callFrame->globalData());
432 if (slotBase == cell)
433 slotOffset = cell->structure()->get(callFrame->globalData(), propertyName);
434 }
435
436 ++count;
437 }
438
439 ASSERT(count);
440 return count;
441 }
442
443 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
444 {
445 size_t count = 0;
446 while (1) {
447 JSValue v = base->structure()->prototypeForLookup(callFrame);
448 if (v.isNull())
449 return count;
450
451 base = v.asCell();
452
453 // Since we're accessing a prototype in a loop, it's a good bet that it
454 // should not be treated as a dictionary.
455 if (base->structure()->isDictionary())
456 asObject(base)->flattenDictionaryObject(callFrame->globalData());
457
458 ++count;
459 }
460 }
461
462 ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain, bool isStrictPut)
463 {
464 ScopeChainIterator iter = scopeChain->begin();
465 ScopeChainIterator next = iter;
466 ++next;
467 ScopeChainIterator end = scopeChain->end();
468 ASSERT(iter != end);
469
470 PropertySlot slot;
471 JSObject* base;
472 while (true) {
473 base = iter->get();
474 if (next == end) {
475 if (isStrictPut && !base->getPropertySlot(callFrame, property, slot))
476 return JSValue();
477 return base;
478 }
479 if (base->getPropertySlot(callFrame, property, slot))
480 return base;
481
482 iter = next;
483 ++next;
484 }
485
486 ASSERT_NOT_REACHED();
487 return JSValue();
488 }
489 } // namespace JSC
490
491 #endif // Operations_h