]>
git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Operations.h
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.
25 #include "ExceptionHelpers.h"
26 #include "Interpreter.h"
27 #include "JSImmediate.h"
28 #include "JSNumberCell.h"
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 unsigned length1
= s1
->length();
43 unsigned length2
= s2
->length();
46 if ((length1
+ length2
) < length1
)
47 return throwOutOfMemoryError(exec
);
49 unsigned fiberCount
= s1
->size() + s2
->size();
50 JSGlobalData
* globalData
= &exec
->globalData();
52 if (fiberCount
<= JSString::s_maxInternalRopeLength
)
53 return new (globalData
) JSString(globalData
, fiberCount
, s1
, s2
);
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());
63 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, const UString
& u1
, JSString
* s2
)
65 unsigned length1
= u1
.size();
68 unsigned length2
= s2
->length();
70 return jsString(exec
, u1
);
71 if ((length1
+ length2
) < length1
)
72 return throwOutOfMemoryError(exec
);
74 unsigned fiberCount
= 1 + s2
->size();
75 JSGlobalData
* globalData
= &exec
->globalData();
77 if (fiberCount
<= JSString::s_maxInternalRopeLength
)
78 return new (globalData
) JSString(globalData
, fiberCount
, u1
, s2
);
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());
88 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, JSString
* s1
, const UString
& u2
)
90 unsigned length1
= s1
->length();
92 return jsString(exec
, u2
);
93 unsigned length2
= u2
.size();
96 if ((length1
+ length2
) < length1
)
97 return throwOutOfMemoryError(exec
);
99 unsigned fiberCount
= s1
->size() + 1;
100 JSGlobalData
* globalData
= &exec
->globalData();
102 if (fiberCount
<= JSString::s_maxInternalRopeLength
)
103 return new (globalData
) JSString(globalData
, fiberCount
, s1
, u2
);
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());
113 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, const UString
& u1
, const UString
& u2
)
115 unsigned length1
= u1
.size();
117 return jsString(exec
, u2
);
118 unsigned length2
= u2
.size();
120 return jsString(exec
, u1
);
121 if ((length1
+ length2
) < length1
)
122 return throwOutOfMemoryError(exec
);
124 JSGlobalData
* globalData
= &exec
->globalData();
125 return new (globalData
) JSString(globalData
, u1
, u2
);
128 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, const UString
& u1
, const UString
& u2
, const UString
& u3
)
130 unsigned length1
= u1
.size();
131 unsigned length2
= u2
.size();
132 unsigned length3
= u3
.size();
134 return jsString(exec
, u2
, u3
);
136 return jsString(exec
, u1
, u3
);
138 return jsString(exec
, u1
, u2
);
140 if ((length1
+ length2
) < length1
)
141 return throwOutOfMemoryError(exec
);
142 if ((length1
+ length2
+ length3
) < length3
)
143 return throwOutOfMemoryError(exec
);
145 JSGlobalData
* globalData
= &exec
->globalData();
146 return new (globalData
) JSString(globalData
, u1
, u2
, u3
);
149 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, Register
* strings
, unsigned count
)
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();
162 JSGlobalData
* globalData
= &exec
->globalData();
164 return new (globalData
) JSString(exec
, strings
[0].jsValue(), strings
[1].jsValue(), strings
[2].jsValue());
166 JSString::RopeBuilder
ropeBuilder(fiberCount
);
167 if (UNLIKELY(ropeBuilder
.isOutOfMemory()))
168 return throwOutOfMemoryError(exec
);
171 bool overflow
= false;
173 for (unsigned i
= 0; i
< count
; ++i
) {
174 JSValue v
= strings
[i
].jsValue();
175 if (LIKELY(v
.isString()))
176 ropeBuilder
.append(asString(v
));
178 ropeBuilder
.append(v
.toString(exec
));
180 unsigned newLength
= ropeBuilder
.length();
181 if (newLength
< length
)
187 return throwOutOfMemoryError(exec
);
189 return new (globalData
) JSString(globalData
, ropeBuilder
.release());
192 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, JSValue thisValue
, const ArgList
& args
)
194 unsigned fiberCount
= 0;
195 if (LIKELY(thisValue
.isString()))
196 fiberCount
+= asString(thisValue
)->size();
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();
207 JSString::RopeBuilder
ropeBuilder(fiberCount
);
208 if (UNLIKELY(ropeBuilder
.isOutOfMemory()))
209 return throwOutOfMemoryError(exec
);
211 if (LIKELY(thisValue
.isString()))
212 ropeBuilder
.append(asString(thisValue
));
214 ropeBuilder
.append(thisValue
.toString(exec
));
217 bool overflow
= false;
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
));
224 ropeBuilder
.append(v
.toString(exec
));
226 unsigned newLength
= ropeBuilder
.length();
227 if (newLength
< length
)
233 return throwOutOfMemoryError(exec
);
235 JSGlobalData
* globalData
= &exec
->globalData();
236 return new (globalData
) JSString(globalData
, ropeBuilder
.release());
240 inline bool JSValue::equal(ExecState
* exec
, JSValue v1
, JSValue v2
)
242 if (v1
.isInt32() && v2
.isInt32())
245 return equalSlowCase(exec
, v1
, v2
);
248 ALWAYS_INLINE
bool JSValue::equalSlowCaseInline(ExecState
* exec
, JSValue v1
, JSValue v2
)
251 if (v1
.isNumber() && v2
.isNumber())
252 return v1
.uncheckedGetNumber() == v2
.uncheckedGetNumber();
254 bool s1
= v1
.isString();
255 bool s2
= v2
.isString();
257 return asString(v1
)->value(exec
) == asString(v2
)->value(exec
);
259 if (v1
.isUndefinedOrNull()) {
260 if (v2
.isUndefinedOrNull())
264 return v2
.asCell()->structure()->typeInfo().masqueradesAsUndefined();
267 if (v2
.isUndefinedOrNull()) {
270 return v1
.asCell()->structure()->typeInfo().masqueradesAsUndefined();
276 JSValue p1
= v1
.toPrimitive(exec
);
277 if (exec
->hadException())
280 if (v1
.isInt32() && v2
.isInt32())
286 JSValue p2
= v2
.toPrimitive(exec
);
287 if (exec
->hadException())
290 if (v1
.isInt32() && v2
.isInt32())
296 double d1
= v1
.toNumber(exec
);
297 double d2
= v2
.toNumber(exec
);
301 if (v1
.isBoolean()) {
303 return static_cast<double>(v1
.getBoolean()) == v2
.uncheckedGetNumber();
304 } else if (v2
.isBoolean()) {
306 return v1
.uncheckedGetNumber() == static_cast<double>(v2
.getBoolean());
314 ALWAYS_INLINE
bool JSValue::strictEqualSlowCaseInline(ExecState
* exec
, JSValue v1
, JSValue v2
)
316 ASSERT(v1
.isCell() && v2
.isCell());
318 if (v1
.asCell()->isString() && v2
.asCell()->isString())
319 return asString(v1
)->value(exec
) == asString(v2
)->value(exec
);
324 inline bool JSValue::strictEqual(ExecState
* exec
, JSValue v1
, JSValue v2
)
326 if (v1
.isInt32() && v2
.isInt32())
329 if (v1
.isNumber() && v2
.isNumber())
330 return v1
.uncheckedGetNumber() == v2
.uncheckedGetNumber();
332 if (!v1
.isCell() || !v2
.isCell())
335 return strictEqualSlowCaseInline(exec
, v1
, v2
);
338 ALWAYS_INLINE
bool jsLess(CallFrame
* callFrame
, JSValue v1
, JSValue v2
)
340 if (v1
.isInt32() && v2
.isInt32())
341 return v1
.asInt32() < v2
.asInt32();
345 if (v1
.getNumber(n1
) && v2
.getNumber(n2
))
348 JSGlobalData
* globalData
= &callFrame
->globalData();
349 if (isJSString(globalData
, v1
) && isJSString(globalData
, v2
))
350 return asString(v1
)->value(callFrame
) < asString(v2
)->value(callFrame
);
354 bool wasNotString1
= v1
.getPrimitiveNumber(callFrame
, n1
, p1
);
355 bool wasNotString2
= v2
.getPrimitiveNumber(callFrame
, n2
, p2
);
357 if (wasNotString1
| wasNotString2
)
360 return asString(p1
)->value(callFrame
) < asString(p2
)->value(callFrame
);
363 inline bool jsLessEq(CallFrame
* callFrame
, JSValue v1
, JSValue v2
)
365 if (v1
.isInt32() && v2
.isInt32())
366 return v1
.asInt32() <= v2
.asInt32();
370 if (v1
.getNumber(n1
) && v2
.getNumber(n2
))
373 JSGlobalData
* globalData
= &callFrame
->globalData();
374 if (isJSString(globalData
, v1
) && isJSString(globalData
, v2
))
375 return !(asString(v2
)->value(callFrame
) < asString(v1
)->value(callFrame
));
379 bool wasNotString1
= v1
.getPrimitiveNumber(callFrame
, n1
, p1
);
380 bool wasNotString2
= v2
.getPrimitiveNumber(callFrame
, n2
, p2
);
382 if (wasNotString1
| wasNotString2
)
385 return !(asString(p2
)->value(callFrame
) < asString(p1
)->value(callFrame
));
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
397 ALWAYS_INLINE JSValue
jsAdd(CallFrame
* callFrame
, JSValue v1
, JSValue v2
)
399 double left
= 0.0, right
;
400 if (v1
.getNumber(left
) && v2
.getNumber(right
))
401 return jsNumber(callFrame
, left
+ right
);
405 ? jsString(callFrame
, asString(v1
), asString(v2
))
406 : jsString(callFrame
, asString(v1
), v2
.toPrimitiveString(callFrame
));
409 // All other cases are pretty uncommon
410 return jsAddSlowCase(callFrame
, v1
, v2
);
413 inline size_t normalizePrototypeChain(CallFrame
* callFrame
, JSValue base
, JSValue slotBase
, const Identifier
& propertyName
, size_t& slotOffset
)
415 JSCell
* cell
= asCell(base
);
418 while (slotBase
!= cell
) {
419 JSValue v
= cell
->structure()->prototypeForLookup(callFrame
);
421 // If we didn't find slotBase in base's prototype chain, then base
422 // must be a proxy for another object.
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
);
444 inline size_t normalizePrototypeChain(CallFrame
* callFrame
, JSCell
* base
)
448 JSValue v
= base
->structure()->prototypeForLookup(callFrame
);
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();
463 ALWAYS_INLINE JSValue
resolveBase(CallFrame
* callFrame
, Identifier
& property
, ScopeChainNode
* scopeChain
)
465 ScopeChainIterator iter
= scopeChain
->begin();
466 ScopeChainIterator next
= iter
;
468 ScopeChainIterator end
= scopeChain
->end();
475 if (next
== end
|| base
->getPropertySlot(callFrame
, property
, slot
))
482 ASSERT_NOT_REACHED();
487 #endif // Operations_h