1 | // Copyright (c) 2011, 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 | #include "platform/unicode.h" |
9 | #include "vm/dart_api_impl.h" |
10 | #include "vm/exceptions.h" |
11 | #include "vm/isolate.h" |
12 | #include "vm/native_entry.h" |
13 | #include "vm/object.h" |
14 | #include "vm/object_store.h" |
15 | #include "vm/symbols.h" |
16 | |
17 | namespace dart { |
18 | |
19 | DEFINE_NATIVE_ENTRY(String_fromEnvironment, 0, 3) { |
20 | GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(1)); |
21 | GET_NATIVE_ARGUMENT(String, default_value, arguments->NativeArgAt(2)); |
22 | // Call the embedder to supply us with the environment. |
23 | const String& env_value = |
24 | String::Handle(Api::GetEnvironmentValue(thread, name)); |
25 | if (!env_value.IsNull()) { |
26 | return Symbols::New(thread, env_value); |
27 | } |
28 | return default_value.raw(); |
29 | } |
30 | |
31 | DEFINE_NATIVE_ENTRY(StringBase_createFromCodePoints, 0, 3) { |
32 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, list, arguments->NativeArgAt(0)); |
33 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1)); |
34 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2)); |
35 | |
36 | Array& a = Array::Handle(); |
37 | intptr_t length; |
38 | if (list.IsGrowableObjectArray()) { |
39 | const GrowableObjectArray& growableArray = GrowableObjectArray::Cast(list); |
40 | a = growableArray.data(); |
41 | length = growableArray.Length(); |
42 | } else if (list.IsArray()) { |
43 | a = Array::Cast(list).raw(); |
44 | length = a.Length(); |
45 | } else { |
46 | Exceptions::ThrowArgumentError(list); |
47 | return NULL; // Unreachable. |
48 | } |
49 | |
50 | intptr_t start = start_obj.Value(); |
51 | if ((start < 0) || (start > length)) { |
52 | Exceptions::ThrowArgumentError(start_obj); |
53 | } |
54 | |
55 | intptr_t end = end_obj.Value(); |
56 | if ((end < start) || (end > length)) { |
57 | Exceptions::ThrowArgumentError(end_obj); |
58 | } |
59 | |
60 | // Unbox the array and determine the maximum element width. |
61 | bool is_one_byte_string = true; |
62 | intptr_t array_len = end - start; |
63 | intptr_t utf16_len = array_len; |
64 | int32_t* utf32_array = zone->Alloc<int32_t>(array_len); |
65 | Instance& index_object = Instance::Handle(zone); |
66 | for (intptr_t i = 0; i < array_len; i++) { |
67 | index_object ^= a.At(start + i); |
68 | if (!index_object.IsSmi()) { |
69 | Exceptions::ThrowArgumentError(index_object); |
70 | } |
71 | intptr_t value = Smi::Cast(index_object).Value(); |
72 | if (Utf::IsOutOfRange(value)) { |
73 | Exceptions::ThrowByType(Exceptions::kArgument, Object::empty_array()); |
74 | UNREACHABLE(); |
75 | } |
76 | // Now it is safe to cast the value. |
77 | int32_t value32 = static_cast<int32_t>(value); |
78 | if (!Utf::IsLatin1(value32)) { |
79 | is_one_byte_string = false; |
80 | if (Utf::IsSupplementary(value32)) { |
81 | utf16_len += 1; |
82 | } |
83 | } |
84 | utf32_array[i] = value32; |
85 | } |
86 | if (is_one_byte_string) { |
87 | return OneByteString::New(utf32_array, array_len, Heap::kNew); |
88 | } |
89 | return TwoByteString::New(utf16_len, utf32_array, array_len, Heap::kNew); |
90 | } |
91 | |
92 | DEFINE_NATIVE_ENTRY(StringBase_substringUnchecked, 0, 3) { |
93 | const String& receiver = |
94 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
95 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1)); |
96 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2)); |
97 | |
98 | intptr_t start = start_obj.Value(); |
99 | intptr_t end = end_obj.Value(); |
100 | return String::SubString(receiver, start, (end - start)); |
101 | } |
102 | |
103 | // Return the bitwise-or of all characters in the slice from start to end. |
104 | static uint16_t CharacterLimit(const String& string, |
105 | intptr_t start, |
106 | intptr_t end) { |
107 | ASSERT(string.IsTwoByteString() || string.IsExternalTwoByteString()); |
108 | // Maybe do loop unrolling, and handle two uint16_t in a single uint32_t |
109 | // operation. |
110 | NoSafepointScope no_safepoint; |
111 | uint16_t result = 0; |
112 | if (string.IsTwoByteString()) { |
113 | for (intptr_t i = start; i < end; i++) { |
114 | result |= TwoByteString::CharAt(string, i); |
115 | } |
116 | } else { |
117 | for (intptr_t i = start; i < end; i++) { |
118 | result |= ExternalTwoByteString::CharAt(string, i); |
119 | } |
120 | } |
121 | return result; |
122 | } |
123 | |
124 | static const intptr_t kLengthSize = 11; |
125 | static const intptr_t kLengthMask = (1 << kLengthSize) - 1; |
126 | |
127 | static bool CheckSlicesOneByte(const String& base, |
128 | const Array& matches, |
129 | const int len) { |
130 | Instance& object = Instance::Handle(); |
131 | // Check each slice for one-bytedness. |
132 | for (intptr_t i = 0; i < len; i++) { |
133 | object ^= matches.At(i); |
134 | if (object.IsSmi()) { |
135 | intptr_t slice_start = Smi::Cast(object).Value(); |
136 | intptr_t slice_end; |
137 | if (slice_start < 0) { |
138 | intptr_t bits = -slice_start; |
139 | slice_start = bits >> kLengthSize; |
140 | slice_end = slice_start + (bits & kLengthMask); |
141 | } else { |
142 | i++; |
143 | if (i >= len) { |
144 | // Bad format, handled later. |
145 | return false; |
146 | } |
147 | object ^= matches.At(i); |
148 | if (!object.IsSmi()) { |
149 | // Bad format, handled later. |
150 | return false; |
151 | } |
152 | slice_end = Smi::Cast(object).Value(); |
153 | } |
154 | uint16_t char_limit = CharacterLimit(base, slice_start, slice_end); |
155 | if (char_limit > 0xff) { |
156 | return false; |
157 | } |
158 | } |
159 | } |
160 | return true; |
161 | } |
162 | |
163 | DEFINE_NATIVE_ENTRY(StringBase_joinReplaceAllResult, 0, 4) { |
164 | const String& base = String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
165 | GET_NON_NULL_NATIVE_ARGUMENT(GrowableObjectArray, matches_growable, |
166 | arguments->NativeArgAt(1)); |
167 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, length_obj, arguments->NativeArgAt(2)); |
168 | GET_NON_NULL_NATIVE_ARGUMENT(Bool, is_onebyte_obj, arguments->NativeArgAt(3)); |
169 | |
170 | intptr_t len = matches_growable.Length(); |
171 | const Array& matches = Array::Handle(zone, matches_growable.data()); |
172 | |
173 | const intptr_t length = length_obj.Value(); |
174 | if (length < 0) { |
175 | Exceptions::ThrowArgumentError(length_obj); |
176 | } |
177 | |
178 | // Start out assuming result is one-byte if replacements are. |
179 | bool is_onebyte = is_onebyte_obj.value(); |
180 | if (is_onebyte) { |
181 | // If any of the base string slices are not one-byte, the result will be |
182 | // a two-byte string. |
183 | if (!base.IsOneByteString() && !base.IsExternalOneByteString()) { |
184 | is_onebyte = CheckSlicesOneByte(base, matches, len); |
185 | } |
186 | } |
187 | |
188 | const intptr_t base_length = base.Length(); |
189 | String& result = String::Handle(zone); |
190 | if (is_onebyte) { |
191 | result = OneByteString::New(length, Heap::kNew); |
192 | } else { |
193 | result = TwoByteString::New(length, Heap::kNew); |
194 | } |
195 | Instance& object = Instance::Handle(zone); |
196 | intptr_t write_index = 0; |
197 | for (intptr_t i = 0; i < len; i++) { |
198 | object ^= matches.At(i); |
199 | if (object.IsSmi()) { |
200 | intptr_t slice_start = Smi::Cast(object).Value(); |
201 | intptr_t slice_length = -1; |
202 | // Slices with limited ranges are stored in a single negative Smi. |
203 | if (slice_start < 0) { |
204 | intptr_t bits = -slice_start; |
205 | slice_start = bits >> kLengthSize; |
206 | slice_length = bits & kLengthMask; |
207 | } else { |
208 | i++; |
209 | if (i < len) { // Otherwise slice_length stays at -1. |
210 | object ^= matches.At(i); |
211 | if (object.IsSmi()) { |
212 | intptr_t slice_end = Smi::Cast(object).Value(); |
213 | slice_length = slice_end - slice_start; |
214 | } |
215 | } |
216 | } |
217 | if (slice_length > 0) { |
218 | if (0 <= slice_start && slice_start + slice_length <= base_length && |
219 | write_index + slice_length <= length) { |
220 | String::Copy(result, write_index, base, slice_start, slice_length); |
221 | write_index += slice_length; |
222 | continue; |
223 | } |
224 | } |
225 | // Either the slice_length was zero, |
226 | // or the first smi was positive and not followed by another smi, |
227 | // or the smis were not a valid slice of the base string, |
228 | // or the slice was too large to fit in the result. |
229 | // Something is wrong with the matches array! |
230 | Exceptions::ThrowArgumentError(matches_growable); |
231 | } else if (object.IsString()) { |
232 | const String& replacement = String::Cast(object); |
233 | intptr_t replacement_length = replacement.Length(); |
234 | if (write_index + replacement_length > length) { |
235 | // Invalid input data, either in matches list or the total length. |
236 | Exceptions::ThrowArgumentError(matches_growable); |
237 | } |
238 | String::Copy(result, write_index, replacement, 0, replacement_length); |
239 | write_index += replacement_length; |
240 | } |
241 | } |
242 | if (write_index < length) { |
243 | Exceptions::ThrowArgumentError(matches_growable); |
244 | } |
245 | return result.raw(); |
246 | } |
247 | |
248 | DEFINE_NATIVE_ENTRY(OneByteString_substringUnchecked, 0, 3) { |
249 | const String& receiver = |
250 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
251 | ASSERT(receiver.IsOneByteString()); |
252 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1)); |
253 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2)); |
254 | |
255 | const intptr_t start = start_obj.Value(); |
256 | const intptr_t end = end_obj.Value(); |
257 | return OneByteString::New(receiver, start, end - start, Heap::kNew); |
258 | } |
259 | |
260 | // This is high-performance code. |
261 | DEFINE_NATIVE_ENTRY(OneByteString_splitWithCharCode, 0, 2) { |
262 | const String& receiver = |
263 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
264 | ASSERT(receiver.IsOneByteString()); |
265 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_split_code, arguments->NativeArgAt(1)); |
266 | const intptr_t len = receiver.Length(); |
267 | const intptr_t split_code = smi_split_code.Value(); |
268 | const GrowableObjectArray& result = GrowableObjectArray::Handle( |
269 | zone, GrowableObjectArray::New(16, Heap::kNew)); |
270 | String& str = String::Handle(zone); |
271 | intptr_t start = 0; |
272 | intptr_t i = 0; |
273 | for (; i < len; i++) { |
274 | if (split_code == OneByteString::CharAt(receiver, i)) { |
275 | str = OneByteString::SubStringUnchecked(receiver, start, (i - start), |
276 | Heap::kNew); |
277 | result.Add(str); |
278 | start = i + 1; |
279 | } |
280 | } |
281 | str = OneByteString::SubStringUnchecked(receiver, start, (i - start), |
282 | Heap::kNew); |
283 | result.Add(str); |
284 | result.SetTypeArguments(TypeArguments::Handle( |
285 | zone, isolate->object_store()->type_argument_string())); |
286 | return result.raw(); |
287 | } |
288 | |
289 | DEFINE_NATIVE_ENTRY(Internal_allocateOneByteString, 0, 1) { |
290 | GET_NON_NULL_NATIVE_ARGUMENT(Integer, length_obj, arguments->NativeArgAt(0)); |
291 | const int64_t length = length_obj.AsInt64Value(); |
292 | if ((length < 0) || (length > OneByteString::kMaxElements)) { |
293 | // Assume that negative lengths are the result of wrapping in code in |
294 | // string_patch.dart. |
295 | const Instance& exception = |
296 | Instance::Handle(thread->isolate()->object_store()->out_of_memory()); |
297 | Exceptions::Throw(thread, exception); |
298 | UNREACHABLE(); |
299 | } |
300 | return OneByteString::New(static_cast<intptr_t>(length), Heap::kNew); |
301 | } |
302 | |
303 | DEFINE_NATIVE_ENTRY(Internal_allocateTwoByteString, 0, 1) { |
304 | GET_NON_NULL_NATIVE_ARGUMENT(Integer, length_obj, arguments->NativeArgAt(0)); |
305 | const int64_t length = length_obj.AsInt64Value(); |
306 | if ((length < 0) || (length > TwoByteString::kMaxElements)) { |
307 | // Assume that negative lengths are the result of wrapping in code in |
308 | // string_patch.dart. |
309 | const Instance& exception = |
310 | Instance::Handle(thread->isolate()->object_store()->out_of_memory()); |
311 | Exceptions::Throw(thread, exception); |
312 | UNREACHABLE(); |
313 | } |
314 | return TwoByteString::New(static_cast<intptr_t>(length), Heap::kNew); |
315 | } |
316 | |
317 | DEFINE_NATIVE_ENTRY(OneByteString_allocateFromOneByteList, 0, 3) { |
318 | Instance& list = Instance::CheckedHandle(zone, arguments->NativeArgAt(0)); |
319 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1)); |
320 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2)); |
321 | |
322 | intptr_t start = start_obj.Value(); |
323 | intptr_t end = end_obj.Value(); |
324 | if (start < 0) { |
325 | const Array& args = Array::Handle(Array::New(1)); |
326 | args.SetAt(0, start_obj); |
327 | Exceptions::ThrowByType(Exceptions::kArgument, args); |
328 | } |
329 | intptr_t length = end - start; |
330 | if (length < 0) { |
331 | const Array& args = Array::Handle(Array::New(1)); |
332 | args.SetAt(0, end_obj); |
333 | Exceptions::ThrowByType(Exceptions::kArgument, args); |
334 | } |
335 | ASSERT(length >= 0); |
336 | |
337 | Heap::Space space = Heap::kNew; |
338 | if (list.IsTypedData()) { |
339 | const TypedData& array = TypedData::Cast(list); |
340 | if (end > array.LengthInBytes()) { |
341 | const Array& args = Array::Handle(Array::New(1)); |
342 | args.SetAt(0, end_obj); |
343 | Exceptions::ThrowByType(Exceptions::kArgument, args); |
344 | } |
345 | return OneByteString::New(array, start, length, space); |
346 | } else if (list.IsExternalTypedData()) { |
347 | const ExternalTypedData& array = ExternalTypedData::Cast(list); |
348 | if (end > array.LengthInBytes()) { |
349 | const Array& args = Array::Handle(Array::New(1)); |
350 | args.SetAt(0, end_obj); |
351 | Exceptions::ThrowByType(Exceptions::kArgument, args); |
352 | } |
353 | return OneByteString::New(array, start, length, space); |
354 | } else if (list.IsTypedDataView()) { |
355 | const auto& view = TypedDataView::Cast(list); |
356 | if (end > Smi::Value(view.length())) { |
357 | const Array& args = Array::Handle(Array::New(1)); |
358 | args.SetAt(0, end_obj); |
359 | Exceptions::ThrowByType(Exceptions::kArgument, args); |
360 | } |
361 | const Instance& data_obj = Instance::Handle(view.typed_data()); |
362 | intptr_t data_offset = Smi::Value(view.offset_in_bytes()); |
363 | if (data_obj.IsTypedData()) { |
364 | const TypedData& array = TypedData::Cast(data_obj); |
365 | return OneByteString::New(array, data_offset + start, length, space); |
366 | } else if (data_obj.IsExternalTypedData()) { |
367 | const ExternalTypedData& array = ExternalTypedData::Cast(data_obj); |
368 | return OneByteString::New(array, data_offset + start, length, space); |
369 | } |
370 | } else if (list.IsArray()) { |
371 | const Array& array = Array::Cast(list); |
372 | if (end > array.Length()) { |
373 | const Array& args = Array::Handle(Array::New(1)); |
374 | args.SetAt(0, end_obj); |
375 | Exceptions::ThrowByType(Exceptions::kArgument, args); |
376 | } |
377 | String& string = String::Handle(OneByteString::New(length, space)); |
378 | for (int i = 0; i < length; i++) { |
379 | intptr_t value = Smi::Value(static_cast<SmiPtr>(array.At(start + i))); |
380 | OneByteString::SetCharAt(string, i, value); |
381 | } |
382 | return string.raw(); |
383 | } else if (list.IsGrowableObjectArray()) { |
384 | const GrowableObjectArray& array = GrowableObjectArray::Cast(list); |
385 | if (end > array.Length()) { |
386 | const Array& args = Array::Handle(Array::New(1)); |
387 | args.SetAt(0, end_obj); |
388 | Exceptions::ThrowByType(Exceptions::kArgument, args); |
389 | } |
390 | String& string = String::Handle(OneByteString::New(length, space)); |
391 | for (int i = 0; i < length; i++) { |
392 | intptr_t value = Smi::Value(static_cast<SmiPtr>(array.At(start + i))); |
393 | OneByteString::SetCharAt(string, i, value); |
394 | } |
395 | return string.raw(); |
396 | } |
397 | UNREACHABLE(); |
398 | return Object::null(); |
399 | } |
400 | |
401 | DEFINE_NATIVE_ENTRY(Internal_writeIntoOneByteString, 0, 3) { |
402 | GET_NON_NULL_NATIVE_ARGUMENT(String, receiver, arguments->NativeArgAt(0)); |
403 | ASSERT(receiver.IsOneByteString()); |
404 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, index_obj, arguments->NativeArgAt(1)); |
405 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, code_point_obj, arguments->NativeArgAt(2)); |
406 | ASSERT((0 <= code_point_obj.Value()) && (code_point_obj.Value() <= 0xFF)); |
407 | OneByteString::SetCharAt(receiver, index_obj.Value(), code_point_obj.Value()); |
408 | return Object::null(); |
409 | } |
410 | |
411 | DEFINE_NATIVE_ENTRY(Internal_writeIntoTwoByteString, 0, 3) { |
412 | GET_NON_NULL_NATIVE_ARGUMENT(String, receiver, arguments->NativeArgAt(0)); |
413 | ASSERT(receiver.IsTwoByteString()); |
414 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, index_obj, arguments->NativeArgAt(1)); |
415 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, code_point_obj, arguments->NativeArgAt(2)); |
416 | ASSERT((0 <= code_point_obj.Value()) && (code_point_obj.Value() <= 0xFFFF)); |
417 | TwoByteString::SetCharAt(receiver, index_obj.Value(), code_point_obj.Value()); |
418 | return Object::null(); |
419 | } |
420 | |
421 | DEFINE_NATIVE_ENTRY(TwoByteString_allocateFromTwoByteList, 0, 3) { |
422 | Instance& list = Instance::CheckedHandle(zone, arguments->NativeArgAt(0)); |
423 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1)); |
424 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2)); |
425 | |
426 | intptr_t start = start_obj.Value(); |
427 | intptr_t end = end_obj.Value(); |
428 | if (start < 0) { |
429 | Exceptions::ThrowArgumentError(start_obj); |
430 | } |
431 | intptr_t length = end - start; |
432 | if (length < 0) { |
433 | Exceptions::ThrowArgumentError(end_obj); |
434 | } |
435 | |
436 | Heap::Space space = Heap::kNew; |
437 | if (list.IsTypedData()) { |
438 | const TypedData& array = TypedData::Cast(list); |
439 | if (array.ElementType() != kUint16ArrayElement) { |
440 | Exceptions::ThrowArgumentError(list); |
441 | } |
442 | if (end > array.Length()) { |
443 | Exceptions::ThrowArgumentError(end_obj); |
444 | } |
445 | return TwoByteString::New(array, start * sizeof(uint16_t), length, space); |
446 | } else if (list.IsExternalTypedData()) { |
447 | const ExternalTypedData& array = ExternalTypedData::Cast(list); |
448 | if (array.ElementType() != kUint16ArrayElement) { |
449 | Exceptions::ThrowArgumentError(list); |
450 | } |
451 | if (end > array.Length()) { |
452 | Exceptions::ThrowArgumentError(end_obj); |
453 | } |
454 | return TwoByteString::New(array, start * sizeof(uint16_t), length, space); |
455 | } else if (IsTypedDataViewClassId(list.GetClassId())) { |
456 | const auto& view = TypedDataView::Cast(list); |
457 | const intptr_t cid = list.GetClassId(); |
458 | if (cid != kTypedDataUint16ArrayViewCid) { |
459 | Exceptions::ThrowArgumentError(list); |
460 | } |
461 | if (end > Smi::Value(view.length())) { |
462 | Exceptions::ThrowArgumentError(end_obj); |
463 | } |
464 | const auto& data_obj = Instance::Handle(zone, view.typed_data()); |
465 | const intptr_t data_offset = Smi::Value(view.offset_in_bytes()); |
466 | if (data_obj.IsTypedData()) { |
467 | const TypedData& array = TypedData::Cast(data_obj); |
468 | return TwoByteString::New(array, data_offset + start * sizeof(uint16_t), |
469 | length, space); |
470 | } else if (data_obj.IsExternalTypedData()) { |
471 | const ExternalTypedData& array = ExternalTypedData::Cast(data_obj); |
472 | return TwoByteString::New(array, data_offset + start * sizeof(uint16_t), |
473 | length, space); |
474 | } |
475 | } else if (list.IsArray()) { |
476 | const Array& array = Array::Cast(list); |
477 | if (end > array.Length()) { |
478 | Exceptions::ThrowArgumentError(end_obj); |
479 | } |
480 | const String& string = |
481 | String::Handle(zone, TwoByteString::New(length, space)); |
482 | for (int i = 0; i < length; i++) { |
483 | intptr_t value = Smi::Value(static_cast<SmiPtr>(array.At(start + i))); |
484 | TwoByteString::SetCharAt(string, i, value); |
485 | } |
486 | return string.raw(); |
487 | } else if (list.IsGrowableObjectArray()) { |
488 | const GrowableObjectArray& array = GrowableObjectArray::Cast(list); |
489 | if (end > array.Length()) { |
490 | Exceptions::ThrowArgumentError(end_obj); |
491 | } |
492 | const String& string = |
493 | String::Handle(zone, TwoByteString::New(length, space)); |
494 | for (int i = 0; i < length; i++) { |
495 | intptr_t value = Smi::Value(static_cast<SmiPtr>(array.At(start + i))); |
496 | TwoByteString::SetCharAt(string, i, value); |
497 | } |
498 | return string.raw(); |
499 | } |
500 | UNREACHABLE(); |
501 | return Object::null(); |
502 | } |
503 | |
504 | DEFINE_NATIVE_ENTRY(String_getHashCode, 0, 1) { |
505 | const String& receiver = |
506 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
507 | intptr_t hash_val = receiver.Hash(); |
508 | ASSERT(hash_val > 0); |
509 | ASSERT(Smi::IsValid(hash_val)); |
510 | return Smi::New(hash_val); |
511 | } |
512 | |
513 | DEFINE_NATIVE_ENTRY(String_getLength, 0, 1) { |
514 | const String& receiver = |
515 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
516 | return Smi::New(receiver.Length()); |
517 | } |
518 | |
519 | static uint16_t StringValueAt(const String& str, const Integer& index) { |
520 | if (index.IsSmi()) { |
521 | const intptr_t index_value = Smi::Cast(index).Value(); |
522 | if ((0 <= index_value) && (index_value < str.Length())) { |
523 | return str.CharAt(index_value); |
524 | } |
525 | } |
526 | |
527 | // An index larger than Smi is always illegal. |
528 | Exceptions::ThrowRangeError("index" , index, 0, str.Length() - 1); |
529 | return 0; |
530 | } |
531 | |
532 | DEFINE_NATIVE_ENTRY(String_charAt, 0, 2) { |
533 | const String& receiver = |
534 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
535 | GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1)); |
536 | uint16_t value = StringValueAt(receiver, index); |
537 | return Symbols::FromCharCode(thread, static_cast<int32_t>(value)); |
538 | } |
539 | |
540 | // Returns the 16-bit UTF-16 code unit at the given index. |
541 | DEFINE_NATIVE_ENTRY(String_codeUnitAt, 0, 2) { |
542 | const String& receiver = |
543 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
544 | GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1)); |
545 | uint16_t value = StringValueAt(receiver, index); |
546 | return Smi::New(static_cast<intptr_t>(value)); |
547 | } |
548 | |
549 | DEFINE_NATIVE_ENTRY(String_concat, 0, 2) { |
550 | const String& receiver = |
551 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
552 | GET_NON_NULL_NATIVE_ARGUMENT(String, b, arguments->NativeArgAt(1)); |
553 | return String::Concat(receiver, b); |
554 | } |
555 | |
556 | DEFINE_NATIVE_ENTRY(String_toLowerCase, 0, 1) { |
557 | const String& receiver = |
558 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
559 | ASSERT(!receiver.IsNull()); |
560 | return String::ToLowerCase(receiver); |
561 | } |
562 | |
563 | DEFINE_NATIVE_ENTRY(String_toUpperCase, 0, 1) { |
564 | const String& receiver = |
565 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
566 | ASSERT(!receiver.IsNull()); |
567 | return String::ToUpperCase(receiver); |
568 | } |
569 | |
570 | DEFINE_NATIVE_ENTRY(String_concatRange, 0, 3) { |
571 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, argument, arguments->NativeArgAt(0)); |
572 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, start, arguments->NativeArgAt(1)); |
573 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, end, arguments->NativeArgAt(2)); |
574 | const intptr_t start_ix = start.Value(); |
575 | const intptr_t end_ix = end.Value(); |
576 | if (start_ix < 0) { |
577 | Exceptions::ThrowArgumentError(start); |
578 | } |
579 | Array& strings = Array::Handle(); |
580 | intptr_t length = -1; |
581 | if (argument.IsArray()) { |
582 | strings ^= argument.raw(); |
583 | length = strings.Length(); |
584 | } else if (argument.IsGrowableObjectArray()) { |
585 | const GrowableObjectArray& g_array = GrowableObjectArray::Cast(argument); |
586 | strings = g_array.data(); |
587 | length = g_array.Length(); |
588 | } else { |
589 | Exceptions::ThrowArgumentError(argument); |
590 | } |
591 | if (end_ix > length) { |
592 | Exceptions::ThrowArgumentError(end); |
593 | } |
594 | #if defined(DEBUG) |
595 | // Check that the array contains strings. |
596 | Instance& elem = Instance::Handle(); |
597 | for (intptr_t i = start_ix; i < end_ix; i++) { |
598 | elem ^= strings.At(i); |
599 | ASSERT(elem.IsString()); |
600 | } |
601 | #endif |
602 | return String::ConcatAllRange(strings, start_ix, end_ix, Heap::kNew); |
603 | } |
604 | |
605 | DEFINE_NATIVE_ENTRY(StringBuffer_createStringFromUint16Array, 0, 3) { |
606 | GET_NON_NULL_NATIVE_ARGUMENT(TypedData, codeUnits, arguments->NativeArgAt(0)); |
607 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1)); |
608 | GET_NON_NULL_NATIVE_ARGUMENT(Bool, isLatin1, arguments->NativeArgAt(2)); |
609 | intptr_t array_length = codeUnits.Length(); |
610 | intptr_t length_value = length.Value(); |
611 | if (length_value < 0 || length_value > array_length) { |
612 | Exceptions::ThrowRangeError("length" , length, 0, array_length); |
613 | } |
614 | const String& result = |
615 | isLatin1.value() |
616 | ? String::Handle(OneByteString::New(length_value, Heap::kNew)) |
617 | : String::Handle(TwoByteString::New(length_value, Heap::kNew)); |
618 | NoSafepointScope no_safepoint; |
619 | |
620 | uint16_t* data_position = reinterpret_cast<uint16_t*>(codeUnits.DataAddr(0)); |
621 | String::Copy(result, 0, data_position, length_value); |
622 | return result.raw(); |
623 | } |
624 | |
625 | } // namespace dart |
626 | |