]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Operations.h
JavaScriptCore-554.1.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 "Interpreter.h"
26 #include "JSImmediate.h"
27 #include "JSNumberCell.h"
28 #include "JSString.h"
29
30 namespace JSC {
31
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
38 // ECMA 11.9.3
39 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
40 {
41 if (v1.isInt32() && v2.isInt32())
42 return v1 == v2;
43
44 return equalSlowCase(exec, v1, v2);
45 }
46
47 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
48 {
49 do {
50 if (v1.isNumber() && v2.isNumber())
51 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
52
53 bool s1 = v1.isString();
54 bool s2 = v2.isString();
55 if (s1 && s2)
56 return asString(v1)->value() == asString(v2)->value();
57
58 if (v1.isUndefinedOrNull()) {
59 if (v2.isUndefinedOrNull())
60 return true;
61 if (!v2.isCell())
62 return false;
63 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
64 }
65
66 if (v2.isUndefinedOrNull()) {
67 if (!v1.isCell())
68 return false;
69 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
70 }
71
72 if (v1.isObject()) {
73 if (v2.isObject())
74 return v1 == v2;
75 JSValue p1 = v1.toPrimitive(exec);
76 if (exec->hadException())
77 return false;
78 v1 = p1;
79 if (v1.isInt32() && v2.isInt32())
80 return v1 == v2;
81 continue;
82 }
83
84 if (v2.isObject()) {
85 JSValue p2 = v2.toPrimitive(exec);
86 if (exec->hadException())
87 return false;
88 v2 = p2;
89 if (v1.isInt32() && v2.isInt32())
90 return v1 == v2;
91 continue;
92 }
93
94 if (s1 || s2) {
95 double d1 = v1.toNumber(exec);
96 double d2 = v2.toNumber(exec);
97 return d1 == d2;
98 }
99
100 if (v1.isBoolean()) {
101 if (v2.isNumber())
102 return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber();
103 } else if (v2.isBoolean()) {
104 if (v1.isNumber())
105 return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean());
106 }
107
108 return v1 == v2;
109 } while (true);
110 }
111
112 // ECMA 11.9.3
113 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(JSValue v1, JSValue v2)
114 {
115 ASSERT(v1.isCell() && v2.isCell());
116
117 if (v1.asCell()->isString() && v2.asCell()->isString())
118 return asString(v1)->value() == asString(v2)->value();
119
120 return v1 == v2;
121 }
122
123 inline bool JSValue::strictEqual(JSValue v1, JSValue v2)
124 {
125 if (v1.isInt32() && v2.isInt32())
126 return v1 == v2;
127
128 if (v1.isNumber() && v2.isNumber())
129 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
130
131 if (!v1.isCell() || !v2.isCell())
132 return v1 == v2;
133
134 return strictEqualSlowCaseInline(v1, v2);
135 }
136
137 inline bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
138 {
139 if (v1.isInt32() && v2.isInt32())
140 return v1.asInt32() < v2.asInt32();
141
142 double n1;
143 double n2;
144 if (v1.getNumber(n1) && v2.getNumber(n2))
145 return n1 < n2;
146
147 JSGlobalData* globalData = &callFrame->globalData();
148 if (isJSString(globalData, v1) && isJSString(globalData, v2))
149 return asString(v1)->value() < asString(v2)->value();
150
151 JSValue p1;
152 JSValue p2;
153 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
154 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
155
156 if (wasNotString1 | wasNotString2)
157 return n1 < n2;
158
159 return asString(p1)->value() < asString(p2)->value();
160 }
161
162 inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
163 {
164 if (v1.isInt32() && v2.isInt32())
165 return v1.asInt32() <= v2.asInt32();
166
167 double n1;
168 double n2;
169 if (v1.getNumber(n1) && v2.getNumber(n2))
170 return n1 <= n2;
171
172 JSGlobalData* globalData = &callFrame->globalData();
173 if (isJSString(globalData, v1) && isJSString(globalData, v2))
174 return !(asString(v2)->value() < asString(v1)->value());
175
176 JSValue p1;
177 JSValue p2;
178 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
179 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
180
181 if (wasNotString1 | wasNotString2)
182 return n1 <= n2;
183
184 return !(asString(p2)->value() < asString(p1)->value());
185 }
186
187 // Fast-path choices here are based on frequency data from SunSpider:
188 // <times> Add case: <t1> <t2>
189 // ---------------------------
190 // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
191 // 247412 Add case: 5 5
192 // 20900 Add case: 5 6
193 // 13962 Add case: 5 3
194 // 4000 Add case: 3 5
195
196 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
197 {
198 double left;
199 double right = 0.0;
200
201 bool rightIsNumber = v2.getNumber(right);
202 if (rightIsNumber && v1.getNumber(left))
203 return jsNumber(callFrame, left + right);
204
205 bool leftIsString = v1.isString();
206 if (leftIsString && v2.isString()) {
207 RefPtr<UString::Rep> value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep());
208 if (!value)
209 return throwOutOfMemoryError(callFrame);
210 return jsString(callFrame, value.release());
211 }
212
213 if (rightIsNumber & leftIsString) {
214 RefPtr<UString::Rep> value = v2.isInt32() ?
215 concatenate(asString(v1)->value().rep(), v2.asInt32()) :
216 concatenate(asString(v1)->value().rep(), right);
217
218 if (!value)
219 return throwOutOfMemoryError(callFrame);
220 return jsString(callFrame, value.release());
221 }
222
223 // All other cases are pretty uncommon
224 return jsAddSlowCase(callFrame, v1, v2);
225 }
226
227 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
228 {
229 JSCell* cell = asCell(base);
230 size_t count = 0;
231
232 while (slotBase != cell) {
233 JSValue v = cell->structure()->prototypeForLookup(callFrame);
234
235 // If we didn't find slotBase in base's prototype chain, then base
236 // must be a proxy for another object.
237
238 if (v.isNull())
239 return 0;
240
241 cell = asCell(v);
242
243 // Since we're accessing a prototype in a loop, it's a good bet that it
244 // should not be treated as a dictionary.
245 if (cell->structure()->isDictionary()) {
246 asObject(cell)->flattenDictionaryObject();
247 if (slotBase == cell)
248 slotOffset = cell->structure()->get(propertyName);
249 }
250
251 ++count;
252 }
253
254 ASSERT(count);
255 return count;
256 }
257
258 ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
259 {
260 ScopeChainIterator iter = scopeChain->begin();
261 ScopeChainIterator next = iter;
262 ++next;
263 ScopeChainIterator end = scopeChain->end();
264 ASSERT(iter != end);
265
266 PropertySlot slot;
267 JSObject* base;
268 while (true) {
269 base = *iter;
270 if (next == end || base->getPropertySlot(callFrame, property, slot))
271 return base;
272
273 iter = next;
274 ++next;
275 }
276
277 ASSERT_NOT_REACHED();
278 return JSValue();
279 }
280
281 ALWAYS_INLINE JSValue concatenateStrings(CallFrame* callFrame, Register* strings, unsigned count)
282 {
283 ASSERT(count >= 3);
284
285 // Estimate the amount of space required to hold the entire string. If all
286 // arguments are strings, we can easily calculate the exact amount of space
287 // required. For any other arguments, for now let's assume they may require
288 // 11 UChars of storage. This is enouch to hold any int, and likely is also
289 // reasonable for the other immediates. We may want to come back and tune
290 // this value at some point.
291 unsigned bufferSize = 0;
292 for (unsigned i = 0; i < count; ++i) {
293 JSValue v = strings[i].jsValue();
294 if (LIKELY(v.isString()))
295 bufferSize += asString(v)->value().size();
296 else
297 bufferSize += 11;
298 }
299
300 // Allocate an output string to store the result.
301 // If the first argument is a String, and if it has the capacity (or can grow
302 // its capacity) to hold the entire result then use this as a base to concatenate
303 // onto. Otherwise, allocate a new empty output buffer.
304 JSValue firstValue = strings[0].jsValue();
305 RefPtr<UString::Rep> resultRep;
306 if (firstValue.isString() && (resultRep = asString(firstValue)->value().rep())->reserveCapacity(bufferSize)) {
307 // We're going to concatenate onto the first string - remove it from the list of items to be appended.
308 ++strings;
309 --count;
310 } else
311 resultRep = UString::Rep::createEmptyBuffer(bufferSize);
312 UString result(resultRep);
313
314 // Loop over the openards, writing them into the output buffer.
315 for (unsigned i = 0; i < count; ++i) {
316 JSValue v = strings[i].jsValue();
317 if (LIKELY(v.isString()))
318 result.append(asString(v)->value());
319 else if (v.isInt32())
320 result.appendNumeric(v.asInt32());
321 else {
322 double d;
323 if (v.getNumber(d))
324 result.appendNumeric(d);
325 else
326 result.append(v.toString(callFrame));
327 }
328 }
329
330 return jsString(callFrame, result);
331 }
332
333 } // namespace JSC
334
335 #endif // Operations_h