Cache toString results for CoW arrays
[WebKit-https.git] / Source / JavaScriptCore / runtime / Operations.h
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2002-2017 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 #pragma once
23
24 #include "CallFrame.h"
25 #include "ExceptionHelpers.h"
26 #include "JSBigInt.h"
27 #include "JSCJSValue.h"
28 #include <wtf/Variant.h>
29
30 namespace JSC {
31
32 #define InvalidPrototypeChain (std::numeric_limits<size_t>::max())
33
34 NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
35 JSValue jsTypeStringForValue(CallFrame*, JSValue);
36 JSValue jsTypeStringForValue(VM&, JSGlobalObject*, JSValue);
37 bool jsIsObjectTypeOrNull(CallFrame*, JSValue);
38 size_t normalizePrototypeChain(CallFrame*, JSCell*, bool& sawPolyProto);
39
40 ALWAYS_INLINE JSString* jsString(ExecState* exec, JSString* s1, JSString* s2)
41 {
42     VM& vm = exec->vm();
43     auto scope = DECLARE_THROW_SCOPE(vm);
44
45     int32_t length1 = s1->length();
46     if (!length1)
47         return s2;
48     int32_t length2 = s2->length();
49     if (!length2)
50         return s1;
51     if (sumOverflows<int32_t>(length1, length2)) {
52         throwOutOfMemoryError(exec, scope);
53         return nullptr;
54     }
55
56     return JSRopeString::create(vm, s1, s2);
57 }
58
59 ALWAYS_INLINE JSString* jsString(ExecState* exec, JSString* s1, JSString* s2, JSString* s3)
60 {
61     VM& vm = exec->vm();
62     auto scope = DECLARE_THROW_SCOPE(vm);
63
64     int32_t length1 = s1->length();
65     if (!length1) {
66         scope.release();
67         return jsString(exec, s2, s3);
68     }
69     int32_t length2 = s2->length();
70     if (!length2) {
71         scope.release();
72         return jsString(exec, s1, s3);
73     }
74     int32_t length3 = s3->length();
75     if (!length3) {
76         scope.release();
77         return jsString(exec, s1, s2);
78     }
79
80     if (sumOverflows<int32_t>(length1, length2, length3)) {
81         throwOutOfMemoryError(exec, scope);
82         return nullptr;
83     }
84     return JSRopeString::create(vm, s1, s2, s3);
85 }
86
87 ALWAYS_INLINE JSString* jsString(ExecState* exec, const String& u1, const String& u2, const String& u3)
88 {
89     VM* vm = &exec->vm();
90     auto scope = DECLARE_THROW_SCOPE(*vm);
91
92     int32_t length1 = u1.length();
93     int32_t length2 = u2.length();
94     int32_t length3 = u3.length();
95     
96     if (length1 < 0 || length2 < 0 || length3 < 0) {
97         throwOutOfMemoryError(exec, scope);
98         return nullptr;
99     }
100     
101     if (!length1) {
102         scope.release();
103         return jsString(exec, jsString(vm, u2), jsString(vm, u3));
104     }
105     if (!length2) {
106         scope.release();
107         return jsString(exec, jsString(vm, u1), jsString(vm, u3));
108     }
109     if (!length3) {
110         scope.release();
111         return jsString(exec, jsString(vm, u1), jsString(vm, u2));
112     }
113
114     if (sumOverflows<int32_t>(length1, length2, length3)) {
115         throwOutOfMemoryError(exec, scope);
116         return nullptr;
117     }
118
119     return JSRopeString::create(*vm, jsString(vm, u1), jsString(vm, u2), jsString(vm, u3));
120 }
121
122 ALWAYS_INLINE JSValue jsStringFromRegisterArray(ExecState* exec, Register* strings, unsigned count)
123 {
124     VM* vm = &exec->vm();
125     auto scope = DECLARE_THROW_SCOPE(*vm);
126     JSRopeString::RopeBuilder<RecordOverflow> ropeBuilder(*vm);
127
128     for (unsigned i = 0; i < count; ++i) {
129         JSValue v = strings[-static_cast<int>(i)].jsValue();
130         JSString* string = v.toString(exec);
131         RETURN_IF_EXCEPTION(scope, { });
132         if (!ropeBuilder.append(string))
133             return throwOutOfMemoryError(exec, scope);
134     }
135
136     return ropeBuilder.release();
137 }
138
139 ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue)
140 {
141     VM* vm = &exec->vm();
142     auto scope = DECLARE_THROW_SCOPE(*vm);
143     JSRopeString::RopeBuilder<RecordOverflow> ropeBuilder(*vm);
144     JSString* str = thisValue.toString(exec);
145     RETURN_IF_EXCEPTION(scope, { });
146     ropeBuilder.append(str);
147
148     for (unsigned i = 0; i < exec->argumentCount(); ++i) {
149         JSValue v = exec->argument(i);
150         JSString* str = v.toString(exec);
151         RETURN_IF_EXCEPTION(scope, { });
152         if (UNLIKELY(!ropeBuilder.append(str)))
153             return throwOutOfMemoryError(exec, scope);
154     }
155
156     return ropeBuilder.release();
157 }
158
159 ALWAYS_INLINE bool bigIntCompareLess(CallFrame* callFrame, JSValue v1, JSValue v2)
160 {
161     ASSERT(v1.isBigInt() || v2.isBigInt());
162     ASSERT(v1.isPrimitive() && v2.isPrimitive());
163
164     VM& vm = callFrame->vm();
165     auto scope = DECLARE_THROW_SCOPE(vm);
166     
167     if (v1.isBigInt() && v2.isBigInt())
168         return JSBigInt::compare(asBigInt(v1), asBigInt(v2)) == JSBigInt::ComparisonResult::LessThan;
169     
170     if (v1.isBigInt()) {
171         JSValue primValue = v2;
172         if (primValue.isString()) {
173             JSBigInt* bigIntValue = JSBigInt::stringToBigInt(callFrame, asString(primValue)->value(callFrame));
174             RETURN_IF_EXCEPTION(scope, false);
175             if (!bigIntValue)
176                 return false;
177
178             return JSBigInt::compare(asBigInt(v1), bigIntValue) == JSBigInt::ComparisonResult::LessThan;
179         }
180
181         if (primValue.isBigInt())
182             return JSBigInt::compare(asBigInt(v1), asBigInt(primValue)) == JSBigInt::ComparisonResult::LessThan;
183
184         double numberValue = primValue.toNumber(callFrame);
185         RETURN_IF_EXCEPTION(scope, false);
186         return JSBigInt::compareToDouble(asBigInt(v1), numberValue) == JSBigInt::ComparisonResult::LessThan;
187     }
188     
189     JSValue primValue = v1;
190     if (primValue.isString()) {
191         JSBigInt* bigIntValue = JSBigInt::stringToBigInt(callFrame, asString(primValue)->value(callFrame));
192         RETURN_IF_EXCEPTION(scope, false);
193         if (!bigIntValue)
194             return false;
195
196         return JSBigInt::compare(bigIntValue, asBigInt(v2)) == JSBigInt::ComparisonResult::LessThan;
197     }
198     
199     if (primValue.isBigInt())
200         return JSBigInt::compare(asBigInt(primValue), asBigInt(v2)) == JSBigInt::ComparisonResult::LessThan;
201     
202     double numberValue = primValue.toNumber(callFrame);
203     RETURN_IF_EXCEPTION(scope, false);
204     return JSBigInt::compareToDouble(asBigInt(v2), numberValue) == JSBigInt::ComparisonResult::GreaterThan;
205 }
206
207 ALWAYS_INLINE bool toPrimitiveNumeric(CallFrame* callFrame, JSValue v, JSValue& p, double& n)
208 {
209     VM& vm = callFrame->vm();
210     auto scope = DECLARE_THROW_SCOPE(vm);
211     
212     p = v.toPrimitive(callFrame, PreferNumber);
213     RETURN_IF_EXCEPTION(scope, false);
214     if (p.isBigInt())
215         return true;
216     
217     n = p.toNumber(callFrame);
218     RETURN_IF_EXCEPTION(scope, false);
219     return !p.isString();
220 }
221
222 // See ES5 11.8.1/11.8.2/11.8.5 for definition of leftFirst, this value ensures correct
223 // evaluation ordering for argument conversions for '<' and '>'. For '<' pass the value
224 // true, for leftFirst, for '>' pass the value false (and reverse operand order).
225 template<bool leftFirst>
226 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
227 {
228     VM& vm = callFrame->vm();
229     auto scope = DECLARE_THROW_SCOPE(vm);
230
231     if (v1.isInt32() && v2.isInt32())
232         return v1.asInt32() < v2.asInt32();
233
234     if (v1.isNumber() && v2.isNumber())
235         return v1.asNumber() < v2.asNumber();
236
237     if (isJSString(v1) && isJSString(v2))
238         return codePointCompareLessThan(asString(v1)->value(callFrame), asString(v2)->value(callFrame));
239
240     double n1;
241     double n2;
242     JSValue p1;
243     JSValue p2;
244     bool wasNotString1;
245     bool wasNotString2;
246     if (leftFirst) {
247         wasNotString1 = toPrimitiveNumeric(callFrame, v1, p1, n1);
248         RETURN_IF_EXCEPTION(scope, false);
249         wasNotString2 = toPrimitiveNumeric(callFrame, v2, p2, n2);
250     } else {
251         wasNotString2 = toPrimitiveNumeric(callFrame, v2, p2, n2);
252         RETURN_IF_EXCEPTION(scope, false);
253         wasNotString1 = toPrimitiveNumeric(callFrame, v1, p1, n1);
254     }
255     RETURN_IF_EXCEPTION(scope, false);
256
257     if (wasNotString1 | wasNotString2) {
258         if (p1.isBigInt() || p2.isBigInt()) {
259             scope.release();
260             return bigIntCompareLess(callFrame, p1, p2);
261         }
262
263         return n1 < n2;
264     }
265     return codePointCompareLessThan(asString(p1)->value(callFrame), asString(p2)->value(callFrame));
266 }
267
268 // See ES5 11.8.3/11.8.4/11.8.5 for definition of leftFirst, this value ensures correct
269 // evaluation ordering for argument conversions for '<=' and '=>'. For '<=' pass the
270 // value true, for leftFirst, for '=>' pass the value false (and reverse operand order).
271 template<bool leftFirst>
272 ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
273 {
274     VM& vm = callFrame->vm();
275     auto scope = DECLARE_THROW_SCOPE(vm);
276
277     if (v1.isInt32() && v2.isInt32())
278         return v1.asInt32() <= v2.asInt32();
279
280     if (v1.isNumber() && v2.isNumber())
281         return v1.asNumber() <= v2.asNumber();
282
283     if (isJSString(v1) && isJSString(v2))
284         return !codePointCompareLessThan(asString(v2)->value(callFrame), asString(v1)->value(callFrame));
285
286     double n1;
287     double n2;
288     JSValue p1;
289     JSValue p2;
290     bool wasNotString1;
291     bool wasNotString2;
292     if (leftFirst) {
293         wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
294         RETURN_IF_EXCEPTION(scope, false);
295         wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
296     } else {
297         wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
298         RETURN_IF_EXCEPTION(scope, false);
299         wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
300     }
301     RETURN_IF_EXCEPTION(scope, false);
302
303     if (wasNotString1 | wasNotString2)
304         return n1 <= n2;
305     return !codePointCompareLessThan(asString(p2)->value(callFrame), asString(p1)->value(callFrame));
306 }
307
308 // Fast-path choices here are based on frequency data from SunSpider:
309 //    <times> Add case: <t1> <t2>
310 //    ---------------------------
311 //    5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
312 //    247412  Add case: 5 5
313 //    20900   Add case: 5 6
314 //    13962   Add case: 5 3
315 //    4000    Add case: 3 5
316
317 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
318 {
319     if (v1.isNumber() && v2.isNumber())
320         return jsNumber(v1.asNumber() + v2.asNumber());
321         
322     if (v1.isString() && !v2.isObject())
323         return jsString(callFrame, asString(v1), v2.toString(callFrame));
324
325     // All other cases are pretty uncommon
326     return jsAddSlowCase(callFrame, v1, v2);
327 }
328
329 ALWAYS_INLINE JSValue jsMul(ExecState* state, JSValue v1, JSValue v2)
330 {
331     VM& vm = state->vm();
332     auto scope = DECLARE_THROW_SCOPE(vm);
333
334     Variant<JSBigInt*, double> leftNumeric = v1.toNumeric(state);
335     RETURN_IF_EXCEPTION(scope, { });
336     Variant<JSBigInt*, double> rightNumeric = v2.toNumeric(state);
337     RETURN_IF_EXCEPTION(scope, { });
338
339     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
340         if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric))
341             return JSBigInt::multiply(state, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
342
343         throwTypeError(state, scope, ASCIILiteral("Invalid mix of BigInt and other type in multiplication."));
344         return { };
345     }
346
347     double leftValue =  WTF::get<double>(leftNumeric);
348     double rightValue =  WTF::get<double>(rightNumeric);
349     return jsNumber(leftValue * rightValue);
350 }
351
352 inline bool scribbleFreeCells()
353 {
354     return !ASSERT_DISABLED || Options::scribbleFreeCells();
355 }
356
357 #define SCRIBBLE_WORD static_cast<intptr_t>(0xbadbeef0)
358
359 inline bool isScribbledValue(JSValue value)
360 {
361     return JSValue::encode(value) == JSValue::encode(bitwise_cast<JSCell*>(SCRIBBLE_WORD));
362 }
363
364 inline void scribble(void* base, size_t size)
365 {
366     for (size_t i = size / sizeof(EncodedJSValue); i--;) {
367         // Use a 16-byte aligned value to ensure that it passes the cell check.
368         static_cast<EncodedJSValue*>(base)[i] = JSValue::encode(bitwise_cast<JSCell*>(SCRIBBLE_WORD));
369     }
370 }
371
372 } // namespace JSC