]>
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"
28 #include "JSValueInlineMethods.h"
32 NEVER_INLINE JSValue
jsAddSlowCase(CallFrame
*, JSValue
, JSValue
);
33 JSValue
jsTypeStringForValue(CallFrame
*, JSValue
);
34 bool jsIsObjectType(JSValue
);
35 bool jsIsFunctionType(JSValue
);
37 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, JSString
* s1
, JSString
* s2
)
39 unsigned length1
= s1
->length();
42 unsigned length2
= s2
->length();
45 if ((length1
+ length2
) < length1
)
46 return throwOutOfMemoryError(exec
);
48 unsigned fiberCount
= s1
->fiberCount() + s2
->fiberCount();
49 JSGlobalData
* globalData
= &exec
->globalData();
51 if (fiberCount
<= JSString::s_maxInternalRopeLength
)
52 return new (globalData
) JSString(globalData
, fiberCount
, s1
, s2
);
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());
62 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, const UString
& u1
, JSString
* s2
)
64 unsigned length1
= u1
.length();
67 unsigned length2
= s2
->length();
69 return jsString(exec
, u1
);
70 if ((length1
+ length2
) < length1
)
71 return throwOutOfMemoryError(exec
);
73 unsigned fiberCount
= 1 + s2
->fiberCount();
74 JSGlobalData
* globalData
= &exec
->globalData();
76 if (fiberCount
<= JSString::s_maxInternalRopeLength
)
77 return new (globalData
) JSString(globalData
, fiberCount
, u1
, s2
);
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());
87 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, JSString
* s1
, const UString
& u2
)
89 unsigned length1
= s1
->length();
91 return jsString(exec
, u2
);
92 unsigned length2
= u2
.length();
95 if ((length1
+ length2
) < length1
)
96 return throwOutOfMemoryError(exec
);
98 unsigned fiberCount
= s1
->fiberCount() + 1;
99 JSGlobalData
* globalData
= &exec
->globalData();
101 if (fiberCount
<= JSString::s_maxInternalRopeLength
)
102 return new (globalData
) JSString(globalData
, fiberCount
, s1
, u2
);
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());
112 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, const UString
& u1
, const UString
& u2
)
114 unsigned length1
= u1
.length();
116 return jsString(exec
, u2
);
117 unsigned length2
= u2
.length();
119 return jsString(exec
, u1
);
120 if ((length1
+ length2
) < length1
)
121 return throwOutOfMemoryError(exec
);
123 JSGlobalData
* globalData
= &exec
->globalData();
124 return new (globalData
) JSString(globalData
, u1
, u2
);
127 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, const UString
& u1
, const UString
& u2
, const UString
& u3
)
129 unsigned length1
= u1
.length();
130 unsigned length2
= u2
.length();
131 unsigned length3
= u3
.length();
133 return jsString(exec
, u2
, u3
);
135 return jsString(exec
, u1
, u3
);
137 return jsString(exec
, u1
, u2
);
139 if ((length1
+ length2
) < length1
)
140 return throwOutOfMemoryError(exec
);
141 if ((length1
+ length2
+ length3
) < length3
)
142 return throwOutOfMemoryError(exec
);
144 JSGlobalData
* globalData
= &exec
->globalData();
145 return new (globalData
) JSString(globalData
, u1
, u2
, u3
);
148 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, Register
* strings
, unsigned count
)
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();
161 JSGlobalData
* globalData
= &exec
->globalData();
163 return new (globalData
) JSString(exec
, strings
[0].jsValue(), strings
[1].jsValue(), strings
[2].jsValue());
165 JSString::RopeBuilder
ropeBuilder(fiberCount
);
166 if (UNLIKELY(ropeBuilder
.isOutOfMemory()))
167 return throwOutOfMemoryError(exec
);
170 bool overflow
= false;
172 for (unsigned i
= 0; i
< count
; ++i
) {
173 JSValue v
= strings
[i
].jsValue();
174 if (LIKELY(v
.isString()))
175 ropeBuilder
.append(asString(v
));
177 ropeBuilder
.append(v
.toString(exec
));
179 unsigned newLength
= ropeBuilder
.length();
180 if (newLength
< length
)
186 return throwOutOfMemoryError(exec
);
188 return new (globalData
) JSString(globalData
, ropeBuilder
.release());
191 ALWAYS_INLINE JSValue
jsString(ExecState
* exec
, JSValue thisValue
)
193 unsigned fiberCount
= 0;
194 if (LIKELY(thisValue
.isString()))
195 fiberCount
+= asString(thisValue
)->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();
206 JSString::RopeBuilder
ropeBuilder(fiberCount
);
207 if (UNLIKELY(ropeBuilder
.isOutOfMemory()))
208 return throwOutOfMemoryError(exec
);
210 if (LIKELY(thisValue
.isString()))
211 ropeBuilder
.append(asString(thisValue
));
213 ropeBuilder
.append(thisValue
.toString(exec
));
216 bool overflow
= false;
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
));
223 ropeBuilder
.append(v
.toString(exec
));
225 unsigned newLength
= ropeBuilder
.length();
226 if (newLength
< length
)
232 return throwOutOfMemoryError(exec
);
234 JSGlobalData
* globalData
= &exec
->globalData();
235 return new (globalData
) JSString(globalData
, ropeBuilder
.release());
239 inline bool JSValue::equal(ExecState
* exec
, JSValue v1
, JSValue v2
)
241 if (v1
.isInt32() && v2
.isInt32())
244 return equalSlowCase(exec
, v1
, v2
);
247 ALWAYS_INLINE
bool JSValue::equalSlowCaseInline(ExecState
* exec
, JSValue v1
, JSValue v2
)
250 if (v1
.isNumber() && v2
.isNumber())
251 return v1
.uncheckedGetNumber() == v2
.uncheckedGetNumber();
253 bool s1
= v1
.isString();
254 bool s2
= v2
.isString();
256 return asString(v1
)->value(exec
) == asString(v2
)->value(exec
);
258 if (v1
.isUndefinedOrNull()) {
259 if (v2
.isUndefinedOrNull())
263 return v2
.asCell()->structure()->typeInfo().masqueradesAsUndefined();
266 if (v2
.isUndefinedOrNull()) {
269 return v1
.asCell()->structure()->typeInfo().masqueradesAsUndefined();
275 JSValue p1
= v1
.toPrimitive(exec
);
276 if (exec
->hadException())
279 if (v1
.isInt32() && v2
.isInt32())
285 JSValue p2
= v2
.toPrimitive(exec
);
286 if (exec
->hadException())
289 if (v1
.isInt32() && v2
.isInt32())
295 double d1
= v1
.toNumber(exec
);
296 double d2
= v2
.toNumber(exec
);
300 if (v1
.isBoolean()) {
302 return static_cast<double>(v1
.getBoolean()) == v2
.uncheckedGetNumber();
303 } else if (v2
.isBoolean()) {
305 return v1
.uncheckedGetNumber() == static_cast<double>(v2
.getBoolean());
313 ALWAYS_INLINE
bool JSValue::strictEqualSlowCaseInline(ExecState
* exec
, JSValue v1
, JSValue v2
)
315 ASSERT(v1
.isCell() && v2
.isCell());
317 if (v1
.asCell()->isString() && v2
.asCell()->isString())
318 return asString(v1
)->value(exec
) == asString(v2
)->value(exec
);
323 inline bool JSValue::strictEqual(ExecState
* exec
, JSValue v1
, JSValue v2
)
325 if (v1
.isInt32() && v2
.isInt32())
328 if (v1
.isNumber() && v2
.isNumber())
329 return v1
.uncheckedGetNumber() == v2
.uncheckedGetNumber();
331 if (!v1
.isCell() || !v2
.isCell())
334 return strictEqualSlowCaseInline(exec
, v1
, v2
);
337 ALWAYS_INLINE
bool jsLess(CallFrame
* callFrame
, JSValue v1
, JSValue v2
)
339 if (v1
.isInt32() && v2
.isInt32())
340 return v1
.asInt32() < v2
.asInt32();
344 if (v1
.getNumber(n1
) && v2
.getNumber(n2
))
347 JSGlobalData
* globalData
= &callFrame
->globalData();
348 if (isJSString(globalData
, v1
) && isJSString(globalData
, v2
))
349 return asString(v1
)->value(callFrame
) < asString(v2
)->value(callFrame
);
353 bool wasNotString1
= v1
.getPrimitiveNumber(callFrame
, n1
, p1
);
354 bool wasNotString2
= v2
.getPrimitiveNumber(callFrame
, n2
, p2
);
356 if (wasNotString1
| wasNotString2
)
359 return asString(p1
)->value(callFrame
) < asString(p2
)->value(callFrame
);
362 inline bool jsLessEq(CallFrame
* callFrame
, JSValue v1
, JSValue v2
)
364 if (v1
.isInt32() && v2
.isInt32())
365 return v1
.asInt32() <= v2
.asInt32();
369 if (v1
.getNumber(n1
) && v2
.getNumber(n2
))
372 JSGlobalData
* globalData
= &callFrame
->globalData();
373 if (isJSString(globalData
, v1
) && isJSString(globalData
, v2
))
374 return !(asString(v2
)->value(callFrame
) < asString(v1
)->value(callFrame
));
378 bool wasNotString1
= v1
.getPrimitiveNumber(callFrame
, n1
, p1
);
379 bool wasNotString2
= v2
.getPrimitiveNumber(callFrame
, n2
, p2
);
381 if (wasNotString1
| wasNotString2
)
384 return !(asString(p2
)->value(callFrame
) < asString(p1
)->value(callFrame
));
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
396 ALWAYS_INLINE JSValue
jsAdd(CallFrame
* callFrame
, JSValue v1
, JSValue v2
)
398 double left
= 0.0, right
;
399 if (v1
.getNumber(left
) && v2
.getNumber(right
))
400 return jsNumber(left
+ right
);
404 ? jsString(callFrame
, asString(v1
), asString(v2
))
405 : jsString(callFrame
, asString(v1
), v2
.toPrimitiveString(callFrame
));
408 // All other cases are pretty uncommon
409 return jsAddSlowCase(callFrame
, v1
, v2
);
412 inline size_t normalizePrototypeChain(CallFrame
* callFrame
, JSValue base
, JSValue slotBase
, const Identifier
& propertyName
, size_t& slotOffset
)
414 JSCell
* cell
= base
.asCell();
417 while (slotBase
!= cell
) {
418 JSValue v
= cell
->structure()->prototypeForLookup(callFrame
);
420 // If we didn't find slotBase in base's prototype chain, then base
421 // must be a proxy for another object.
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
);
443 inline size_t normalizePrototypeChain(CallFrame
* callFrame
, JSCell
* base
)
447 JSValue v
= base
->structure()->prototypeForLookup(callFrame
);
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());
462 ALWAYS_INLINE JSValue
resolveBase(CallFrame
* callFrame
, Identifier
& property
, ScopeChainNode
* scopeChain
, bool isStrictPut
)
464 ScopeChainIterator iter
= scopeChain
->begin();
465 ScopeChainIterator next
= iter
;
467 ScopeChainIterator end
= scopeChain
->end();
475 if (isStrictPut
&& !base
->getPropertySlot(callFrame
, property
, slot
))
479 if (base
->getPropertySlot(callFrame
, property
, slot
))
486 ASSERT_NOT_REACHED();
491 #endif // Operations_h