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 ; |
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 | 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 | |
163 | Structure* 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 | |
169 | Structure* ClonedArguments::createSlowPutStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) |
170 | { |
171 | return createStructure(vm, globalObject, prototype, NonArrayWithSlowPutArrayStorage); |
172 | } |
173 | |
174 | bool ClonedArguments::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName ident, PropertySlot& slot) |
175 | { |
176 | ClonedArguments* thisObject = jsCast<ClonedArguments*>(object); |
177 | VM& vm = exec->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, exec, ident, slot); |
199 | } |
200 | |
201 | void ClonedArguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) |
202 | { |
203 | ClonedArguments* thisObject = jsCast<ClonedArguments*>(object); |
204 | thisObject->materializeSpecialsIfNecessary(exec); |
205 | Base::getOwnPropertyNames(thisObject, exec, array, mode); |
206 | } |
207 | |
208 | bool ClonedArguments::put(JSCell* cell, ExecState* exec, PropertyName ident, JSValue value, PutPropertySlot& slot) |
209 | { |
210 | ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell); |
211 | VM& vm = exec->vm(); |
212 | |
213 | if (ident == vm.propertyNames->callee |
214 | || ident == vm.propertyNames->iteratorSymbol) { |
215 | thisObject->materializeSpecialsIfNecessary(exec); |
216 | PutPropertySlot dummy = slot; // Shadow the given PutPropertySlot to prevent caching. |
217 | return Base::put(thisObject, exec, ident, value, dummy); |
218 | } |
219 | |
220 | return Base::put(thisObject, exec, ident, value, slot); |
221 | } |
222 | |
223 | bool ClonedArguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName ident) |
224 | { |
225 | ClonedArguments* thisObject = jsCast<ClonedArguments*>(cell); |
226 | VM& vm = exec->vm(); |
227 | |
228 | if (ident == vm.propertyNames->callee |
229 | || ident == vm.propertyNames->iteratorSymbol) |
230 | thisObject->materializeSpecialsIfNecessary(exec); |
231 | |
232 | return Base::deleteProperty(thisObject, exec, ident); |
233 | } |
234 | |
235 | bool ClonedArguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName ident, const PropertyDescriptor& descriptor, bool shouldThrow) |
236 | { |
237 | ClonedArguments* thisObject = jsCast<ClonedArguments*>(object); |
238 | VM& vm = exec->vm(); |
239 | |
240 | if (ident == vm.propertyNames->callee |
241 | || ident == vm.propertyNames->iteratorSymbol) |
242 | thisObject->materializeSpecialsIfNecessary(exec); |
243 | |
244 | return Base::defineOwnProperty(object, exec, ident, descriptor, shouldThrow); |
245 | } |
246 | |
247 | void ClonedArguments::materializeSpecials(ExecState* exec) |
248 | { |
249 | RELEASE_ASSERT(!specialsMaterialized()); |
250 | VM& vm = exec->vm(); |
251 | |
252 | FunctionExecutable* executable = jsCast<FunctionExecutable*>(m_callee->executable()); |
253 | bool isStrictMode = executable->isStrictMode(); |
254 | |
255 | if (isStrictMode) |
256 | putDirectAccessor(exec, vm.propertyNames->callee, 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, globalObject(vm)->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum)); |
261 | |
262 | m_callee.clear(); |
263 | } |
264 | |
265 | void ClonedArguments::materializeSpecialsIfNecessary(ExecState* exec) |
266 | { |
267 | if (!specialsMaterialized()) |
268 | materializeSpecials(exec); |
269 | } |
270 | |
271 | void 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 | |