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
30namespace 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 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, jsString: s1);
56 rope->append(index, jsString: 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, string: u1);
74 rope->append(index, jsString: 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, jsString: s1);
92 rope->append(index, string: 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(value: 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, jsString: asString(value: v));
123 else
124 rope->append(index, string: 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(value: thisValue)->ropeLength();
136 else
137 ++ropeLength;
138 for (unsigned i = 0; i < args.size(); ++i) {
139 JSValue v = args.at(idx: i);
140 if (LIKELY(v.isString()))
141 ropeLength += asString(value: 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, jsString: asString(value: thisValue));
153 else
154 rope->append(index, string: thisValue.toString(exec));
155 for (unsigned i = 0; i < args.size(); ++i) {
156 JSValue v = args.at(idx: i);
157 if (LIKELY(v.isString()))
158 rope->append(index, jsString: asString(value: v));
159 else
160 rope->append(index, string: v.toString(exec));
161 }
162 ASSERT(index == ropeLength);
163
164 JSGlobalData* globalData = &exec->globalData();
165 return new (globalData) JSString(globalData, rope.release());
166 }
167
168 // ECMA 11.9.3
169 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
170 {
171 if (v1.isInt32() && v2.isInt32())
172 return v1 == v2;
173
174 return equalSlowCase(exec, v1, v2);
175 }
176
177 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
178 {
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)
186 return asString(value: v1)->value(exec) == asString(value: v2)->value(exec);
187
188 if (v1.isUndefinedOrNull()) {
189 if (v2.isUndefinedOrNull())
190 return true;
191 if (!v2.isCell())
192 return false;
193 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
194 }
195
196 if (v2.isUndefinedOrNull()) {
197 if (!v1.isCell())
198 return false;
199 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
200 }
201
202 if (v1.isObject()) {
203 if (v2.isObject())
204 return v1 == v2
205#ifdef QT_BUILD_SCRIPT_LIB
206 || asObject(value: v1)->compareToObject(exec, other: asObject(value: v2))
207#endif
208 ;
209 JSValue p1 = v1.toPrimitive(exec);
210 if (exec->hadException())
211 return false;
212 v1 = p1;
213 if (v1.isInt32() && v2.isInt32())
214 return v1 == v2;
215 continue;
216 }
217
218 if (v2.isObject()) {
219 JSValue p2 = v2.toPrimitive(exec);
220 if (exec->hadException())
221 return false;
222 v2 = p2;
223 if (v1.isInt32() && v2.isInt32())
224 return v1 == v2;
225 continue;
226 }
227
228 if (s1 || s2) {
229 double d1 = v1.toNumber(exec);
230 double d2 = v2.toNumber(exec);
231 return d1 == d2;
232 }
233
234 if (v1.isBoolean()) {
235 if (v2.isNumber())
236 return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber();
237 } else if (v2.isBoolean()) {
238 if (v1.isNumber())
239 return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean());
240 }
241
242 return v1 == v2;
243 } while (true);
244 }
245
246 // ECMA 11.9.3
247 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
248 {
249 ASSERT(v1.isCell() && v2.isCell());
250
251 if (v1.asCell()->isString() && v2.asCell()->isString())
252 return asString(value: v1)->value(exec) == asString(value: v2)->value(exec);
253
254 return v1 == v2;
255 }
256
257 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
258 {
259 if (v1.isInt32() && v2.isInt32())
260 return v1 == v2;
261
262 if (v1.isNumber() && v2.isNumber())
263 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
264
265 if (!v1.isCell() || !v2.isCell())
266 return v1 == v2;
267
268 return strictEqualSlowCaseInline(exec, v1, v2);
269 }
270
271 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
272 {
273 if (v1.isInt32() && v2.isInt32())
274 return v1.asInt32() < v2.asInt32();
275
276 double n1;
277 double n2;
278 if (v1.getNumber(result&: n1) && v2.getNumber(result&: n2))
279 return n1 < n2;
280
281 JSGlobalData* globalData = &callFrame->globalData();
282 if (isJSString(globalData, v: v1) && isJSString(globalData, v: v2))
283 return asString(value: v1)->value(exec: callFrame) < asString(value: v2)->value(exec: callFrame);
284
285 JSValue p1;
286 JSValue p2;
287 bool wasNotString1 = v1.getPrimitiveNumber(exec: callFrame, number&: n1, value&: p1);
288 bool wasNotString2 = v2.getPrimitiveNumber(exec: callFrame, number&: n2, value&: p2);
289
290 if (wasNotString1 | wasNotString2)
291 return n1 < n2;
292
293 return asString(value: p1)->value(exec: callFrame) < asString(value: p2)->value(exec: callFrame);
294 }
295
296 inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
297 {
298 if (v1.isInt32() && v2.isInt32())
299 return v1.asInt32() <= v2.asInt32();
300
301 double n1;
302 double n2;
303 if (v1.getNumber(result&: n1) && v2.getNumber(result&: n2))
304 return n1 <= n2;
305
306 JSGlobalData* globalData = &callFrame->globalData();
307 if (isJSString(globalData, v: v1) && isJSString(globalData, v: v2))
308 return !(asString(value: v2)->value(exec: callFrame) < asString(value: v1)->value(exec: callFrame));
309
310 JSValue p1;
311 JSValue p2;
312 bool wasNotString1 = v1.getPrimitiveNumber(exec: callFrame, number&: n1, value&: p1);
313 bool wasNotString2 = v2.getPrimitiveNumber(exec: callFrame, number&: n2, value&: p2);
314
315 if (wasNotString1 | wasNotString2)
316 return n1 <= n2;
317
318 return !(asString(value: p2)->value(exec: callFrame) < asString(value: p1)->value(exec: callFrame));
319 }
320
321 // Fast-path choices here are based on frequency data from SunSpider:
322 // <times> Add case: <t1> <t2>
323 // ---------------------------
324 // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
325 // 247412 Add case: 5 5
326 // 20900 Add case: 5 6
327 // 13962 Add case: 5 3
328 // 4000 Add case: 3 5
329
330 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
331 {
332 double left = 0.0, right;
333 if (v1.getNumber(result&: left) && v2.getNumber(result&: right))
334 return jsNumber(exec: callFrame, d: left + right);
335
336 if (v1.isString()) {
337 return v2.isString()
338 ? jsString(exec: callFrame, s1: asString(value: v1), s2: asString(value: v2))
339 : jsString(exec: callFrame, s1: asString(value: v1), u2: v2.toPrimitiveString(exec: callFrame));
340 }
341
342 // All other cases are pretty uncommon
343 return jsAddSlowCase(callFrame, v1, v2);
344 }
345
346 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
347 {
348 JSCell* cell = asCell(value: base);
349 size_t count = 0;
350
351 while (slotBase != cell) {
352 JSValue v = cell->structure()->prototypeForLookup(callFrame);
353
354 // If we didn't find slotBase in base's prototype chain, then base
355 // must be a proxy for another object.
356
357 if (v.isNull())
358 return 0;
359
360 cell = asCell(value: v);
361
362 // Since we're accessing a prototype in a loop, it's a good bet that it
363 // should not be treated as a dictionary.
364 if (cell->structure()->isDictionary()) {
365 asObject(cell)->flattenDictionaryObject();
366 if (slotBase == cell)
367 slotOffset = cell->structure()->get(propertyName);
368 }
369
370 ++count;
371 }
372
373 ASSERT(count);
374 return count;
375 }
376
377 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
378 {
379 size_t count = 0;
380 while (1) {
381 JSValue v = base->structure()->prototypeForLookup(callFrame);
382 if (v.isNull())
383 return count;
384
385 base = asCell(value: v);
386
387 // Since we're accessing a prototype in a loop, it's a good bet that it
388 // should not be treated as a dictionary.
389 if (base->structure()->isDictionary())
390 asObject(cell: base)->flattenDictionaryObject();
391
392 ++count;
393 }
394 }
395
396 ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
397 {
398 ScopeChainIterator iter = scopeChain->begin();
399 ScopeChainIterator next = iter;
400 ++next;
401 ScopeChainIterator end = scopeChain->end();
402 ASSERT(iter != end);
403
404 PropertySlot slot;
405 JSObject* base;
406 while (true) {
407 base = *iter;
408 if (next == end || base->getPropertySlot(exec: callFrame, propertyName: property, slot))
409 return base;
410
411 iter = next;
412 ++next;
413 }
414
415 ASSERT_NOT_REACHED();
416 return JSValue();
417 }
418} // namespace JSC
419
420#endif // Operations_h
421

source code of qtscript/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Operations.h