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