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