1/*
2 * Copyright (C) 2015-2018 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#pragma once
27
28#include "CagedBarrierPtr.h"
29#include "DirectArgumentsOffset.h"
30#include "GenericArguments.h"
31#include <wtf/CagedPtr.h>
32
33namespace JSC {
34
35// This is an Arguments-class object that we create when you say "arguments" inside a function,
36// and none of the arguments are captured in the function's activation. The function will copy all
37// of its arguments into this object, and all subsequent accesses to the arguments will go through
38// this object thereafter. Special support is in place for mischevious events like the arguments
39// being deleted (something like "delete arguments[0]") or reconfigured (broadly, we say deletions
40// and reconfigurations mean that the respective argument was "overridden").
41//
42// To speed allocation, this object will hold all of the arguments in-place. The arguments as well
43// as a table of flags saying which arguments were overridden.
44class DirectArguments final : public GenericArguments<DirectArguments> {
45private:
46 DirectArguments(VM&, Structure*, unsigned length, unsigned capacity);
47
48public:
49 template<typename CellType, SubspaceAccess>
50 static CompleteSubspace* subspaceFor(VM& vm)
51 {
52 static_assert(!CellType::needsDestruction, "");
53 return &vm.jsValueGigacageCellSpace;
54 }
55
56 // Creates an arguments object but leaves it uninitialized. This is dangerous if we GC right
57 // after allocation.
58 static DirectArguments* createUninitialized(VM&, Structure*, unsigned length, unsigned capacity);
59
60 // Creates an arguments object and initializes everything to the empty value. Use this if you
61 // cannot guarantee that you'll immediately initialize all of the elements.
62 static DirectArguments* create(VM&, Structure*, unsigned length, unsigned capacity);
63
64 // Creates an arguments object by copying the argumnets from the stack.
65 static DirectArguments* createByCopying(ExecState*);
66
67 static size_t estimatedSize(JSCell*, VM&);
68 static void visitChildren(JSCell*, SlotVisitor&);
69
70 uint32_t internalLength() const
71 {
72 return m_length;
73 }
74
75 uint32_t length(ExecState* exec) const
76 {
77 if (UNLIKELY(m_mappedArguments)) {
78 VM& vm = exec->vm();
79 auto scope = DECLARE_THROW_SCOPE(vm);
80 JSValue value = get(exec, vm.propertyNames->length);
81 RETURN_IF_EXCEPTION(scope, 0);
82 RELEASE_AND_RETURN(scope, value.toUInt32(exec));
83 }
84 return m_length;
85 }
86
87 bool isMappedArgument(uint32_t i) const
88 {
89 return i < m_length && (!m_mappedArguments || !m_mappedArguments.at(i, m_length));
90 }
91
92 bool isMappedArgumentInDFG(uint32_t i) const
93 {
94 return i < m_length && !overrodeThings();
95 }
96
97 JSValue getIndexQuickly(uint32_t i) const
98 {
99 ASSERT_WITH_SECURITY_IMPLICATION(isMappedArgument(i));
100 return const_cast<DirectArguments*>(this)->storage()[i].get();
101 }
102
103 void setIndexQuickly(VM& vm, uint32_t i, JSValue value)
104 {
105 ASSERT_WITH_SECURITY_IMPLICATION(isMappedArgument(i));
106 storage()[i].set(vm, this, value);
107 }
108
109 JSFunction* callee()
110 {
111 return m_callee.get();
112 }
113
114 void setCallee(VM& vm, JSFunction* function)
115 {
116 m_callee.set(vm, this, function);
117 }
118
119 WriteBarrier<Unknown>& argument(DirectArgumentsOffset offset)
120 {
121 ASSERT(offset);
122 ASSERT_WITH_SECURITY_IMPLICATION(offset.offset() < std::max(m_length, m_minCapacity));
123 return storage()[offset.offset()];
124 }
125
126 // Methods intended for use by the GenericArguments mixin.
127 bool overrodeThings() const { return !!m_mappedArguments; }
128 void overrideThings(VM&);
129 void overrideThingsIfNecessary(VM&);
130 void unmapArgument(VM&, unsigned index);
131
132 void initModifiedArgumentsDescriptorIfNecessary(VM& vm)
133 {
134 GenericArguments<DirectArguments>::initModifiedArgumentsDescriptorIfNecessary(vm, m_length);
135 }
136
137 void setModifiedArgumentDescriptor(VM& vm, unsigned index)
138 {
139 GenericArguments<DirectArguments>::setModifiedArgumentDescriptor(vm, index, m_length);
140 }
141
142 bool isModifiedArgumentDescriptor(unsigned index)
143 {
144 return GenericArguments<DirectArguments>::isModifiedArgumentDescriptor(index, m_length);
145 }
146
147 void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length);
148
149 DECLARE_INFO;
150
151 static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
152
153 static ptrdiff_t offsetOfCallee() { return OBJECT_OFFSETOF(DirectArguments, m_callee); }
154 static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(DirectArguments, m_length); }
155 static ptrdiff_t offsetOfMinCapacity() { return OBJECT_OFFSETOF(DirectArguments, m_minCapacity); }
156 static ptrdiff_t offsetOfMappedArguments() { return OBJECT_OFFSETOF(DirectArguments, m_mappedArguments); }
157 static ptrdiff_t offsetOfModifiedArgumentsDescriptor() { return OBJECT_OFFSETOF(DirectArguments, m_modifiedArgumentsDescriptor); }
158
159 static size_t storageOffset()
160 {
161 return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<Unknown>)>(sizeof(DirectArguments));
162 }
163
164 static size_t offsetOfSlot(Checked<size_t> index)
165 {
166 return (storageOffset() + sizeof(WriteBarrier<Unknown>) * index).unsafeGet();
167 }
168
169 static size_t allocationSize(Checked<size_t> capacity)
170 {
171 return offsetOfSlot(capacity);
172 }
173
174private:
175 WriteBarrier<Unknown>* storage()
176 {
177 return bitwise_cast<WriteBarrier<Unknown>*>(bitwise_cast<char*>(this) + storageOffset());
178 }
179
180 unsigned mappedArgumentsSize();
181
182 WriteBarrier<JSFunction> m_callee;
183 uint32_t m_length; // Always the actual length of captured arguments and never what was stored into the length property.
184 uint32_t m_minCapacity; // The max of this and length determines the capacity of this object. It may be the actual capacity, or maybe something smaller. We arrange it this way to be kind to the JITs.
185 using MappedArguments = CagedBarrierPtr<Gigacage::Primitive, bool>;
186 MappedArguments m_mappedArguments; // If non-null, it means that length, callee, and caller are fully materialized properties.
187};
188
189} // namespace JSC
190