[ESNext][BigInt] Implement support for "=<" and ">=" relational operation
[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 bigIntCompareResult(JSBigInt::ComparisonResult comparisonResult, JSBigInt::ComparisonMode comparisonMode)
160 {
161     if (comparisonMode == JSBigInt::ComparisonMode::LessThan)
162         return comparisonResult == JSBigInt::ComparisonResult::LessThan;
163
164     ASSERT(comparisonMode == JSBigInt::ComparisonMode::LessThanOrEqual);
165     return comparisonResult == JSBigInt::ComparisonResult::LessThan || comparisonResult == JSBigInt::ComparisonResult::Equal;
166 }
167
168 ALWAYS_INLINE bool bigIntCompare(CallFrame* callFrame, JSValue v1, JSValue v2, JSBigInt::ComparisonMode comparisonMode)
169 {
170     ASSERT(v1.isBigInt() || v2.isBigInt());
171     ASSERT(v1.isPrimitive() && v2.isPrimitive());
172
173     VM& vm = callFrame->vm();
174     auto scope = DECLARE_THROW_SCOPE(vm);
175     
176     if (v1.isBigInt() && v2.isBigInt())
177         return bigIntCompareResult(JSBigInt::compare(asBigInt(v1), asBigInt(v2)), comparisonMode);
178     
179     if (v1.isBigInt()) {
180         JSValue primValue = v2;
181         if (primValue.isString()) {
182             JSBigInt* bigIntValue = JSBigInt::stringToBigInt(callFrame, asString(primValue)->value(callFrame));
183             RETURN_IF_EXCEPTION(scope, false);
184             if (!bigIntValue)
185                 return false;
186
187             return bigIntCompareResult(JSBigInt::compare(asBigInt(v1), bigIntValue), comparisonMode);
188         }
189
190         if (primValue.isBigInt())
191             return bigIntCompareResult(JSBigInt::compare(asBigInt(v1), asBigInt(primValue)), comparisonMode);
192
193         double numberValue = primValue.toNumber(callFrame);
194         RETURN_IF_EXCEPTION(scope, false);
195         return bigIntCompareResult(JSBigInt::compareToDouble(asBigInt(v1), numberValue), comparisonMode);
196     }
197     
198     JSValue primValue = v1;
199     if (primValue.isString()) {
200         JSBigInt* bigIntValue = JSBigInt::stringToBigInt(callFrame, asString(primValue)->value(callFrame));
201         RETURN_IF_EXCEPTION(scope, false);
202         if (!bigIntValue)
203             return false;
204
205         return bigIntCompareResult(JSBigInt::compare(bigIntValue, asBigInt(v2)), comparisonMode);
206     }
207     
208     if (primValue.isBigInt())
209         return bigIntCompareResult(JSBigInt::compare(asBigInt(primValue), asBigInt(v2)), comparisonMode);
210     
211     double numberValue = primValue.toNumber(callFrame);
212     RETURN_IF_EXCEPTION(scope, false);
213
214     // Here we check inverted because BigInt is the v2
215     JSBigInt::ComparisonResult comparisonResult = JSBigInt::compareToDouble(asBigInt(v2), numberValue);
216     if (comparisonMode == JSBigInt::ComparisonMode::LessThan)
217         return comparisonResult == JSBigInt::ComparisonResult::GreaterThan;
218
219     return comparisonResult == JSBigInt::ComparisonResult::GreaterThan || comparisonResult == JSBigInt::ComparisonResult::Equal;
220 }
221
222 ALWAYS_INLINE bool toPrimitiveNumeric(CallFrame* callFrame, JSValue v, JSValue& p, double& n)
223 {
224     VM& vm = callFrame->vm();
225     auto scope = DECLARE_THROW_SCOPE(vm);
226     
227     p = v.toPrimitive(callFrame, PreferNumber);
228     RETURN_IF_EXCEPTION(scope, false);
229     if (p.isBigInt())
230         return true;
231     
232     n = p.toNumber(callFrame);
233     RETURN_IF_EXCEPTION(scope, false);
234     return !p.isString();
235 }
236
237 // See ES5 11.8.1/11.8.2/11.8.5 for definition of leftFirst, this value ensures correct
238 // evaluation ordering for argument conversions for '<' and '>'. For '<' pass the value
239 // true, for leftFirst, for '>' pass the value false (and reverse operand order).
240 template<bool leftFirst>
241 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
242 {
243     VM& vm = callFrame->vm();
244     auto scope = DECLARE_THROW_SCOPE(vm);
245
246     if (v1.isInt32() && v2.isInt32())
247         return v1.asInt32() < v2.asInt32();
248
249     if (v1.isNumber() && v2.isNumber())
250         return v1.asNumber() < v2.asNumber();
251
252     if (isJSString(v1) && isJSString(v2))
253         return codePointCompareLessThan(asString(v1)->value(callFrame), asString(v2)->value(callFrame));
254
255     double n1;
256     double n2;
257     JSValue p1;
258     JSValue p2;
259     bool wasNotString1;
260     bool wasNotString2;
261     if (leftFirst) {
262         wasNotString1 = toPrimitiveNumeric(callFrame, v1, p1, n1);
263         RETURN_IF_EXCEPTION(scope, false);
264         wasNotString2 = toPrimitiveNumeric(callFrame, v2, p2, n2);
265     } else {
266         wasNotString2 = toPrimitiveNumeric(callFrame, v2, p2, n2);
267         RETURN_IF_EXCEPTION(scope, false);
268         wasNotString1 = toPrimitiveNumeric(callFrame, v1, p1, n1);
269     }
270     RETURN_IF_EXCEPTION(scope, false);
271
272     if (wasNotString1 | wasNotString2) {
273         if (p1.isBigInt() || p2.isBigInt()) {
274             scope.release();
275             return bigIntCompare(callFrame, p1, p2, JSBigInt::ComparisonMode::LessThan);
276         }
277
278         return n1 < n2;
279     }
280
281     return codePointCompareLessThan(asString(p1)->value(callFrame), asString(p2)->value(callFrame));
282 }
283
284 // See ES5 11.8.3/11.8.4/11.8.5 for definition of leftFirst, this value ensures correct
285 // evaluation ordering for argument conversions for '<=' and '=>'. For '<=' pass the
286 // value true, for leftFirst, for '=>' pass the value false (and reverse operand order).
287 template<bool leftFirst>
288 ALWAYS_INLINE bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
289 {
290     VM& vm = callFrame->vm();
291     auto scope = DECLARE_THROW_SCOPE(vm);
292
293     if (v1.isInt32() && v2.isInt32())
294         return v1.asInt32() <= v2.asInt32();
295
296     if (v1.isNumber() && v2.isNumber())
297         return v1.asNumber() <= v2.asNumber();
298
299     if (isJSString(v1) && isJSString(v2))
300         return !codePointCompareLessThan(asString(v2)->value(callFrame), asString(v1)->value(callFrame));
301
302     double n1;
303     double n2;
304     JSValue p1;
305     JSValue p2;
306     bool wasNotString1;
307     bool wasNotString2;
308     if (leftFirst) {
309         wasNotString1 = toPrimitiveNumeric(callFrame, v1, p1, n1);
310         RETURN_IF_EXCEPTION(scope, false);
311         wasNotString2 = toPrimitiveNumeric(callFrame, v2, p2, n2);
312     } else {
313         wasNotString2 = toPrimitiveNumeric(callFrame, v2, p2, n2);
314         RETURN_IF_EXCEPTION(scope, false);
315         wasNotString1 = toPrimitiveNumeric(callFrame, v1, p1, n1);
316     }
317     RETURN_IF_EXCEPTION(scope, false);
318
319     if (wasNotString1 | wasNotString2) {
320         if (p1.isBigInt() || p2.isBigInt()) {
321             scope.release();
322             return bigIntCompare(callFrame, p1, p2, JSBigInt::ComparisonMode::LessThanOrEqual);
323         }
324
325         return n1 <= n2;
326     }
327     return !codePointCompareLessThan(asString(p2)->value(callFrame), asString(p1)->value(callFrame));
328 }
329
330 // Fast-path choices here are based on frequency data from SunSpider:
331 //    <times> Add case: <t1> <t2>
332 //    ---------------------------
333 //    5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
334 //    247412  Add case: 5 5
335 //    20900   Add case: 5 6
336 //    13962   Add case: 5 3
337 //    4000    Add case: 3 5
338
339 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
340 {
341     if (v1.isNumber() && v2.isNumber())
342         return jsNumber(v1.asNumber() + v2.asNumber());
343         
344     if (v1.isString() && !v2.isObject())
345         return jsString(callFrame, asString(v1), v2.toString(callFrame));
346
347     // All other cases are pretty uncommon
348     return jsAddSlowCase(callFrame, v1, v2);
349 }
350
351 ALWAYS_INLINE JSValue jsMul(ExecState* state, JSValue v1, JSValue v2)
352 {
353     VM& vm = state->vm();
354     auto scope = DECLARE_THROW_SCOPE(vm);
355
356     Variant<JSBigInt*, double> leftNumeric = v1.toNumeric(state);
357     RETURN_IF_EXCEPTION(scope, { });
358     Variant<JSBigInt*, double> rightNumeric = v2.toNumeric(state);
359     RETURN_IF_EXCEPTION(scope, { });
360
361     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
362         if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric))
363             return JSBigInt::multiply(state, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
364
365         throwTypeError(state, scope, ASCIILiteral("Invalid mix of BigInt and other type in multiplication."));
366         return { };
367     }
368
369     double leftValue =  WTF::get<double>(leftNumeric);
370     double rightValue =  WTF::get<double>(rightNumeric);
371     return jsNumber(leftValue * rightValue);
372 }
373
374 inline bool scribbleFreeCells()
375 {
376     return !ASSERT_DISABLED || Options::scribbleFreeCells();
377 }
378
379 #define SCRIBBLE_WORD static_cast<intptr_t>(0xbadbeef0)
380
381 inline bool isScribbledValue(JSValue value)
382 {
383     return JSValue::encode(value) == JSValue::encode(bitwise_cast<JSCell*>(SCRIBBLE_WORD));
384 }
385
386 inline void scribble(void* base, size_t size)
387 {
388     for (size_t i = size / sizeof(EncodedJSValue); i--;) {
389         // Use a 16-byte aligned value to ensure that it passes the cell check.
390         static_cast<EncodedJSValue*>(base)[i] = JSValue::encode(bitwise_cast<JSCell*>(SCRIBBLE_WORD));
391     }
392 }
393
394 } // namespace JSC