1/*
2 * Copyright (C) 2009-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 "ArrayBufferSharingMode.h"
29#include "GCIncomingRefCounted.h"
30#include "Weak.h"
31#include <wtf/CagedPtr.h>
32#include <wtf/CheckedArithmetic.h>
33#include <wtf/Function.h>
34#include <wtf/StdLibExtras.h>
35#include <wtf/ThreadSafeRefCounted.h>
36#include <wtf/text/WTFString.h>
37
38namespace JSC {
39
40#define MAX_ARRAY_BUFFER_SIZE 0x7fffffffu
41
42class VM;
43class ArrayBuffer;
44class ArrayBufferView;
45class JSArrayBuffer;
46
47typedef Function<void(void*)> ArrayBufferDestructorFunction;
48
49class SharedArrayBufferContents : public ThreadSafeRefCounted<SharedArrayBufferContents> {
50public:
51 SharedArrayBufferContents(void* data, unsigned size, ArrayBufferDestructorFunction&&);
52 ~SharedArrayBufferContents();
53
54 void* data() const { return m_data.getMayBeNull(m_sizeInBytes); }
55
56private:
57 using DataType = CagedPtr<Gigacage::Primitive, void, tagCagedPtr>;
58 DataType m_data;
59 ArrayBufferDestructorFunction m_destructor;
60 unsigned m_sizeInBytes;
61};
62
63class ArrayBufferContents {
64 WTF_MAKE_NONCOPYABLE(ArrayBufferContents);
65public:
66 JS_EXPORT_PRIVATE ArrayBufferContents();
67 JS_EXPORT_PRIVATE ArrayBufferContents(void* data, unsigned sizeInBytes, ArrayBufferDestructorFunction&&);
68
69 JS_EXPORT_PRIVATE ArrayBufferContents(ArrayBufferContents&&);
70 JS_EXPORT_PRIVATE ArrayBufferContents& operator=(ArrayBufferContents&&);
71
72 JS_EXPORT_PRIVATE ~ArrayBufferContents();
73
74 JS_EXPORT_PRIVATE void clear();
75
76 explicit operator bool() { return !!m_data; }
77
78 void* data() const { return m_data.getMayBeNull(sizeInBytes()); }
79 unsigned sizeInBytes() const { return m_sizeInBytes; }
80
81 bool isShared() const { return m_shared; }
82
83private:
84 void destroy();
85 void reset();
86
87 friend class ArrayBuffer;
88
89 enum InitializationPolicy {
90 ZeroInitialize,
91 DontInitialize
92 };
93
94 void tryAllocate(unsigned numElements, unsigned elementByteSize, InitializationPolicy);
95
96 void makeShared();
97 void transferTo(ArrayBufferContents&);
98 void copyTo(ArrayBufferContents&);
99 void shareWith(ArrayBufferContents&);
100
101 ArrayBufferDestructorFunction m_destructor;
102 RefPtr<SharedArrayBufferContents> m_shared;
103 using DataType = CagedPtr<Gigacage::Primitive, void, tagCagedPtr>;
104 DataType m_data;
105 unsigned m_sizeInBytes;
106};
107
108class ArrayBuffer : public GCIncomingRefCounted<ArrayBuffer> {
109public:
110 JS_EXPORT_PRIVATE static Ref<ArrayBuffer> create(unsigned numElements, unsigned elementByteSize);
111 JS_EXPORT_PRIVATE static Ref<ArrayBuffer> create(ArrayBuffer&);
112 JS_EXPORT_PRIVATE static Ref<ArrayBuffer> create(const void* source, unsigned byteLength);
113 JS_EXPORT_PRIVATE static Ref<ArrayBuffer> create(ArrayBufferContents&&);
114 JS_EXPORT_PRIVATE static Ref<ArrayBuffer> createAdopted(const void* data, unsigned byteLength);
115 JS_EXPORT_PRIVATE static Ref<ArrayBuffer> createFromBytes(const void* data, unsigned byteLength, ArrayBufferDestructorFunction&&);
116 JS_EXPORT_PRIVATE static RefPtr<ArrayBuffer> tryCreate(unsigned numElements, unsigned elementByteSize);
117 JS_EXPORT_PRIVATE static RefPtr<ArrayBuffer> tryCreate(ArrayBuffer&);
118 JS_EXPORT_PRIVATE static RefPtr<ArrayBuffer> tryCreate(const void* source, unsigned byteLength);
119
120 // Only for use by Uint8ClampedArray::tryCreateUninitialized and SharedBuffer::tryCreateArrayBuffer.
121 JS_EXPORT_PRIVATE static Ref<ArrayBuffer> createUninitialized(unsigned numElements, unsigned elementByteSize);
122 JS_EXPORT_PRIVATE static RefPtr<ArrayBuffer> tryCreateUninitialized(unsigned numElements, unsigned elementByteSize);
123
124 inline void* data();
125 inline const void* data() const;
126 inline unsigned byteLength() const;
127
128 void makeShared();
129 void setSharingMode(ArrayBufferSharingMode);
130 inline bool isShared() const;
131 inline ArrayBufferSharingMode sharingMode() const { return isShared() ? ArrayBufferSharingMode::Shared : ArrayBufferSharingMode::Default; }
132
133 inline size_t gcSizeEstimateInBytes() const;
134
135 JS_EXPORT_PRIVATE Ref<ArrayBuffer> slice(double begin, double end) const;
136 JS_EXPORT_PRIVATE Ref<ArrayBuffer> slice(double begin) const;
137
138 inline void pin();
139 inline void unpin();
140 inline void pinAndLock();
141 inline bool isLocked();
142
143 void makeWasmMemory();
144 inline bool isWasmMemory();
145
146 JS_EXPORT_PRIVATE bool transferTo(VM&, ArrayBufferContents&);
147 JS_EXPORT_PRIVATE bool shareWith(ArrayBufferContents&);
148
149 void neuter(VM&);
150 bool isNeutered() { return !m_contents.m_data; }
151
152 static ptrdiff_t offsetOfData() { return OBJECT_OFFSETOF(ArrayBuffer, m_contents) + OBJECT_OFFSETOF(ArrayBufferContents, m_data); }
153
154 ~ArrayBuffer() { }
155
156private:
157 static Ref<ArrayBuffer> create(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy);
158 static Ref<ArrayBuffer> createInternal(ArrayBufferContents&&, const void*, unsigned);
159 static RefPtr<ArrayBuffer> tryCreate(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy);
160 ArrayBuffer(ArrayBufferContents&&);
161 Ref<ArrayBuffer> sliceImpl(unsigned begin, unsigned end) const;
162 inline unsigned clampIndex(double index) const;
163 static inline unsigned clampValue(double x, unsigned left, unsigned right);
164
165 void notifyIncommingReferencesOfTransfer(VM&);
166
167 ArrayBufferContents m_contents;
168 Checked<unsigned> m_pinCount;
169 bool m_isWasmMemory;
170 // m_locked == true means that some API user fetched m_contents directly from a TypedArray object,
171 // the buffer is backed by a WebAssembly.Memory, or is a SharedArrayBuffer.
172 bool m_locked;
173
174public:
175 Weak<JSArrayBuffer> m_wrapper;
176};
177
178void* ArrayBuffer::data()
179{
180 return m_contents.data();
181}
182
183const void* ArrayBuffer::data() const
184{
185 return m_contents.data();
186}
187
188unsigned ArrayBuffer::byteLength() const
189{
190 return m_contents.sizeInBytes();
191}
192
193bool ArrayBuffer::isShared() const
194{
195 return m_contents.isShared();
196}
197
198size_t ArrayBuffer::gcSizeEstimateInBytes() const
199{
200 // FIXME: We probably want to scale this by the shared ref count or something.
201 return sizeof(ArrayBuffer) + static_cast<size_t>(byteLength());
202}
203
204void ArrayBuffer::pin()
205{
206 m_pinCount++;
207}
208
209void ArrayBuffer::unpin()
210{
211 m_pinCount--;
212}
213
214void ArrayBuffer::pinAndLock()
215{
216 m_locked = true;
217}
218
219bool ArrayBuffer::isLocked()
220{
221 return m_locked;
222}
223
224bool ArrayBuffer::isWasmMemory()
225{
226 return m_isWasmMemory;
227}
228
229JS_EXPORT_PRIVATE ASCIILiteral errorMesasgeForTransfer(ArrayBuffer*);
230
231} // namespace JSC
232
233using JSC::ArrayBuffer;
234