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
17namespace dart {
18
19DEFINE_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
31DEFINE_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
92DEFINE_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.
104static 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
124static const intptr_t kLengthSize = 11;
125static const intptr_t kLengthMask = (1 << kLengthSize) - 1;
126
127static 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
163DEFINE_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
248DEFINE_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.
261DEFINE_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
289DEFINE_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
303DEFINE_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
317DEFINE_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
401DEFINE_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
411DEFINE_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
421DEFINE_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
504DEFINE_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
513DEFINE_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
519static 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
532DEFINE_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.
541DEFINE_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
549DEFINE_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
556DEFINE_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
563DEFINE_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
570DEFINE_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
605DEFINE_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