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 
jsString(ExecState * exec,JSString * s1,JSString * s2)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 
jsString(ExecState * exec,const UString & u1,JSString * s2)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 
jsString(ExecState * exec,JSString * s1,const UString & u2)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 
jsString(ExecState * exec,Register * strings,unsigned count)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 
jsString(ExecState * exec,JSValue thisValue,const ArgList & args)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 
168     // ECMA 11.9.3
equal(ExecState * exec,JSValue v1,JSValue v2)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 
equalSlowCaseInline(ExecState * exec,JSValue v1,JSValue v2)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(v1)->value(exec) == asString(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(v1)->compareToObject(exec, asObject(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
strictEqualSlowCaseInline(ExecState * exec,JSValue v1,JSValue v2)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(v1)->value(exec) == asString(v2)->value(exec);
253 
254         return v1 == v2;
255     }
256 
strictEqual(ExecState * exec,JSValue v1,JSValue v2)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 
jsLess(CallFrame * callFrame,JSValue v1,JSValue v2)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(n1) && v2.getNumber(n2))
279             return n1 < n2;
280 
281         JSGlobalData* globalData = &callFrame->globalData();
282         if (isJSString(globalData, v1) && isJSString(globalData, v2))
283             return asString(v1)->value(callFrame) < asString(v2)->value(callFrame);
284 
285         JSValue p1;
286         JSValue p2;
287         bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
288         bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
289 
290         if (wasNotString1 | wasNotString2)
291             return n1 < n2;
292 
293         return asString(p1)->value(callFrame) < asString(p2)->value(callFrame);
294     }
295 
jsLessEq(CallFrame * callFrame,JSValue v1,JSValue v2)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(n1) && v2.getNumber(n2))
304             return n1 <= n2;
305 
306         JSGlobalData* globalData = &callFrame->globalData();
307         if (isJSString(globalData, v1) && isJSString(globalData, v2))
308             return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame));
309 
310         JSValue p1;
311         JSValue p2;
312         bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
313         bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
314 
315         if (wasNotString1 | wasNotString2)
316             return n1 <= n2;
317 
318         return !(asString(p2)->value(callFrame) < asString(p1)->value(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 
jsAdd(CallFrame * callFrame,JSValue v1,JSValue v2)330     ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
331     {
332         double left = 0.0, right;
333         if (v1.getNumber(left) && v2.getNumber(right))
334             return jsNumber(callFrame, left + right);
335 
336         if (v1.isString()) {
337             return v2.isString()
338                 ? jsString(callFrame, asString(v1), asString(v2))
339                 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame));
340         }
341 
342         // All other cases are pretty uncommon
343         return jsAddSlowCase(callFrame, v1, v2);
344     }
345 
normalizePrototypeChain(CallFrame * callFrame,JSValue base,JSValue slotBase,const Identifier & propertyName,size_t & slotOffset)346     inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
347     {
348         JSCell* cell = asCell(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(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 
normalizePrototypeChain(CallFrame * callFrame,JSCell * base)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(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(base)->flattenDictionaryObject();
391 
392             ++count;
393         }
394     }
395 
resolveBase(CallFrame * callFrame,Identifier & property,ScopeChainNode * scopeChain)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(callFrame, 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