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
33namespace JSC {
34
35STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ClonedArguments);
36
37const ClassInfo ClonedArguments::s_info = { "Arguments", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ClonedArguments) };
38
39ClonedArguments::ClonedArguments(VM& vm, Structure* structure, Butterfly* butterfly)
40 : Base(vm, structure, butterfly)
41{
42}
43
44ClonedArguments* 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
77ClonedArguments* ClonedArguments::createEmpty(JSGlobalObject* globalObject, JSFunction* callee, unsigned length)
78{
79 VM& vm = globalObject->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, globalObject->clonedArgumentsStructure(), callee, length);
83 ASSERT(!result->needsSlowPutIndexing(vm) || shouldUseSlowPut(result->structure(vm)->indexingType()));
84 return result;
85}
86
87ClonedArguments* ClonedArguments::createWithInlineFrame(JSGlobalObject* globalObject, CallFrame* 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(globalObject, callee, length);
108
109 for (unsigned i = length; i--;)
110 result->putDirectIndex(globalObject, i, inlineCallFrame->argumentsWithFixup[i + 1].recover(targetFrame));
111 } else {
112 length = targetFrame->argumentCount();
113 result = createEmpty(globalObject, callee, length);
114
115 for (unsigned i = length; i--;)
116 result->putDirectIndex(globalObject, i, targetFrame->uncheckedArgument(i));
117 }
118 break;
119 }
120
121 case ArgumentsMode::FakeValues: {
122 result = createEmpty(globalObject, callee, 0);
123 break;
124 } }
125
126 ASSERT(globalObject->clonedArgumentsStructure() == result->structure(globalObject->vm()));
127 ASSERT(!result->needsSlowPutIndexing(globalObject->vm()) || shouldUseSlowPut(result->structure(globalObject->vm())->indexingType()));
128 return result;
129}
130
131ClonedArguments* ClonedArguments::createWithMachineFrame(JSGlobalObject* globalObject, CallFrame* targetFrame, ArgumentsMode mode)
132{
133 ClonedArguments* result = createWithInlineFrame(globalObject, targetFrame, nullptr, mode);
134 ASSERT(!result->needsSlowPutIndexing(globalObject->vm()) || shouldUseSlowPut(result->structure(globalObject->vm())->indexingType()));
135 return result;
136}
137
138ClonedArguments* ClonedArguments::createByCopyingFrom(
139 JSGlobalObject* globalObject, Structure* structure, Register* argumentStart, unsigned length,
140 JSFunction* callee)
141{
142 VM& vm = globalObject->vm();
143 ClonedArguments* result = createEmpty(vm, structure, callee, length);
144
145 for (unsigned i = length; i--;)
146 result->putDirectIndex(globalObject, i, argumentStart[i].jsValue());
147 ASSERT(!result->needsSlowPutIndexing(vm) || shouldUseSlowPut(result->structure(vm)->indexingType()));
148 return result;
149}
150
151Structure* 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 structure->addPropertyWithoutTransition(
155 vm, vm.propertyNames->length, static_cast<unsigned>(PropertyAttribute::DontEnum),
156 [&] (const GCSafeConcurrentJSLocker&, PropertyOffset offset, PropertyOffset newLastOffset) {
157 RELEASE_ASSERT(offset == clonedArgumentsLengthPropertyOffset);
158 structure->setLastOffset(newLastOffset);
159 });
160 return structure;
161}
162
163Structure* ClonedArguments::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
164{
165 // We use contiguous storage because optimizations in the FTL assume that cloned arguments creation always produces the same initial structure.
166 return createStructure(vm, globalObject, prototype, NonArrayWithContiguous);
167}
168
169Structure* ClonedArguments::createSlowPutStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
170{
171 return createStructure(vm, globalObject, prototype, NonArrayWithSlowPutArrayStorage);
172}
173
174bool ClonedArguments::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName ident, PropertySlot& slot)
175{
176 ClonedArguments* thisObject = jsCast<ClonedArguments*>(object);
177 VM& vm = globalObject->vm();
178
179 if (!thisObject->specialsMaterialized()) {
180 FunctionExecutable* executable = jsCast<FunctionExecutable*>(thisObject->m_callee->executable());
181 bool isStrictMode = executable->isStrictMode();
182
183 if (ident == vm.propertyNames->callee) {
184 if (isStrictMode) {
185 slot.setGetterSlot(thisObject, PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::Accessor, thisObject->globalObject(vm)->throwTypeErrorArgumentsCalleeAndCallerGetterSetter());
186 return true;
187 }
188 slot.setValue(thisObject, 0, thisObject->m_callee.get());
189 return true;
190 }
191
192 if (ident == vm.propertyNames->iteratorSymbol) {
193 slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum), thisObject->globalObject(vm)->arrayProtoValuesFunction());
194 return true;
195 }
196 }
197
198 return Base::getOwnPropertySlot(thisObject, globalObject, ident, slot);
199}
200
201void ClonedArguments::getOwnPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& array, EnumerationMode mode)
202{
203 ClonedArguments* thisObject = jsCast<ClonedArguments*>(object);
204 thisObject->materializeSpecialsIfNecessary(globalObject);
205 Base::getOwnPropertyNames(thisObject, globalObject, array, mode);
206}
207
208bool ClonedArguments::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName ident, JSValue value, PutPropertySlot& slot)
209{
210 ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell);
211 VM& vm = globalObject->vm();
212
213 if (ident == vm.propertyNames->callee
214 || ident == vm.propertyNames->iteratorSymbol) {
215 thisObject->materializeSpecialsIfNecessary(globalObject);
216 PutPropertySlot dummy = slot; // Shadow the given PutPropertySlot to prevent caching.
217 return Base::put(thisObject, globalObject, ident, value, dummy);
218 }
219
220 return Base::put(thisObject, globalObject, ident, value, slot);
221}
222
223bool ClonedArguments::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName ident)
224{
225 ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell);
226 VM& vm = globalObject->vm();
227
228 if (ident == vm.propertyNames->callee
229 || ident == vm.propertyNames->iteratorSymbol)
230 thisObject->materializeSpecialsIfNecessary(globalObject);
231
232 return Base::deleteProperty(thisObject, globalObject, ident);
233}
234
235bool ClonedArguments::defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow)
236{
237 ClonedArguments* thisObject = jsCast<ClonedArguments*>(object);
238 VM& vm = globalObject->vm();
239
240 if (ident == vm.propertyNames->callee
241 || ident == vm.propertyNames->iteratorSymbol)
242 thisObject->materializeSpecialsIfNecessary(globalObject);
243
244 return Base::defineOwnProperty(object, globalObject, ident, descriptor, shouldThrow);
245}
246
247void ClonedArguments::materializeSpecials(JSGlobalObject* globalObject)
248{
249 RELEASE_ASSERT(!specialsMaterialized());
250 VM& vm = globalObject->vm();
251
252 FunctionExecutable* executable = jsCast<FunctionExecutable*>(m_callee->executable());
253 bool isStrictMode = executable->isStrictMode();
254
255 if (isStrictMode)
256 putDirectAccessor(globalObject, vm.propertyNames->callee, this->globalObject(vm)->throwTypeErrorArgumentsCalleeAndCallerGetterSetter(), PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::Accessor);
257 else
258 putDirect(vm, vm.propertyNames->callee, JSValue(m_callee.get()));
259
260 putDirect(vm, vm.propertyNames->iteratorSymbol, this->globalObject(vm)->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
261
262 m_callee.clear();
263}
264
265void ClonedArguments::materializeSpecialsIfNecessary(JSGlobalObject* globalObject)
266{
267 if (!specialsMaterialized())
268 materializeSpecials(globalObject);
269}
270
271void ClonedArguments::visitChildren(JSCell* cell, SlotVisitor& visitor)
272{
273 ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell);
274 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
275 Base::visitChildren(thisObject, visitor);
276 visitor.append(thisObject->m_callee);
277}
278
279} // namespace JSC
280
281