1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/bootstrap_natives.h"
6
7#include "include/dart_api.h"
8
9#include "vm/exceptions.h"
10#include "vm/native_entry.h"
11#include "vm/object.h"
12#include "vm/object_store.h"
13
14namespace dart {
15
16// TypedData.
17
18// Checks to see if offsetInBytes + num_bytes is in the range.
19static void RangeCheck(intptr_t offset_in_bytes,
20 intptr_t access_size,
21 intptr_t length_in_bytes,
22 intptr_t element_size_in_bytes) {
23 if (!Utils::RangeCheck(offset_in_bytes, access_size, length_in_bytes)) {
24 const intptr_t index =
25 (offset_in_bytes + access_size) / element_size_in_bytes;
26 const intptr_t length = length_in_bytes / element_size_in_bytes;
27 Exceptions::ThrowRangeError("index", Integer::Handle(Integer::New(index)),
28 0, length);
29 }
30}
31
32static void AlignmentCheck(intptr_t offset_in_bytes, intptr_t element_size) {
33 if ((offset_in_bytes % element_size) != 0) {
34 const auto& error = String::Handle(String::NewFormatted(
35 "Offset in bytes (%" Pd ") must be a multiple of %" Pd "",
36 offset_in_bytes, element_size));
37 Exceptions::ThrowArgumentError(error);
38 }
39}
40
41// Checks to see if a length will not result in an OOM error.
42static void LengthCheck(intptr_t len, intptr_t max) {
43 if (len < 0 || len > max) {
44 const String& error = String::Handle(String::NewFormatted(
45 "Length (%" Pd ") of object must be in range [0..%" Pd "]", len, max));
46 Exceptions::ThrowArgumentError(error);
47 }
48}
49
50DEFINE_NATIVE_ENTRY(TypedData_length, 0, 1) {
51 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0));
52 if (instance.IsTypedData()) {
53 const TypedData& array = TypedData::Cast(instance);
54 return Smi::New(array.Length());
55 }
56 if (instance.IsExternalTypedData()) {
57 const ExternalTypedData& array = ExternalTypedData::Cast(instance);
58 return Smi::New(array.Length());
59 }
60 const String& error = String::Handle(String::NewFormatted(
61 "Expected a TypedData object but found %s", instance.ToCString()));
62 Exceptions::ThrowArgumentError(error);
63 return Integer::null();
64}
65
66DEFINE_NATIVE_ENTRY(TypedDataView_offsetInBytes, 0, 1) {
67 // "this" is either a _*ArrayView class or _ByteDataView.
68 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0));
69 ASSERT(instance.IsTypedDataView());
70 return TypedDataView::Cast(instance).offset_in_bytes();
71}
72
73DEFINE_NATIVE_ENTRY(TypedDataView_length, 0, 1) {
74 // "this" is either a _*ArrayView class or _ByteDataView.
75 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0));
76 ASSERT(instance.IsTypedDataView());
77 return TypedDataView::Cast(instance).length();
78}
79
80DEFINE_NATIVE_ENTRY(TypedDataView_typedData, 0, 1) {
81 // "this" is either a _*ArrayView class or _ByteDataView.
82 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0));
83 ASSERT(instance.IsTypedDataView());
84 return TypedDataView::Cast(instance).typed_data();
85}
86
87template <typename DstType, typename SrcType>
88static BoolPtr CopyData(const Instance& dst,
89 const Instance& src,
90 const Smi& dst_start,
91 const Smi& src_start,
92 const Smi& length,
93 bool clamped) {
94 const DstType& dst_array = DstType::Cast(dst);
95 const SrcType& src_array = SrcType::Cast(src);
96 const intptr_t dst_offset_in_bytes = dst_start.Value();
97 const intptr_t src_offset_in_bytes = src_start.Value();
98 const intptr_t length_in_bytes = length.Value();
99 ASSERT(Utils::RangeCheck(src_offset_in_bytes, length_in_bytes,
100 src_array.LengthInBytes()));
101 ASSERT(Utils::RangeCheck(dst_offset_in_bytes, length_in_bytes,
102 dst_array.LengthInBytes()));
103 if (clamped) {
104 TypedData::ClampedCopy<DstType, SrcType>(dst_array, dst_offset_in_bytes,
105 src_array, src_offset_in_bytes,
106 length_in_bytes);
107 } else {
108 TypedData::Copy<DstType, SrcType>(dst_array, dst_offset_in_bytes, src_array,
109 src_offset_in_bytes, length_in_bytes);
110 }
111 return Bool::True().raw();
112}
113
114static bool IsClamped(intptr_t cid) {
115 switch (cid) {
116 case kTypedDataUint8ClampedArrayCid:
117 case kExternalTypedDataUint8ClampedArrayCid:
118 case kTypedDataUint8ClampedArrayViewCid:
119 return true;
120 default:
121 return false;
122 }
123}
124
125static bool IsUint8(intptr_t cid) {
126 switch (cid) {
127 case kTypedDataUint8ClampedArrayCid:
128 case kExternalTypedDataUint8ClampedArrayCid:
129 case kTypedDataUint8ClampedArrayViewCid:
130 case kTypedDataUint8ArrayCid:
131 case kExternalTypedDataUint8ArrayCid:
132 case kTypedDataUint8ArrayViewCid:
133 return true;
134 default:
135 return false;
136 }
137}
138
139DEFINE_NATIVE_ENTRY(TypedData_setRange, 0, 7) {
140 const Instance& dst =
141 Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
142 const Smi& dst_start = Smi::CheckedHandle(zone, arguments->NativeArgAt(1));
143 const Smi& length = Smi::CheckedHandle(zone, arguments->NativeArgAt(2));
144 const Instance& src =
145 Instance::CheckedHandle(zone, arguments->NativeArgAt(3));
146 const Smi& src_start = Smi::CheckedHandle(zone, arguments->NativeArgAt(4));
147 const Smi& to_cid_smi = Smi::CheckedHandle(zone, arguments->NativeArgAt(5));
148 const Smi& from_cid_smi = Smi::CheckedHandle(zone, arguments->NativeArgAt(6));
149
150 if (length.Value() < 0) {
151 const String& error = String::Handle(String::NewFormatted(
152 "length (%" Pd ") must be non-negative", length.Value()));
153 Exceptions::ThrowArgumentError(error);
154 }
155 const intptr_t to_cid = to_cid_smi.Value();
156 const intptr_t from_cid = from_cid_smi.Value();
157
158 const bool needs_clamping = IsClamped(to_cid) && !IsUint8(from_cid);
159 if (dst.IsTypedData()) {
160 if (src.IsTypedData()) {
161 return CopyData<TypedData, TypedData>(dst, src, dst_start, src_start,
162 length, needs_clamping);
163 } else if (src.IsExternalTypedData()) {
164 return CopyData<TypedData, ExternalTypedData>(
165 dst, src, dst_start, src_start, length, needs_clamping);
166 }
167 } else if (dst.IsExternalTypedData()) {
168 if (src.IsTypedData()) {
169 return CopyData<ExternalTypedData, TypedData>(
170 dst, src, dst_start, src_start, length, needs_clamping);
171 } else if (src.IsExternalTypedData()) {
172 return CopyData<ExternalTypedData, ExternalTypedData>(
173 dst, src, dst_start, src_start, length, needs_clamping);
174 }
175 }
176 UNREACHABLE();
177 return Bool::False().raw();
178}
179
180// We check the length parameter against a possible maximum length for the
181// array based on available physical addressable memory on the system.
182//
183// More specifically
184//
185// TypedData::MaxElements(cid) is equal to (kSmiMax / ElementSizeInBytes(cid))
186//
187// which ensures that the number of bytes the array holds is guaranteed to fit
188// into a _Smi.
189//
190// Argument 0 is type arguments and is ignored.
191#define TYPED_DATA_NEW(name) \
192 DEFINE_NATIVE_ENTRY(TypedData_##name##_new, 0, 2) { \
193 GET_NON_NULL_NATIVE_ARGUMENT(Integer, length, arguments->NativeArgAt(1)); \
194 const intptr_t cid = kTypedData##name##Cid; \
195 const intptr_t max = TypedData::MaxElements(cid); \
196 const int64_t len = length.AsInt64Value(); \
197 if (len < 0) { \
198 Exceptions::ThrowRangeError("length", length, 0, max); \
199 } else if (len > max) { \
200 const Instance& exception = Instance::Handle( \
201 zone, thread->isolate()->object_store()->out_of_memory()); \
202 Exceptions::Throw(thread, exception); \
203 } \
204 return TypedData::New(cid, static_cast<intptr_t>(len)); \
205 }
206
207#define TYPED_DATA_NEW_NATIVE(name) TYPED_DATA_NEW(name)
208
209CLASS_LIST_TYPED_DATA(TYPED_DATA_NEW_NATIVE)
210#undef TYPED_DATA_NEW_NATIVE
211#undef TYPED_DATA_NEW
212
213#define TYPED_DATA_VIEW_NEW(native_name, cid) \
214 DEFINE_NATIVE_ENTRY(native_name, 0, 4) { \
215 GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, typed_data, \
216 arguments->NativeArgAt(1)); \
217 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offset, arguments->NativeArgAt(2)); \
218 GET_NON_NULL_NATIVE_ARGUMENT(Smi, len, arguments->NativeArgAt(3)); \
219 const intptr_t backing_length = typed_data.LengthInBytes(); \
220 const intptr_t offset_in_bytes = offset.Value(); \
221 const intptr_t length = len.Value(); \
222 const intptr_t element_size = TypedDataBase::ElementSizeInBytes(cid); \
223 AlignmentCheck(offset_in_bytes, element_size); \
224 LengthCheck(offset_in_bytes + length * element_size, backing_length); \
225 return TypedDataView::New(cid, typed_data, offset_in_bytes, length); \
226 }
227
228#define TYPED_DATA_NEW_NATIVE(name) \
229 TYPED_DATA_VIEW_NEW(TypedDataView_##name##View_new, kTypedData##name##ViewCid)
230
231CLASS_LIST_TYPED_DATA(TYPED_DATA_NEW_NATIVE)
232TYPED_DATA_VIEW_NEW(TypedDataView_ByteDataView_new, kByteDataViewCid)
233#undef TYPED_DATA_NEW_NATIVE
234#undef TYPED_DATA_VIEW_NEW
235
236#define TYPED_DATA_GETTER(getter, object, ctor, access_size) \
237 DEFINE_NATIVE_ENTRY(TypedData_##getter, 0, 2) { \
238 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, \
239 arguments->NativeArgAt(0)); \
240 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, \
241 arguments->NativeArgAt(1)); \
242 if (instance.IsTypedData()) { \
243 const TypedData& array = TypedData::Cast(instance); \
244 RangeCheck(offsetInBytes.Value(), access_size, array.LengthInBytes(), \
245 access_size); \
246 return object::ctor(array.getter(offsetInBytes.Value())); \
247 } \
248 if (instance.IsExternalTypedData()) { \
249 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \
250 RangeCheck(offsetInBytes.Value(), access_size, array.LengthInBytes(), \
251 access_size); \
252 return object::ctor(array.getter(offsetInBytes.Value())); \
253 } \
254 const String& error = String::Handle(String::NewFormatted( \
255 "Expected a TypedData object but found %s", instance.ToCString())); \
256 Exceptions::ThrowArgumentError(error); \
257 return object::null(); \
258 }
259
260#define TYPED_DATA_SETTER(setter, object, get_object_value, access_size, \
261 access_type) \
262 DEFINE_NATIVE_ENTRY(TypedData_##setter, 0, 3) { \
263 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, \
264 arguments->NativeArgAt(0)); \
265 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, \
266 arguments->NativeArgAt(1)); \
267 GET_NON_NULL_NATIVE_ARGUMENT(object, value, arguments->NativeArgAt(2)); \
268 if (instance.IsTypedData()) { \
269 const TypedData& array = TypedData::Cast(instance); \
270 RangeCheck(offsetInBytes.Value(), access_size, array.LengthInBytes(), \
271 access_size); \
272 array.setter(offsetInBytes.Value(), \
273 static_cast<access_type>(value.get_object_value())); \
274 } else if (instance.IsExternalTypedData()) { \
275 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \
276 RangeCheck(offsetInBytes.Value(), access_size, array.LengthInBytes(), \
277 access_size); \
278 array.setter(offsetInBytes.Value(), \
279 static_cast<access_type>(value.get_object_value())); \
280 } else { \
281 const String& error = String::Handle(String::NewFormatted( \
282 "Expected a TypedData object but found %s", instance.ToCString())); \
283 Exceptions::ThrowArgumentError(error); \
284 } \
285 return Object::null(); \
286 }
287
288#define TYPED_DATA_NATIVES(type_name, object, ctor, get_object_value, \
289 access_size, access_type) \
290 TYPED_DATA_GETTER(Get##type_name, object, ctor, access_size) \
291 TYPED_DATA_SETTER(Set##type_name, object, get_object_value, access_size, \
292 access_type)
293
294TYPED_DATA_NATIVES(Int8, Integer, New, AsTruncatedUint32Value, 1, int8_t)
295TYPED_DATA_NATIVES(Uint8, Integer, New, AsTruncatedUint32Value, 1, uint8_t)
296TYPED_DATA_NATIVES(Int16, Integer, New, AsTruncatedUint32Value, 2, int16_t)
297TYPED_DATA_NATIVES(Uint16, Integer, New, AsTruncatedUint32Value, 2, uint16_t)
298TYPED_DATA_NATIVES(Int32, Integer, New, AsTruncatedUint32Value, 4, int32_t)
299TYPED_DATA_NATIVES(Uint32, Integer, New, AsTruncatedUint32Value, 4, uint32_t)
300TYPED_DATA_NATIVES(Int64, Integer, New, AsTruncatedInt64Value, 8, int64_t)
301TYPED_DATA_NATIVES(Uint64,
302 Integer,
303 NewFromUint64,
304 AsTruncatedInt64Value,
305 8,
306 uint64_t)
307TYPED_DATA_NATIVES(Float32, Double, New, value, 4, float)
308TYPED_DATA_NATIVES(Float64, Double, New, value, 8, double)
309TYPED_DATA_NATIVES(Float32x4, Float32x4, New, value, 16, simd128_value_t)
310TYPED_DATA_NATIVES(Int32x4, Int32x4, New, value, 16, simd128_value_t)
311TYPED_DATA_NATIVES(Float64x2, Float64x2, New, value, 16, simd128_value_t)
312
313} // namespace dart
314