]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Operations.h
JavaScriptCore-721.26.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 "JSImmediate.h"
28 #include "JSNumberCell.h"
29 #include "JSString.h"
30
31 namespace JSC {
32
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 unsigned length1 = s1->length();
41 if (!length1)
42 return s2;
43 unsigned length2 = s2->length();
44 if (!length2)
45 return s1;
46 if ((length1 + length2) < length1)
47 return throwOutOfMemoryError(exec);
48
49 unsigned fiberCount = s1->size() + s2->size();
50 JSGlobalData* globalData = &exec->globalData();
51
52 if (fiberCount <= JSString::s_maxInternalRopeLength)
53 return new (globalData) JSString(globalData, fiberCount, s1, s2);
54
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());
61 }
62
63 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2)
64 {
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();
75 JSGlobalData* globalData = &exec->globalData();
76
77 if (fiberCount <= JSString::s_maxInternalRopeLength)
78 return new (globalData) JSString(globalData, fiberCount, u1, s2);
79
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());
86 }
87
88 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2)
89 {
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
124 JSGlobalData* globalData = &exec->globalData();
125 return new (globalData) JSString(globalData, u1, u2);
126 }
127
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);
139
140 if ((length1 + length2) < length1)
141 return throwOutOfMemoryError(exec);
142 if ((length1 + length2 + length3) < length3)
143 return throwOutOfMemoryError(exec);
144
145 JSGlobalData* globalData = &exec->globalData();
146 return new (globalData) JSString(globalData, u1, u2, u3);
147 }
148
149 ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
150 {
151 ASSERT(count >= 3);
152
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)->size();
158 else
159 ++fiberCount;
160 }
161
162 JSGlobalData* globalData = &exec->globalData();
163 if (fiberCount == 3)
164 return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue());
165
166 JSString::RopeBuilder ropeBuilder(fiberCount);
167 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
168 return throwOutOfMemoryError(exec);
169
170 unsigned length = 0;
171 bool overflow = false;
172
173 for (unsigned i = 0; i < count; ++i) {
174 JSValue v = strings[i].jsValue();
175 if (LIKELY(v.isString()))
176 ropeBuilder.append(asString(v));
177 else
178 ropeBuilder.append(v.toString(exec));
179
180 unsigned newLength = ropeBuilder.length();
181 if (newLength < length)
182 overflow = true;
183 length = newLength;
184 }
185
186 if (overflow)
187 return throwOutOfMemoryError(exec);
188
189 return new (globalData) JSString(globalData, ropeBuilder.release());
190 }
191
192 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args)
193 {
194 unsigned fiberCount = 0;
195 if (LIKELY(thisValue.isString()))
196 fiberCount += asString(thisValue)->size();
197 else
198 ++fiberCount;
199 for (unsigned i = 0; i < args.size(); ++i) {
200 JSValue v = args.at(i);
201 if (LIKELY(v.isString()))
202 fiberCount += asString(v)->size();
203 else
204 ++fiberCount;
205 }
206
207 JSString::RopeBuilder ropeBuilder(fiberCount);
208 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
209 return throwOutOfMemoryError(exec);
210
211 if (LIKELY(thisValue.isString()))
212 ropeBuilder.append(asString(thisValue));
213 else
214 ropeBuilder.append(thisValue.toString(exec));
215
216 unsigned length = 0;
217 bool overflow = false;
218
219 for (unsigned i = 0; i < args.size(); ++i) {
220 JSValue v = args.at(i);
221 if (LIKELY(v.isString()))
222 ropeBuilder.append(asString(v));
223 else
224 ropeBuilder.append(v.toString(exec));
225
226 unsigned newLength = ropeBuilder.length();
227 if (newLength < length)
228 overflow = true;
229 length = newLength;
230 }
231
232 if (overflow)
233 return throwOutOfMemoryError(exec);
234
235 JSGlobalData* globalData = &exec->globalData();
236 return new (globalData) JSString(globalData, ropeBuilder.release());
237 }
238
239 // ECMA 11.9.3
240 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
241 {
242 if (v1.isInt32() && v2.isInt32())
243 return v1 == v2;
244
245 return equalSlowCase(exec, v1, v2);
246 }
247
248 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
249 {
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)
257 return asString(v1)->value(exec) == asString(v2)->value(exec);
258
259 if (v1.isUndefinedOrNull()) {
260 if (v2.isUndefinedOrNull())
261 return true;
262 if (!v2.isCell())
263 return false;
264 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
265 }
266
267 if (v2.isUndefinedOrNull()) {
268 if (!v1.isCell())
269 return false;
270 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
271 }
272
273 if (v1.isObject()) {
274 if (v2.isObject())
275 return v1 == v2;
276 JSValue p1 = v1.toPrimitive(exec);
277 if (exec->hadException())
278 return false;
279 v1 = p1;
280 if (v1.isInt32() && v2.isInt32())
281 return v1 == v2;
282 continue;
283 }
284
285 if (v2.isObject()) {
286 JSValue p2 = v2.toPrimitive(exec);
287 if (exec->hadException())
288 return false;
289 v2 = p2;
290 if (v1.isInt32() && v2.isInt32())
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
314 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
315 {
316 ASSERT(v1.isCell() && v2.isCell());
317
318 if (v1.asCell()->isString() && v2.asCell()->isString())
319 return asString(v1)->value(exec) == asString(v2)->value(exec);
320
321 return v1 == v2;
322 }
323
324 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
325 {
326 if (v1.isInt32() && v2.isInt32())
327 return v1 == v2;
328
329 if (v1.isNumber() && v2.isNumber())
330 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
331
332 if (!v1.isCell() || !v2.isCell())
333 return v1 == v2;
334
335 return strictEqualSlowCaseInline(exec, v1, v2);
336 }
337
338 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
339 {
340 if (v1.isInt32() && v2.isInt32())
341 return v1.asInt32() < v2.asInt32();
342
343 double n1;
344 double n2;
345 if (v1.getNumber(n1) && v2.getNumber(n2))
346 return n1 < n2;
347
348 JSGlobalData* globalData = &callFrame->globalData();
349 if (isJSString(globalData, v1) && isJSString(globalData, v2))
350 return asString(v1)->value(callFrame) < asString(v2)->value(callFrame);
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
360 return asString(p1)->value(callFrame) < asString(p2)->value(callFrame);
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))
375 return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame));
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
385 return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame));
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 {
399 double left = 0.0, right;
400 if (v1.getNumber(left) && v2.getNumber(right))
401 return jsNumber(callFrame, left + right);
402
403 if (v1.isString()) {
404 return v2.isString()
405 ? jsString(callFrame, asString(v1), asString(v2))
406 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame));
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
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
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 }
485 } // namespace JSC
486
487 #endif // Operations_h