Objective-C API: Rename JSValue.h/APIJSValue.h to JSCJSValue.h/JSValue.h
[WebKit-https.git] / Source / JavaScriptCore / 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 "ExceptionHelpers.h"
26 #include "Interpreter.h"
27 #include "JSCJSValueInlines.h"
28 #include "JSProxy.h"
29 #include "JSString.h"
30
31 namespace JSC {
32
33     NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
34     JSValue jsTypeStringForValue(CallFrame*, JSValue);
35     JSValue jsTypeStringForValue(JSGlobalData&, JSGlobalObject*, JSValue);
36     bool jsIsObjectType(CallFrame*, JSValue);
37     bool jsIsFunctionType(JSValue);
38
39     ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
40     {
41         JSGlobalData& globalData = exec->globalData();
42
43         unsigned length1 = s1->length();
44         if (!length1)
45             return s2;
46         unsigned length2 = s2->length();
47         if (!length2)
48             return s1;
49         if ((length1 + length2) < length1)
50             return throwOutOfMemoryError(exec);
51
52         return JSRopeString::create(globalData, s1, s2);
53     }
54
55     ALWAYS_INLINE JSValue jsString(ExecState* exec, const String& u1, const String& u2, const String& u3)
56     {
57         JSGlobalData* globalData = &exec->globalData();
58
59         unsigned length1 = u1.length();
60         unsigned length2 = u2.length();
61         unsigned length3 = u3.length();
62         if (!length1)
63             return jsString(exec, jsString(globalData, u2), jsString(globalData, u3));
64         if (!length2)
65             return jsString(exec, jsString(globalData, u1), jsString(globalData, u3));
66         if (!length3)
67             return jsString(exec, jsString(globalData, u1), jsString(globalData, u2));
68
69         if ((length1 + length2) < length1)
70             return throwOutOfMemoryError(exec);
71         if ((length1 + length2 + length3) < length3)
72             return throwOutOfMemoryError(exec);
73
74         return JSRopeString::create(exec->globalData(), jsString(globalData, u1), jsString(globalData, u2), jsString(globalData, u3));
75     }
76
77     ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
78     {
79         JSGlobalData* globalData = &exec->globalData();
80         JSRopeString::RopeBuilder ropeBuilder(*globalData);
81
82         unsigned oldLength = 0;
83
84         for (unsigned i = 0; i < count; ++i) {
85             JSValue v = strings[i].jsValue();
86             ropeBuilder.append(v.toString(exec));
87
88             if (ropeBuilder.length() < oldLength) // True for overflow
89                 return throwOutOfMemoryError(exec);
90             oldLength = ropeBuilder.length();
91         }
92
93         return ropeBuilder.release();
94     }
95
96     ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue)
97     {
98         JSGlobalData* globalData = &exec->globalData();
99         JSRopeString::RopeBuilder ropeBuilder(*globalData);
100         ropeBuilder.append(thisValue.toString(exec));
101
102         unsigned oldLength = 0;
103
104         for (unsigned i = 0; i < exec->argumentCount(); ++i) {
105             JSValue v = exec->argument(i);
106             ropeBuilder.append(v.toString(exec));
107
108             if (ropeBuilder.length() < oldLength) // True for overflow
109                 return throwOutOfMemoryError(exec);
110             oldLength = ropeBuilder.length();
111         }
112
113         return ropeBuilder.release();
114     }
115
116     // See ES5 11.8.1/11.8.2/11.8.5 for definition of leftFirst, this value ensures correct
117     // evaluation ordering for argument conversions for '<' and '>'. For '<' pass the value
118     // true, for leftFirst, for '>' pass the value false (and reverse operand order).
119     template<bool leftFirst>
120     ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
121     {
122         if (v1.isInt32() && v2.isInt32())
123             return v1.asInt32() < v2.asInt32();
124
125         if (v1.isNumber() && v2.isNumber())
126             return v1.asNumber() < v2.asNumber();
127
128         if (isJSString(v1) && isJSString(v2))
129             return codePointCompareLessThan(asString(v1)->value(callFrame), asString(v2)->value(callFrame));
130
131         double n1;
132         double n2;
133         JSValue p1;
134         JSValue p2;
135         bool wasNotString1;
136         bool wasNotString2;
137         if (leftFirst) {
138             wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
139             wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
140         } else {
141             wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
142             wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
143         }
144
145         if (wasNotString1 | wasNotString2)
146             return n1 < n2;
147         return codePointCompareLessThan(asString(p1)->value(callFrame), asString(p2)->value(callFrame));
148     }
149
150     // See ES5 11.8.3/11.8.4/11.8.5 for definition of leftFirst, this value ensures correct
151     // evaluation ordering for argument conversions for '<=' and '=>'. For '<=' pass the
152     // value true, for leftFirst, for '=>' pass the value false (and reverse operand order).
153     template<bool leftFirst>
154     ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
155     {
156         if (v1.isInt32() && v2.isInt32())
157             return v1.asInt32() <= v2.asInt32();
158
159         if (v1.isNumber() && v2.isNumber())
160             return v1.asNumber() <= v2.asNumber();
161
162         if (isJSString(v1) && isJSString(v2))
163             return !codePointCompareLessThan(asString(v2)->value(callFrame), asString(v1)->value(callFrame));
164
165         double n1;
166         double n2;
167         JSValue p1;
168         JSValue p2;
169         bool wasNotString1;
170         bool wasNotString2;
171         if (leftFirst) {
172             wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
173             wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
174         } else {
175             wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
176             wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
177         }
178
179         if (wasNotString1 | wasNotString2)
180             return n1 <= n2;
181         return !codePointCompareLessThan(asString(p2)->value(callFrame), asString(p1)->value(callFrame));
182     }
183
184     // Fast-path choices here are based on frequency data from SunSpider:
185     //    <times> Add case: <t1> <t2>
186     //    ---------------------------
187     //    5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
188     //    247412  Add case: 5 5
189     //    20900   Add case: 5 6
190     //    13962   Add case: 5 3
191     //    4000    Add case: 3 5
192
193     ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
194     {
195         if (v1.isNumber() && v2.isNumber())
196             return jsNumber(v1.asNumber() + v2.asNumber());
197         
198         if (v1.isString() && !v2.isObject())
199             return jsString(callFrame, asString(v1), v2.toString(callFrame));
200
201         // All other cases are pretty uncommon
202         return jsAddSlowCase(callFrame, v1, v2);
203     }
204
205 #define InvalidPrototypeChain (std::numeric_limits<size_t>::max())
206
207     inline size_t normalizePrototypeChainForChainAccess(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, PropertyOffset& slotOffset)
208     {
209         JSCell* cell = base.asCell();
210         size_t count = 0;
211         
212         while (slotBase != cell) {
213             if (cell->isProxy())
214                 return InvalidPrototypeChain;
215             
216             if (cell->structure()->typeInfo().hasImpureGetOwnPropertySlot())
217                 return InvalidPrototypeChain;
218             
219             JSValue v = cell->structure()->prototypeForLookup(callFrame);
220
221             // If we didn't find slotBase in base's prototype chain, then base
222             // must be a proxy for another object.
223
224             if (v.isNull())
225                 return InvalidPrototypeChain;
226
227             cell = v.asCell();
228
229             // Since we're accessing a prototype in a loop, it's a good bet that it
230             // should not be treated as a dictionary.
231             if (cell->structure()->isDictionary()) {
232                 asObject(cell)->flattenDictionaryObject(callFrame->globalData());
233                 if (slotBase == cell)
234                     slotOffset = cell->structure()->get(callFrame->globalData(), propertyName); 
235             }
236             
237             ++count;
238         }
239         
240         ASSERT(count);
241         return count;
242     }
243
244     inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
245     {
246         size_t count = 0;
247         while (1) {
248             if (base->isProxy())
249                 return InvalidPrototypeChain;
250             
251             JSValue v = base->structure()->prototypeForLookup(callFrame);
252             if (v.isNull())
253                 return count;
254
255             base = v.asCell();
256
257             // Since we're accessing a prototype in a loop, it's a good bet that it
258             // should not be treated as a dictionary.
259             if (base->structure()->isDictionary())
260                 asObject(base)->flattenDictionaryObject(callFrame->globalData());
261
262             ++count;
263         }
264     }
265
266     inline bool isPrototypeChainNormalized(JSGlobalObject* globalObject, Structure* structure)
267     {
268         for (;;) {
269             if (structure->typeInfo().type() == ProxyType)
270                 return false;
271             
272             JSValue v = structure->prototypeForLookup(globalObject);
273             if (v.isNull())
274                 return true;
275             
276             structure = v.asCell()->structure();
277             
278             if (structure->isDictionary())
279                 return false;
280         }
281     }
282
283 } // namespace JSC
284
285 #endif // Operations_h