We should support CreateThis in the FTL
[WebKit-https.git] / Source / JavaScriptCore / runtime / ClonedArguments.cpp
1 /*
2  * Copyright (C) 2015-2017 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 "ClonedArguments.h"
28
29 #include "GetterSetter.h"
30 #include "InlineCallFrame.h"
31 #include "JSCInlines.h"
32
33 namespace JSC {
34
35 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ClonedArguments);
36
37 const ClassInfo ClonedArguments::s_info = { "Arguments", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ClonedArguments) };
38
39 ClonedArguments::ClonedArguments(VM& vm, Structure* structure, Butterfly* butterfly)
40     : Base(vm, structure, butterfly)
41 {
42 }
43
44 ClonedArguments* ClonedArguments::createEmpty(
45     VM& vm, Structure* structure, JSFunction* callee, unsigned length)
46 {
47     unsigned vectorLength = length;
48     if (vectorLength > MAX_STORAGE_VECTOR_LENGTH)
49         return 0;
50
51     Butterfly* butterfly;
52     if (UNLIKELY(structure->mayInterceptIndexedAccesses() || structure->storedPrototypeObject()->needsSlowPutIndexing(vm))) {
53         butterfly = createArrayStorageButterfly(vm, nullptr, structure, length, vectorLength);
54         butterfly->arrayStorage()->m_numValuesInVector = vectorLength;
55     } else {
56         IndexingHeader indexingHeader;
57         indexingHeader.setVectorLength(vectorLength);
58         indexingHeader.setPublicLength(length);
59         butterfly = Butterfly::tryCreate(vm, 0, 0, structure->outOfLineCapacity(), true, indexingHeader, vectorLength * sizeof(EncodedJSValue));
60         if (!butterfly)
61             return 0;
62
63         for (unsigned i = length; i < vectorLength; ++i)
64             butterfly->contiguous().atUnsafe(i).clear();
65     }
66     
67     ClonedArguments* result =
68         new (NotNull, allocateCell<ClonedArguments>(vm.heap))
69         ClonedArguments(vm, structure, butterfly);
70     result->finishCreation(vm);
71
72     result->m_callee.set(vm, result, callee);
73     result->putDirect(vm, clonedArgumentsLengthPropertyOffset, jsNumber(length));
74     return result;
75 }
76
77 ClonedArguments* ClonedArguments::createEmpty(ExecState* exec, JSFunction* callee, unsigned length)
78 {
79     VM& vm = exec->vm();
80     // NB. Some clients might expect that the global object of of this object is the global object
81     // of the callee. We don't do this for now, but maybe we should.
82     ClonedArguments* result = createEmpty(vm, exec->lexicalGlobalObject()->clonedArgumentsStructure(), callee, length);
83     ASSERT(!result->needsSlowPutIndexing(vm) || shouldUseSlowPut(result->structure(vm)->indexingType()));
84     return result;
85 }
86
87 ClonedArguments* ClonedArguments::createWithInlineFrame(ExecState* myFrame, ExecState* targetFrame, InlineCallFrame* inlineCallFrame, ArgumentsMode mode)
88 {
89     JSFunction* callee;
90     
91     if (inlineCallFrame)
92         callee = jsCast<JSFunction*>(inlineCallFrame->calleeRecovery.recover(targetFrame));
93     else
94         callee = jsCast<JSFunction*>(targetFrame->jsCallee());
95
96     ClonedArguments* result = nullptr;
97     
98     unsigned length = 0; // Initialize because VC needs it.
99     switch (mode) {
100     case ArgumentsMode::Cloned: {
101         if (inlineCallFrame) {
102             if (inlineCallFrame->argumentCountRegister.isValid())
103                 length = targetFrame->r(inlineCallFrame->argumentCountRegister).unboxedInt32();
104             else
105                 length = inlineCallFrame->argumentCountIncludingThis;
106             length--;
107             result = createEmpty(myFrame, callee, length);
108
109             for (unsigned i = length; i--;)
110                 result->putDirectIndex(myFrame, i, inlineCallFrame->argumentsWithFixup[i + 1].recover(targetFrame));
111         } else {
112             length = targetFrame->argumentCount();
113             result = createEmpty(myFrame, callee, length);
114
115             for (unsigned i = length; i--;)
116                 result->putDirectIndex(myFrame, i, targetFrame->uncheckedArgument(i));
117         }
118         break;
119     }
120         
121     case ArgumentsMode::FakeValues: {
122         result = createEmpty(myFrame, callee, 0);
123         break;
124     } }
125
126     ASSERT(myFrame->lexicalGlobalObject()->clonedArgumentsStructure() == result->structure(myFrame->vm()));
127     ASSERT(!result->needsSlowPutIndexing(myFrame->vm()) || shouldUseSlowPut(result->structure(myFrame->vm())->indexingType()));
128     return result;
129 }
130
131 ClonedArguments* ClonedArguments::createWithMachineFrame(ExecState* myFrame, ExecState* targetFrame, ArgumentsMode mode)
132 {
133     ClonedArguments* result = createWithInlineFrame(myFrame, targetFrame, nullptr, mode);
134     ASSERT(!result->needsSlowPutIndexing(myFrame->vm()) || shouldUseSlowPut(result->structure(myFrame->vm())->indexingType()));
135     return result;
136 }
137
138 ClonedArguments* ClonedArguments::createByCopyingFrom(
139     ExecState* exec, Structure* structure, Register* argumentStart, unsigned length,
140     JSFunction* callee)
141 {
142     VM& vm = exec->vm();
143     ClonedArguments* result = createEmpty(vm, structure, callee, length);
144     
145     for (unsigned i = length; i--;)
146         result->putDirectIndex(exec, i, argumentStart[i].jsValue());
147     ASSERT(!result->needsSlowPutIndexing(vm) || shouldUseSlowPut(result->structure(vm)->indexingType()));
148     return result;
149 }
150
151 Structure* ClonedArguments::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, IndexingType indexingType)
152 {
153     Structure* structure = Structure::create(vm, globalObject, prototype, TypeInfo(ClonedArgumentsType, StructureFlags), info(), indexingType);
154     PropertyOffset offset;
155     structure = structure->addPropertyTransition(vm, structure, vm.propertyNames->length, static_cast<unsigned>(PropertyAttribute::DontEnum), offset);
156     ASSERT(offset == clonedArgumentsLengthPropertyOffset);
157     return structure;
158 }
159
160 Structure* ClonedArguments::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
161 {
162     // We use contiguous storage because optimizations in the FTL assume that cloned arguments creation always produces the same initial structure.
163     return createStructure(vm, globalObject, prototype, NonArrayWithContiguous);
164 }
165
166 Structure* ClonedArguments::createSlowPutStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
167 {
168     return createStructure(vm, globalObject, prototype, NonArrayWithSlowPutArrayStorage);
169 }
170
171 bool ClonedArguments::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName ident, PropertySlot& slot)
172 {
173     ClonedArguments* thisObject = jsCast<ClonedArguments*>(object);
174     VM& vm = exec->vm();
175
176     if (!thisObject->specialsMaterialized()) {
177         FunctionExecutable* executable = jsCast<FunctionExecutable*>(thisObject->m_callee->executable());
178         bool isStrictMode = executable->isStrictMode();
179
180         if (ident == vm.propertyNames->callee) {
181             if (isStrictMode) {
182                 slot.setGetterSlot(thisObject, PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::Accessor, thisObject->globalObject(vm)->throwTypeErrorArgumentsCalleeAndCallerGetterSetter());
183                 return true;
184             }
185             slot.setValue(thisObject, 0, thisObject->m_callee.get());
186             return true;
187         }
188
189         if (ident == vm.propertyNames->iteratorSymbol) {
190             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum), thisObject->globalObject(vm)->arrayProtoValuesFunction());
191             return true;
192         }
193     }
194
195     return Base::getOwnPropertySlot(thisObject, exec, ident, slot);
196 }
197
198 void ClonedArguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
199 {
200     ClonedArguments* thisObject = jsCast<ClonedArguments*>(object);
201     thisObject->materializeSpecialsIfNecessary(exec);
202     Base::getOwnPropertyNames(thisObject, exec, array, mode);
203 }
204
205 bool ClonedArguments::put(JSCell* cell, ExecState* exec, PropertyName ident, JSValue value, PutPropertySlot& slot)
206 {
207     ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell);
208     VM& vm = exec->vm();
209     
210     if (ident == vm.propertyNames->callee
211         || ident == vm.propertyNames->iteratorSymbol) {
212         thisObject->materializeSpecialsIfNecessary(exec);
213         PutPropertySlot dummy = slot; // Shadow the given PutPropertySlot to prevent caching.
214         return Base::put(thisObject, exec, ident, value, dummy);
215     }
216     
217     return Base::put(thisObject, exec, ident, value, slot);
218 }
219
220 bool ClonedArguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName ident)
221 {
222     ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell);
223     VM& vm = exec->vm();
224     
225     if (ident == vm.propertyNames->callee
226         || ident == vm.propertyNames->iteratorSymbol)
227         thisObject->materializeSpecialsIfNecessary(exec);
228     
229     return Base::deleteProperty(thisObject, exec, ident);
230 }
231
232 bool ClonedArguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow)
233 {
234     ClonedArguments* thisObject = jsCast<ClonedArguments*>(object);
235     VM& vm = exec->vm();
236     
237     if (ident == vm.propertyNames->callee
238         || ident == vm.propertyNames->iteratorSymbol)
239         thisObject->materializeSpecialsIfNecessary(exec);
240     
241     return Base::defineOwnProperty(object, exec, ident, descriptor, shouldThrow);
242 }
243
244 void ClonedArguments::materializeSpecials(ExecState* exec)
245 {
246     RELEASE_ASSERT(!specialsMaterialized());
247     VM& vm = exec->vm();
248     
249     FunctionExecutable* executable = jsCast<FunctionExecutable*>(m_callee->executable());
250     bool isStrictMode = executable->isStrictMode();
251     
252     if (isStrictMode)
253         putDirectAccessor(exec, vm.propertyNames->callee, globalObject(vm)->throwTypeErrorArgumentsCalleeAndCallerGetterSetter(), PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::Accessor);
254     else
255         putDirect(vm, vm.propertyNames->callee, JSValue(m_callee.get()));
256
257     putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject(vm)->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
258     
259     m_callee.clear();
260 }
261
262 void ClonedArguments::materializeSpecialsIfNecessary(ExecState* exec)
263 {
264     if (!specialsMaterialized())
265         materializeSpecials(exec);
266 }
267
268 void ClonedArguments::visitChildren(JSCell* cell, SlotVisitor& visitor)
269 {
270     ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell);
271     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
272     Base::visitChildren(thisObject, visitor);
273     visitor.append(thisObject->m_callee);
274 }
275
276 } // namespace JSC
277