1/*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DFGOperations.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "CodeBlock.h"
32#include "Interpreter.h"
33#include "JSByteArray.h"
34#include "JSGlobalData.h"
35#include "Operations.h"
36
37namespace JSC { namespace DFG {
38
39EncodedJSValue operationConvertThis(ExecState* exec, EncodedJSValue encodedOp)
40{
41    return JSValue::encode(JSValue::decode(encodedOp).toThisObject(exec));
42}
43
44EncodedJSValue operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
45{
46    JSValue op1 = JSValue::decode(encodedOp1);
47    JSValue op2 = JSValue::decode(encodedOp2);
48
49    if (op1.isInt32() && op2.isInt32()) {
50        int64_t result64 = static_cast<int64_t>(op1.asInt32()) + static_cast<int64_t>(op2.asInt32());
51        int32_t result32 = static_cast<int32_t>(result64);
52        if (LIKELY(result32 == result64))
53            return JSValue::encode(jsNumber(result32));
54        return JSValue::encode(jsNumber((double)result64));
55    }
56
57    double number1;
58    double number2;
59    if (op1.getNumber(number1) && op2.getNumber(number2))
60        return JSValue::encode(jsNumber(number1 + number2));
61
62    return JSValue::encode(jsAddSlowCase(exec, op1, op2));
63}
64
65EncodedJSValue operationGetByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)
66{
67    JSValue baseValue = JSValue::decode(encodedBase);
68    JSValue property = JSValue::decode(encodedProperty);
69
70    if (LIKELY(baseValue.isCell())) {
71        JSCell* base = baseValue.asCell();
72
73        if (property.isUInt32()) {
74            JSGlobalData* globalData = &exec->globalData();
75            uint32_t i = property.asUInt32();
76
77            // FIXME: the JIT used to handle these in compiled code!
78            if (isJSArray(globalData, base) && asArray(base)->canGetIndex(i))
79                return JSValue::encode(asArray(base)->getIndex(i));
80
81            // FIXME: the JITstub used to relink this to an optimized form!
82            if (isJSString(globalData, base) && asString(base)->canGetIndex(i))
83                return JSValue::encode(asString(base)->getIndex(exec, i));
84
85            // FIXME: the JITstub used to relink this to an optimized form!
86            if (isJSByteArray(globalData, base) && asByteArray(base)->canAccessIndex(i))
87                return JSValue::encode(asByteArray(base)->getIndex(exec, i));
88
89            return JSValue::encode(baseValue.get(exec, i));
90        }
91
92        if (property.isString()) {
93            Identifier propertyName(exec, asString(property)->value(exec));
94            PropertySlot slot(base);
95            if (base->fastGetOwnPropertySlot(exec, propertyName, slot))
96                return JSValue::encode(slot.getValue(exec, propertyName));
97        }
98    }
99
100    Identifier ident(exec, property.toString(exec));
101    return JSValue::encode(baseValue.get(exec, ident));
102}
103
104EncodedJSValue operationGetById(ExecState* exec, EncodedJSValue encodedBase, Identifier* identifier)
105{
106    JSValue baseValue = JSValue::decode(encodedBase);
107    PropertySlot slot(baseValue);
108    return JSValue::encode(baseValue.get(exec, *identifier, slot));
109}
110
111template<bool strict>
112ALWAYS_INLINE static void operationPutByValInternal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
113{
114    JSGlobalData* globalData = &exec->globalData();
115
116    JSValue baseValue = JSValue::decode(encodedBase);
117    JSValue property = JSValue::decode(encodedProperty);
118    JSValue value = JSValue::decode(encodedValue);
119
120    if (LIKELY(property.isUInt32())) {
121        uint32_t i = property.asUInt32();
122
123        if (isJSArray(globalData, baseValue)) {
124            JSArray* jsArray = asArray(baseValue);
125            if (jsArray->canSetIndex(i)) {
126                jsArray->setIndex(*globalData, i, value);
127                return;
128            }
129
130            jsArray->JSArray::put(exec, i, value);
131            return;
132        }
133
134        if (isJSByteArray(globalData, baseValue) && asByteArray(baseValue)->canAccessIndex(i)) {
135            JSByteArray* jsByteArray = asByteArray(baseValue);
136            // FIXME: the JITstub used to relink this to an optimized form!
137            if (value.isInt32()) {
138                jsByteArray->setIndex(i, value.asInt32());
139                return;
140            }
141
142            double dValue = 0;
143            if (value.getNumber(dValue)) {
144                jsByteArray->setIndex(i, dValue);
145                return;
146            }
147        }
148
149        baseValue.put(exec, i, value);
150        return;
151    }
152
153    // Don't put to an object if toString throws an exception.
154    Identifier ident(exec, property.toString(exec));
155    if (!globalData->exception) {
156        PutPropertySlot slot(strict);
157        baseValue.put(exec, ident, value, slot);
158    }
159}
160
161void operationPutByValStrict(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
162{
163    operationPutByValInternal<true>(exec, encodedBase, encodedProperty, encodedValue);
164}
165
166void operationPutByValNonStrict(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
167{
168    operationPutByValInternal<false>(exec, encodedBase, encodedProperty, encodedValue);
169}
170
171void operationPutByIdStrict(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedBase, Identifier* identifier)
172{
173    PutPropertySlot slot(true);
174    JSValue::decode(encodedBase).put(exec, *identifier, JSValue::decode(encodedValue), slot);
175}
176
177void operationPutByIdNonStrict(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedBase, Identifier* identifier)
178{
179    PutPropertySlot slot(false);
180    JSValue::decode(encodedBase).put(exec, *identifier, JSValue::decode(encodedValue), slot);
181}
182
183void operationPutByIdDirectStrict(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedBase, Identifier* identifier)
184{
185    PutPropertySlot slot(true);
186    JSValue::decode(encodedBase).putDirect(exec, *identifier, JSValue::decode(encodedValue), slot);
187}
188
189void operationPutByIdDirectNonStrict(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedBase, Identifier* identifier)
190{
191    PutPropertySlot slot(false);
192    JSValue::decode(encodedBase).putDirect(exec, *identifier, JSValue::decode(encodedValue), slot);
193}
194
195bool operationCompareLess(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
196{
197    return jsLess(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
198}
199
200bool operationCompareLessEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
201{
202    return jsLessEq(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
203}
204
205bool operationCompareEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
206{
207    return JSValue::equal(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
208}
209
210bool operationCompareStrictEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
211{
212    return JSValue::strictEqual(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
213}
214
215DFGHandler lookupExceptionHandler(ExecState* exec, ReturnAddressPtr faultLocation)
216{
217    JSValue exceptionValue = exec->exception();
218    ASSERT(exceptionValue);
219
220    unsigned vPCIndex = exec->codeBlock()->bytecodeOffset(faultLocation);
221    HandlerInfo* handler = exec->globalData().interpreter->throwException(exec, exceptionValue, vPCIndex);
222
223    void* catchRoutine = handler ? handler->nativeCode.executableAddress() : (void*)ctiOpThrowNotCaught;
224    ASSERT(catchRoutine);
225    return DFGHandler(exec, catchRoutine);
226}
227
228double dfgConvertJSValueToNumber(ExecState* exec, EncodedJSValue value)
229{
230    return JSValue::decode(value).toNumber(exec);
231}
232
233int32_t dfgConvertJSValueToInt32(ExecState* exec, EncodedJSValue value)
234{
235    return JSValue::decode(value).toInt32(exec);
236}
237
238bool dfgConvertJSValueToBoolean(ExecState* exec, EncodedJSValue encodedOp)
239{
240    return JSValue::decode(encodedOp).toBoolean(exec);
241}
242
243} } // namespace JSC::DFG
244
245#endif
246