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