Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
string.cc
Go to the documentation of this file.
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
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 =
25 if (!env_value.IsNull()) {
26 return Symbols::New(thread, env_value);
27 }
28 return default_value.ptr();
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
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).ptr();
44 length = a.Length();
45 } else {
47 return nullptr; // Unreachable.
48 }
49
50 intptr_t start = start_obj.Value();
51 if ((start < 0) || (start > length)) {
53 }
54
55 intptr_t end = end_obj.Value();
56 if ((end < start) || (end > length)) {
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()) {
70 }
71 intptr_t value = Smi::Cast(index_object).Value();
73 Exceptions::ThrowByType(Exceptions::kArgument, Object::empty_array());
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());
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 for (intptr_t i = start; i < end; i++) {
113 result |= TwoByteString::CharAt(string, i);
114 }
115 return result;
116}
117
118static constexpr intptr_t kLengthSize = 11;
119static constexpr intptr_t kLengthMask = (1 << kLengthSize) - 1;
120
121static bool CheckSlicesOneByte(const String& base,
122 const Array& matches,
123 const int len) {
124 Instance& object = Instance::Handle();
125 // Check each slice for one-bytedness.
126 for (intptr_t i = 0; i < len; i++) {
127 object ^= matches.At(i);
128 if (object.IsSmi()) {
129 intptr_t slice_start = Smi::Cast(object).Value();
130 intptr_t slice_end;
131 if (slice_start < 0) {
132 intptr_t bits = -slice_start;
133 slice_start = bits >> kLengthSize;
134 slice_end = slice_start + (bits & kLengthMask);
135 } else {
136 i++;
137 if (i >= len) {
138 // Bad format, handled later.
139 return false;
140 }
141 object ^= matches.At(i);
142 if (!object.IsSmi()) {
143 // Bad format, handled later.
144 return false;
145 }
146 slice_end = Smi::Cast(object).Value();
147 }
148 uint16_t char_limit = CharacterLimit(base, slice_start, slice_end);
149 if (char_limit > 0xff) {
150 return false;
151 }
152 }
153 }
154 return true;
155}
156
157DEFINE_NATIVE_ENTRY(StringBase_joinReplaceAllResult, 0, 4) {
158 const String& base = String::CheckedHandle(zone, arguments->NativeArgAt(0));
160 arguments->NativeArgAt(1));
161 GET_NON_NULL_NATIVE_ARGUMENT(Smi, length_obj, arguments->NativeArgAt(2));
162 GET_NON_NULL_NATIVE_ARGUMENT(Bool, is_onebyte_obj, arguments->NativeArgAt(3));
163
164 intptr_t len = matches_growable.Length();
165 const Array& matches = Array::Handle(zone, matches_growable.data());
166
167 const intptr_t length = length_obj.Value();
168 if (length < 0) {
170 }
171
172 // Start out assuming result is one-byte if replacements are.
173 bool is_onebyte = is_onebyte_obj.value();
174 if (is_onebyte) {
175 // If any of the base string slices are not one-byte, the result will be
176 // a two-byte string.
177 if (!base.IsOneByteString()) {
178 is_onebyte = CheckSlicesOneByte(base, matches, len);
179 }
180 }
181
182 const intptr_t base_length = base.Length();
184 if (is_onebyte) {
186 } else {
188 }
189 Instance& object = Instance::Handle(zone);
190 intptr_t write_index = 0;
191 for (intptr_t i = 0; i < len; i++) {
192 object ^= matches.At(i);
193 if (object.IsSmi()) {
194 intptr_t slice_start = Smi::Cast(object).Value();
195 intptr_t slice_length = -1;
196 // Slices with limited ranges are stored in a single negative Smi.
197 if (slice_start < 0) {
198 intptr_t bits = -slice_start;
199 slice_start = bits >> kLengthSize;
200 slice_length = bits & kLengthMask;
201 } else {
202 i++;
203 if (i < len) { // Otherwise slice_length stays at -1.
204 object ^= matches.At(i);
205 if (object.IsSmi()) {
206 intptr_t slice_end = Smi::Cast(object).Value();
207 slice_length = slice_end - slice_start;
208 }
209 }
210 }
211 if (slice_length > 0) {
212 if (0 <= slice_start && slice_start + slice_length <= base_length &&
213 write_index + slice_length <= length) {
214 String::Copy(result, write_index, base, slice_start, slice_length);
215 write_index += slice_length;
216 continue;
217 }
218 }
219 // Either the slice_length was zero,
220 // or the first smi was positive and not followed by another smi,
221 // or the smis were not a valid slice of the base string,
222 // or the slice was too large to fit in the result.
223 // Something is wrong with the matches array!
224 Exceptions::ThrowArgumentError(matches_growable);
225 } else if (object.IsString()) {
226 const String& replacement = String::Cast(object);
227 intptr_t replacement_length = replacement.Length();
228 if (write_index + replacement_length > length) {
229 // Invalid input data, either in matches list or the total length.
230 Exceptions::ThrowArgumentError(matches_growable);
231 }
232 String::Copy(result, write_index, replacement, 0, replacement_length);
233 write_index += replacement_length;
234 }
235 }
236 if (write_index < length) {
237 Exceptions::ThrowArgumentError(matches_growable);
238 }
239 return result.ptr();
240}
241
242DEFINE_NATIVE_ENTRY(StringBase_intern, 0, 1) {
243 const String& receiver =
244 String::CheckedHandle(zone, arguments->NativeArgAt(0));
245 return Symbols::New(thread, receiver);
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
260DEFINE_NATIVE_ENTRY(Internal_allocateOneByteString, 0, 1) {
261 GET_NON_NULL_NATIVE_ARGUMENT(Integer, length_obj, arguments->NativeArgAt(0));
262 const int64_t length = length_obj.AsInt64Value();
263 if ((length < 0) || (length > OneByteString::kMaxElements)) {
264 // Assume that negative lengths are the result of wrapping in code in
265 // string_patch.dart.
266 const Instance& exception = Instance::Handle(
267 thread->isolate_group()->object_store()->out_of_memory());
268 Exceptions::Throw(thread, exception);
269 UNREACHABLE();
270 }
271 return OneByteString::New(static_cast<intptr_t>(length), Heap::kNew);
272}
273
274DEFINE_NATIVE_ENTRY(Internal_allocateTwoByteString, 0, 1) {
275 GET_NON_NULL_NATIVE_ARGUMENT(Integer, length_obj, arguments->NativeArgAt(0));
276 const int64_t length = length_obj.AsInt64Value();
277 if ((length < 0) || (length > TwoByteString::kMaxElements)) {
278 // Assume that negative lengths are the result of wrapping in code in
279 // string_patch.dart.
280 const Instance& exception = Instance::Handle(
281 thread->isolate_group()->object_store()->out_of_memory());
282 Exceptions::Throw(thread, exception);
283 UNREACHABLE();
284 }
285 return TwoByteString::New(static_cast<intptr_t>(length), Heap::kNew);
286}
287
288DEFINE_NATIVE_ENTRY(OneByteString_allocateFromOneByteList, 0, 3) {
289 Instance& list = Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
290 GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1));
291 GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2));
292
293 intptr_t start = start_obj.Value();
294 intptr_t end = end_obj.Value();
295 if (start < 0) {
297 }
298 intptr_t length = end - start;
299 if (length < 0) {
301 }
302 ASSERT(length >= 0);
303
304 Heap::Space space = Heap::kNew;
305 if (list.IsTypedDataBase()) {
306 const TypedDataBase& array = TypedDataBase::Cast(list);
307 if (array.ElementType() != kUint8ArrayElement) {
309 }
310 if (end > array.Length()) {
312 }
313 return OneByteString::New(array, start, length, space);
314 } else if (list.IsArray()) {
315 const Array& array = Array::Cast(list);
316 if (end > array.Length()) {
318 }
320 for (int i = 0; i < length; i++) {
321 intptr_t value = Smi::Value(static_cast<SmiPtr>(array.At(start + i)));
323 }
324 return string.ptr();
325 } else if (list.IsGrowableObjectArray()) {
326 const GrowableObjectArray& array = GrowableObjectArray::Cast(list);
327 if (end > array.Length()) {
329 }
331 for (int i = 0; i < length; i++) {
332 intptr_t value = Smi::Value(static_cast<SmiPtr>(array.At(start + i)));
334 }
335 return string.ptr();
336 }
337 UNREACHABLE();
338 return Object::null();
339}
340
341DEFINE_NATIVE_ENTRY(Internal_writeIntoOneByteString, 0, 3) {
342 GET_NON_NULL_NATIVE_ARGUMENT(String, receiver, arguments->NativeArgAt(0));
343 ASSERT(receiver.IsOneByteString());
344 GET_NON_NULL_NATIVE_ARGUMENT(Smi, index_obj, arguments->NativeArgAt(1));
345 GET_NON_NULL_NATIVE_ARGUMENT(Smi, code_point_obj, arguments->NativeArgAt(2));
346 OneByteString::SetCharAt(receiver, index_obj.Value(),
347 code_point_obj.Value() & 0xFF);
348 return Object::null();
349}
350
351DEFINE_NATIVE_ENTRY(Internal_writeIntoTwoByteString, 0, 3) {
352 GET_NON_NULL_NATIVE_ARGUMENT(String, receiver, arguments->NativeArgAt(0));
353 ASSERT(receiver.IsTwoByteString());
354 GET_NON_NULL_NATIVE_ARGUMENT(Smi, index_obj, arguments->NativeArgAt(1));
355 GET_NON_NULL_NATIVE_ARGUMENT(Smi, code_point_obj, arguments->NativeArgAt(2));
356 TwoByteString::SetCharAt(receiver, index_obj.Value(),
357 code_point_obj.Value() & 0xFFFF);
358 return Object::null();
359}
360
361DEFINE_NATIVE_ENTRY(TwoByteString_allocateFromTwoByteList, 0, 3) {
362 Instance& list = Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
363 GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1));
364 GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2));
365
366 intptr_t start = start_obj.Value();
367 intptr_t end = end_obj.Value();
368 if (start < 0) {
370 }
371 intptr_t length = end - start;
372 if (length < 0) {
374 }
375
376 Heap::Space space = Heap::kNew;
377 if (list.IsTypedDataBase()) {
378 const TypedDataBase& array = TypedDataBase::Cast(list);
379 if (array.ElementType() != kUint16ArrayElement) {
381 }
382 if (end > array.Length()) {
384 }
385 return TwoByteString::New(array, start * sizeof(uint16_t), length, space);
386 } else if (list.IsArray()) {
387 const Array& array = Array::Cast(list);
388 if (end > array.Length()) {
390 }
391 const String& string =
393 for (int i = 0; i < length; i++) {
394 intptr_t value = Smi::Value(static_cast<SmiPtr>(array.At(start + i)));
396 }
397 return string.ptr();
398 } else if (list.IsGrowableObjectArray()) {
399 const GrowableObjectArray& array = GrowableObjectArray::Cast(list);
400 if (end > array.Length()) {
402 }
403 const String& string =
405 for (int i = 0; i < length; i++) {
406 intptr_t value = Smi::Value(static_cast<SmiPtr>(array.At(start + i)));
408 }
409 return string.ptr();
410 }
411 UNREACHABLE();
412 return Object::null();
413}
414
415DEFINE_NATIVE_ENTRY(String_getHashCode, 0, 1) {
416 const String& receiver =
417 String::CheckedHandle(zone, arguments->NativeArgAt(0));
418 intptr_t hash_val = receiver.Hash();
419 ASSERT(hash_val > 0);
420 ASSERT(Smi::IsValid(hash_val));
421 return Smi::New(hash_val);
422}
423
424DEFINE_NATIVE_ENTRY(String_getLength, 0, 1) {
425 const String& receiver =
426 String::CheckedHandle(zone, arguments->NativeArgAt(0));
427 return Smi::New(receiver.Length());
428}
429
430static uint16_t StringValueAt(const String& str, const Integer& index) {
431 if (index.IsSmi()) {
432 const intptr_t index_value = Smi::Cast(index).Value();
433 if ((0 <= index_value) && (index_value < str.Length())) {
434 return str.CharAt(index_value);
435 }
436 }
437
438 // An index larger than Smi is always illegal.
439 Exceptions::ThrowRangeError("index", index, 0, str.Length() - 1);
440 return 0;
441}
442
443DEFINE_NATIVE_ENTRY(String_charAt, 0, 2) {
444 const String& receiver =
445 String::CheckedHandle(zone, arguments->NativeArgAt(0));
446 GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
447 uint16_t value = StringValueAt(receiver, index);
448 return Symbols::FromCharCode(thread, static_cast<int32_t>(value));
449}
450
451DEFINE_NATIVE_ENTRY(String_concat, 0, 2) {
452 const String& receiver =
453 String::CheckedHandle(zone, arguments->NativeArgAt(0));
454 GET_NON_NULL_NATIVE_ARGUMENT(String, b, arguments->NativeArgAt(1));
455 return String::Concat(receiver, b);
456}
457
458DEFINE_NATIVE_ENTRY(String_toLowerCase, 0, 1) {
459 const String& receiver =
460 String::CheckedHandle(zone, arguments->NativeArgAt(0));
461 ASSERT(!receiver.IsNull());
462 return String::ToLowerCase(receiver);
463}
464
465DEFINE_NATIVE_ENTRY(String_toUpperCase, 0, 1) {
466 const String& receiver =
467 String::CheckedHandle(zone, arguments->NativeArgAt(0));
468 ASSERT(!receiver.IsNull());
469 return String::ToUpperCase(receiver);
470}
471
472DEFINE_NATIVE_ENTRY(String_concatRange, 0, 3) {
473 GET_NON_NULL_NATIVE_ARGUMENT(Instance, argument, arguments->NativeArgAt(0));
474 GET_NON_NULL_NATIVE_ARGUMENT(Smi, start, arguments->NativeArgAt(1));
475 GET_NON_NULL_NATIVE_ARGUMENT(Smi, end, arguments->NativeArgAt(2));
476 const intptr_t start_ix = start.Value();
477 const intptr_t end_ix = end.Value();
478 if (start_ix < 0) {
480 }
481 Array& strings = Array::Handle();
482 intptr_t length = -1;
483 if (argument.IsArray()) {
484 strings ^= argument.ptr();
485 length = strings.Length();
486 } else if (argument.IsGrowableObjectArray()) {
487 const GrowableObjectArray& g_array = GrowableObjectArray::Cast(argument);
488 strings = g_array.data();
489 length = g_array.Length();
490 } else {
492 }
493 if (end_ix > length) {
495 }
496#if defined(DEBUG)
497 // Check that the array contains strings.
498 Instance& elem = Instance::Handle();
499 for (intptr_t i = start_ix; i < end_ix; i++) {
500 elem ^= strings.At(i);
501 ASSERT(elem.IsString());
502 }
503#endif
504 return String::ConcatAllRange(strings, start_ix, end_ix, Heap::kNew);
505}
506
507DEFINE_NATIVE_ENTRY(StringBuffer_createStringFromUint16Array, 0, 3) {
508 GET_NON_NULL_NATIVE_ARGUMENT(TypedData, codeUnits, arguments->NativeArgAt(0));
509 GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1));
510 GET_NON_NULL_NATIVE_ARGUMENT(Bool, isLatin1, arguments->NativeArgAt(2));
511 intptr_t array_length = codeUnits.Length();
512 intptr_t length_value = length.Value();
513 if (length_value < 0 || length_value > array_length) {
514 Exceptions::ThrowRangeError("length", length, 0, array_length);
515 }
516 const String& result =
517 isLatin1.value()
520 NoSafepointScope no_safepoint;
521
522 uint16_t* data_position = reinterpret_cast<uint16_t*>(codeUnits.DataAddr(0));
523 String::Copy(result, 0, data_position, length_value);
524 return result.ptr();
525}
526
527} // namespace dart
#define UNREACHABLE()
Definition assert.h:248
static StringPtr GetEnvironmentValue(Thread *thread, const String &name)
ObjectPtr At(intptr_t index) const
Definition object.h:10854
intptr_t Length() const
Definition object.h:10808
static DART_NORETURN void ThrowByType(ExceptionType type, const Array &arguments)
static DART_NORETURN void ThrowRangeError(const char *argument_name, const Integer &argument_value, intptr_t expected_from, intptr_t expected_to)
static DART_NORETURN void Throw(Thread *thread, const Instance &exception)
static DART_NORETURN void ThrowArgumentError(const Instance &arg)
intptr_t Length() const
Definition object.h:11046
ObjectPtr At(intptr_t index) const
Definition object.h:11059
ArrayPtr data() const
Definition object.h:11056
@ kNew
Definition heap.h:38
static ObjectPtr null()
Definition object.h:433
ObjectPtr ptr() const
Definition object.h:332
bool IsNull() const
Definition object.h:363
static Object & Handle()
Definition object.h:407
static void SetCharAt(const String &str, intptr_t index, uint8_t code_unit)
Definition object.h:10515
static OneByteStringPtr New(intptr_t len, Heap::Space space)
Definition object.cc:24447
static constexpr intptr_t kMaxElements
Definition object.h:10522
static SmiPtr New(intptr_t value)
Definition object.h:9985
intptr_t Value() const
Definition object.h:9969
static bool IsValid(int64_t value)
Definition object.h:10005
static void Copy(const String &dst, intptr_t dst_offset, const uint8_t *characters, intptr_t len)
Definition object.cc:23871
bool IsOneByteString() const
Definition object.h:10290
intptr_t Length() const
Definition object.h:10189
static StringPtr ToLowerCase(const String &str, Heap::Space space=Heap::kNew)
Definition object.cc:24281
static StringPtr ConcatAllRange(const Array &strings, intptr_t start, intptr_t end, Heap::Space space=Heap::kNew)
Definition object.cc:24131
uint16_t CharAt(intptr_t index) const
Definition object.h:10238
static StringPtr SubString(const String &str, intptr_t begin_index, Heap::Space space=Heap::kNew)
Definition object.cc:24159
static StringPtr Concat(const String &str1, const String &str2, Heap::Space space=Heap::kNew)
Definition object.cc:24116
static StringPtr ToUpperCase(const String &str, Heap::Space space=Heap::kNew)
Definition object.cc:24276
uword Hash() const
Definition object.h:10195
static StringPtr FromCharCode(Thread *thread, uint16_t char_code)
Definition symbols.cc:474
static StringPtr New(Thread *thread, const char *cstr)
Definition symbols.h:722
static uint16_t CharAt(const String &str, intptr_t index)
Definition object.h:10643
static TwoByteStringPtr New(intptr_t len, Heap::Space space)
Definition object.cc:24641
static constexpr intptr_t kMaxElements
Definition object.h:10663
static void SetCharAt(const String &str, intptr_t index, uint16_t ch)
Definition object.h:10654
TypedDataElementType ElementType() const
Definition object.h:11501
intptr_t Length() const
Definition object.h:11492
static bool IsSupplementary(int32_t code_point)
Definition unicode.h:31
static bool IsOutOfRange(int32_t code_point)
Definition unicode.h:36
static bool IsLatin1(int32_t code_point)
Definition unicode.h:23
#define ASSERT(E)
static bool b
struct MyStruct a[10]
glong glong end
uint8_t value
GAsyncResult * result
size_t length
const char *const name
static bool CheckSlicesOneByte(const String &base, const Array &matches, const int len)
Definition string.cc:121
static constexpr intptr_t kLengthSize
Definition string.cc:118
static uint16_t StringValueAt(const String &str, const Integer &index)
Definition string.cc:430
static uint16_t CharacterLimit(const String &string, intptr_t start, intptr_t end)
Definition string.cc:104
static constexpr intptr_t kLengthMask
Definition string.cc:119
#define DEFINE_NATIVE_ENTRY(name, type_argument_count, argument_count)
#define GET_NATIVE_ARGUMENT(type, name, value)
#define GET_NON_NULL_NATIVE_ARGUMENT(type, name, value)