/src/hermes/lib/VM/JSLib/ArrayBuffer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | //===----------------------------------------------------------------------===// |
9 | | /// \file |
10 | | /// ES6 24.1 ArrayBuffer |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "JSLibInternal.h" |
14 | | |
15 | | #include "hermes/VM/JSArrayBuffer.h" |
16 | | #include "hermes/VM/JSDataView.h" |
17 | | #include "hermes/VM/JSTypedArray.h" |
18 | | #include "hermes/VM/Operations.h" |
19 | | #include "hermes/VM/StringPrimitive.h" |
20 | | #pragma GCC diagnostic push |
21 | | |
22 | | #ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32 |
23 | | #pragma GCC diagnostic ignored "-Wshorten-64-to-32" |
24 | | #endif |
25 | | namespace hermes { |
26 | | namespace vm { |
27 | | |
28 | | using std::max; |
29 | | using std::min; |
30 | | |
31 | | /// @name Implementation |
32 | | /// @{ |
33 | | |
34 | 47 | Handle<JSObject> createArrayBufferConstructor(Runtime &runtime) { |
35 | 47 | auto arrayBufferPrototype = |
36 | 47 | Handle<JSObject>::vmcast(&runtime.arrayBufferPrototype); |
37 | 47 | auto cons = defineSystemConstructor<JSArrayBuffer>( |
38 | 47 | runtime, |
39 | 47 | Predefined::getSymbolID(Predefined::ArrayBuffer), |
40 | 47 | arrayBufferConstructor, |
41 | 47 | arrayBufferPrototype, |
42 | 47 | 1, |
43 | 47 | CellKind::JSArrayBufferKind); |
44 | | |
45 | | // ArrayBuffer.prototype.xxx() methods. |
46 | 47 | defineAccessor( |
47 | 47 | runtime, |
48 | 47 | arrayBufferPrototype, |
49 | 47 | Predefined::getSymbolID(Predefined::byteLength), |
50 | 47 | nullptr, |
51 | 47 | arrayBufferPrototypeByteLength, |
52 | 47 | nullptr, |
53 | 47 | false, |
54 | 47 | true); |
55 | 47 | defineMethod( |
56 | 47 | runtime, |
57 | 47 | arrayBufferPrototype, |
58 | 47 | Predefined::getSymbolID(Predefined::slice), |
59 | 47 | nullptr, |
60 | 47 | arrayBufferPrototypeSlice, |
61 | 47 | 2); |
62 | | |
63 | 47 | auto dpf = DefinePropertyFlags::getDefaultNewPropertyFlags(); |
64 | 47 | dpf.writable = 0; |
65 | 47 | dpf.enumerable = 0; |
66 | 47 | defineProperty( |
67 | 47 | runtime, |
68 | 47 | arrayBufferPrototype, |
69 | 47 | Predefined::getSymbolID(Predefined::SymbolToStringTag), |
70 | 47 | runtime.getPredefinedStringHandle(Predefined::ArrayBuffer), |
71 | 47 | dpf); |
72 | | |
73 | | // ArrayBuffer.xxx() methods. |
74 | 47 | defineMethod( |
75 | 47 | runtime, |
76 | 47 | cons, |
77 | 47 | Predefined::getSymbolID(Predefined::isView), |
78 | 47 | nullptr, |
79 | 47 | arrayBufferIsView, |
80 | 47 | 1); |
81 | | |
82 | 47 | return cons; |
83 | 47 | } |
84 | | |
85 | | CallResult<HermesValue> |
86 | 0 | arrayBufferConstructor(void *, Runtime &runtime, NativeArgs args) { |
87 | | // 1. If NewTarget is undefined, throw a TypeError exception. |
88 | 0 | if (!args.isConstructorCall()) { |
89 | 0 | return runtime.raiseTypeError( |
90 | 0 | "ArrayBuffer() called in function context instead of constructor"); |
91 | 0 | } |
92 | 0 | auto self = args.vmcastThis<JSArrayBuffer>(); |
93 | 0 | auto length = args.getArgHandle(0); |
94 | | |
95 | | // 2. Let byteLength be ToIndex(length). |
96 | 0 | auto res = toIndex(runtime, length); |
97 | 0 | if (res == ExecutionStatus::EXCEPTION) { |
98 | 0 | return ExecutionStatus::EXCEPTION; |
99 | 0 | } |
100 | 0 | uint64_t byteLength = res->getNumberAs<uint64_t>(); |
101 | | |
102 | | // 3. Return AllocateArrayBuffer(NewTarget, byteLength). |
103 | | // This object should not have been initialized yet, ensure this is true |
104 | 0 | assert( |
105 | 0 | !self->attached() && |
106 | 0 | "A new array buffer should not have an existing buffer"); |
107 | 0 | if (byteLength > std::numeric_limits<JSArrayBuffer::size_type>::max()) { |
108 | | // On a non-64-bit platform and requested a buffer size greater than |
109 | | // this platform's size type can hold |
110 | 0 | return runtime.raiseRangeError("Too large of a byteLength requested"); |
111 | 0 | } |
112 | 0 | if (JSArrayBuffer::createDataBlock(runtime, self, byteLength) == |
113 | 0 | ExecutionStatus::EXCEPTION) { |
114 | 0 | return ExecutionStatus::EXCEPTION; |
115 | 0 | } |
116 | 0 | return self.getHermesValue(); |
117 | 0 | } |
118 | | |
119 | | CallResult<HermesValue> |
120 | 0 | arrayBufferIsView(void *, Runtime &runtime, NativeArgs args) { |
121 | | // 1. If Type(arg) is not Object, return false. |
122 | | // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true. |
123 | | // 3. Return false. |
124 | 0 | return HermesValue::encodeBoolValue( |
125 | 0 | vmisa<JSTypedArrayBase>(args.getArg(0)) || |
126 | 0 | vmisa<JSDataView>(args.getArg(0))); |
127 | 0 | } |
128 | | |
129 | | CallResult<HermesValue> |
130 | 0 | arrayBufferPrototypeByteLength(void *, Runtime &runtime, NativeArgs args) { |
131 | 0 | auto self = args.dyncastThis<JSArrayBuffer>(); |
132 | 0 | if (!self) { |
133 | 0 | return runtime.raiseTypeError( |
134 | 0 | "byteLength called on a non ArrayBuffer object"); |
135 | 0 | } |
136 | 0 | return HermesValue::encodeUntrustedNumberValue(self->size()); |
137 | 0 | } |
138 | | |
139 | | CallResult<HermesValue> |
140 | 0 | arrayBufferPrototypeSlice(void *, Runtime &runtime, NativeArgs args) { |
141 | 0 | auto start = args.getArgHandle(0); |
142 | 0 | auto end = args.getArgHandle(1); |
143 | | // 1. Let O be the this value. |
144 | 0 | auto self = args.dyncastThis<JSArrayBuffer>(); |
145 | | // 2. If Type(O) is not Object, throw a TypeError exception. |
146 | | // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a |
147 | | // TypeError exception. 4. If IsDetachedBuffer(O) is true, throw a TypeError |
148 | | // exception. |
149 | 0 | if (!self) { |
150 | 0 | return runtime.raiseTypeError( |
151 | 0 | "Called ArrayBuffer.prototype.slice on a non-ArrayBuffer"); |
152 | 0 | } |
153 | | |
154 | | // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot. |
155 | 0 | double len = self->size(); |
156 | | // 6. Let relativeStart be ToIntegerOrInfinity(start). |
157 | 0 | auto intRes = toIntegerOrInfinity(runtime, start); |
158 | 0 | if (intRes == ExecutionStatus::EXCEPTION) { |
159 | | // 7. ReturnIfAbrupt(relativeStart). |
160 | 0 | return ExecutionStatus::EXCEPTION; |
161 | 0 | } |
162 | 0 | double relativeStart = intRes->getNumber(); |
163 | | // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else |
164 | | // let first be min(relativeStart, len). |
165 | 0 | double first = relativeStart < 0 ? max(len + relativeStart, 0.0) |
166 | 0 | : min(relativeStart, len); |
167 | | // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be |
168 | | // ToIntegerOrInfinity(end). |
169 | 0 | double relativeEnd; |
170 | 0 | if (end->isUndefined()) { |
171 | 0 | relativeEnd = len; |
172 | 0 | } else { |
173 | 0 | intRes = toIntegerOrInfinity(runtime, end); |
174 | 0 | if (intRes == ExecutionStatus::EXCEPTION) { |
175 | | // 10. ReturnIfAbrupt(relativeEnd). |
176 | 0 | return ExecutionStatus::EXCEPTION; |
177 | 0 | } |
178 | 0 | relativeEnd = intRes->getNumber(); |
179 | 0 | } |
180 | | // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let |
181 | | // final be min(relativeEnd, len). |
182 | | // NOTE: final is a keyword |
183 | 0 | double finale = |
184 | 0 | relativeEnd < 0 ? max(len + relativeEnd, 0.0) : min(relativeEnd, len); |
185 | | // 12. Let newLen be max(final-first,0). |
186 | 0 | double newLen = max(finale - first, 0.0); |
187 | | // Reduce the doubles to integers for addition and creation |
188 | 0 | JSArrayBuffer::size_type first_int = first; |
189 | 0 | JSArrayBuffer::size_type newLen_int = newLen; |
190 | | // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%). |
191 | | // 14. ReturnIfAbrupt(ctor). |
192 | | // 15. Let new be Construct(ctor, «newLen»). |
193 | | // 16. ReturnIfAbrupt(new). |
194 | |
|
195 | 0 | auto newBuf = runtime.makeHandle(JSArrayBuffer::create( |
196 | 0 | runtime, Handle<JSObject>::vmcast(&runtime.arrayBufferPrototype))); |
197 | |
|
198 | 0 | if (JSArrayBuffer::createDataBlock(runtime, newBuf, newLen_int) == |
199 | 0 | ExecutionStatus::EXCEPTION) { |
200 | 0 | return ExecutionStatus::EXCEPTION; |
201 | 0 | } |
202 | | // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a |
203 | | // TypeError exception. |
204 | | // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception. |
205 | | // 19. If SameValue(new, O) is true, throw a TypeError exception. |
206 | | // 20. If the value of new’s [[ArrayBufferByteLength]] internal |
207 | | // slot < newLen, throw a TypeError exception. |
208 | | // 21. NOTE: Side-effects of the above steps may have detached O. |
209 | | // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception. |
210 | 0 | if (!self->attached() || !newBuf->attached()) { |
211 | 0 | return runtime.raiseTypeError("Cannot split with detached ArrayBuffers"); |
212 | 0 | } |
213 | | |
214 | | // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot. |
215 | | // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot. |
216 | | // 25. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen). |
217 | 0 | JSArrayBuffer::copyDataBlockBytes( |
218 | 0 | runtime, *newBuf, 0, *self, first_int, newLen_int); |
219 | | // 26. Return new. |
220 | 0 | return newBuf.getHermesValue(); |
221 | 0 | } |
222 | | /// @} |
223 | | } // namespace vm |
224 | | } // namespace hermes |