1/*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003-2017 Apple Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 */
21
22#pragma once
23
24#include "CallFrame.h"
25#include <wtf/CheckedArithmetic.h>
26#include <wtf/ForbidHeapAllocation.h>
27#include <wtf/HashSet.h>
28
29namespace JSC {
30
31class MarkedArgumentBuffer : public RecordOverflow {
32 WTF_MAKE_NONCOPYABLE(MarkedArgumentBuffer);
33 WTF_FORBID_HEAP_ALLOCATION;
34 friend class VM;
35 friend class ArgList;
36
37public:
38 using Base = RecordOverflow;
39 static const size_t inlineCapacity = 8;
40 typedef HashSet<MarkedArgumentBuffer*> ListSet;
41
42 // Constructor for a read-write list, to which you may append values.
43 // FIXME: Remove all clients of this API, then remove this API.
44 MarkedArgumentBuffer()
45 : m_size(0)
46 , m_capacity(inlineCapacity)
47 , m_buffer(m_inlineBuffer)
48 , m_markSet(0)
49 {
50 }
51
52 ~MarkedArgumentBuffer()
53 {
54 ASSERT(!m_needsOverflowCheck);
55 if (m_markSet)
56 m_markSet->remove(this);
57
58 if (EncodedJSValue* base = mallocBase())
59 Gigacage::free(Gigacage::JSValue, base);
60 }
61
62 size_t size() const { return m_size; }
63 bool isEmpty() const { return !m_size; }
64
65 JSValue at(int i) const
66 {
67 if (i >= m_size)
68 return jsUndefined();
69
70 return JSValue::decode(slotFor(i));
71 }
72
73 void clear()
74 {
75 ASSERT(!m_needsOverflowCheck);
76 clearOverflow();
77 m_size = 0;
78 }
79
80 enum OverflowCheckAction {
81 CrashOnOverflow,
82 WillCheckLater
83 };
84 template<OverflowCheckAction action>
85 void appendWithAction(JSValue v)
86 {
87 ASSERT(m_size <= m_capacity);
88 if (m_size == m_capacity || mallocBase()) {
89 slowAppend(v);
90 if (action == CrashOnOverflow)
91 RELEASE_ASSERT(!hasOverflowed());
92 return;
93 }
94
95 slotFor(m_size) = JSValue::encode(v);
96 ++m_size;
97 }
98 void append(JSValue v) { appendWithAction<WillCheckLater>(v); }
99 void appendWithCrashOnOverflow(JSValue v) { appendWithAction<CrashOnOverflow>(v); }
100
101 void removeLast()
102 {
103 ASSERT(m_size);
104 m_size--;
105 }
106
107 JSValue last()
108 {
109 ASSERT(m_size);
110 return JSValue::decode(slotFor(m_size - 1));
111 }
112
113 JSValue takeLast()
114 {
115 JSValue result = last();
116 removeLast();
117 return result;
118 }
119
120 static void markLists(SlotVisitor&, ListSet&);
121
122 void ensureCapacity(size_t requestedCapacity)
123 {
124 if (requestedCapacity > static_cast<size_t>(m_capacity))
125 slowEnsureCapacity(requestedCapacity);
126 }
127
128 bool hasOverflowed()
129 {
130 clearNeedsOverflowCheck();
131 return Base::hasOverflowed();
132 }
133
134 void overflowCheckNotNeeded() { clearNeedsOverflowCheck(); }
135
136private:
137 void expandCapacity();
138 void expandCapacity(int newCapacity);
139 void slowEnsureCapacity(size_t requestedCapacity);
140
141 void addMarkSet(JSValue);
142
143 JS_EXPORT_PRIVATE void slowAppend(JSValue);
144
145 EncodedJSValue& slotFor(int item) const
146 {
147 return m_buffer[item];
148 }
149
150 EncodedJSValue* mallocBase()
151 {
152 if (m_buffer == m_inlineBuffer)
153 return 0;
154 return &slotFor(0);
155 }
156
157#if ASSERT_DISABLED
158 void setNeedsOverflowCheck() { }
159 void clearNeedsOverflowCheck() { }
160#else
161 void setNeedsOverflowCheck() { m_needsOverflowCheck = true; }
162 void clearNeedsOverflowCheck() { m_needsOverflowCheck = false; }
163
164 bool m_needsOverflowCheck { false };
165#endif
166 int m_size;
167 int m_capacity;
168 EncodedJSValue m_inlineBuffer[inlineCapacity];
169 EncodedJSValue* m_buffer;
170 ListSet* m_markSet;
171};
172
173class ArgList {
174 WTF_MAKE_FAST_ALLOCATED;
175 friend class Interpreter;
176 friend class JIT;
177public:
178 ArgList()
179 : m_args(0)
180 , m_argCount(0)
181 {
182 }
183
184 ArgList(ExecState* exec)
185 : m_args(reinterpret_cast<JSValue*>(&exec[CallFrame::argumentOffset(0)]))
186 , m_argCount(exec->argumentCount())
187 {
188 }
189
190 ArgList(const MarkedArgumentBuffer& args)
191 : m_args(reinterpret_cast<JSValue*>(args.m_buffer))
192 , m_argCount(args.size())
193 {
194 }
195
196 JSValue at(int i) const
197 {
198 if (i >= m_argCount)
199 return jsUndefined();
200 return m_args[i];
201 }
202
203 bool isEmpty() const { return !m_argCount; }
204 size_t size() const { return m_argCount; }
205
206 JS_EXPORT_PRIVATE void getSlice(int startIndex, ArgList& result) const;
207
208private:
209 JSValue* data() const { return m_args; }
210
211 JSValue* m_args;
212 int m_argCount;
213};
214
215} // namespace JSC
216