1 #include <limits.h> // INT_MAX
2 #include <stdlib.h>
3 #include <string.h>
4 #define NAPI_EXPERIMENTAL
5 #include <js_native_api.h>
6 #include "../common.h"
7 #include "../entry_point.h"
8 #include "test_null.h"
9
10 enum length_type { actual_length, auto_length };
11
validate_and_retrieve_single_string_arg(napi_env env,napi_callback_info info,napi_value * arg)12 static napi_status validate_and_retrieve_single_string_arg(
13 napi_env env, napi_callback_info info, napi_value* arg) {
14 size_t argc = 1;
15 NODE_API_CHECK_STATUS(napi_get_cb_info(env, info, &argc, arg, NULL, NULL));
16
17 NODE_API_ASSERT_STATUS(env, argc >= 1, "Wrong number of arguments");
18
19 napi_valuetype valuetype;
20 NODE_API_CHECK_STATUS(napi_typeof(env, *arg, &valuetype));
21
22 NODE_API_ASSERT_STATUS(env,
23 valuetype == napi_string,
24 "Wrong type of argment. Expects a string.");
25
26 return napi_ok;
27 }
28
29 // These help us factor out code that is common between the bindings.
30 typedef napi_status (*OneByteCreateAPI)(napi_env,
31 const char*,
32 size_t,
33 napi_value*);
34 typedef napi_status (*OneByteGetAPI)(
35 napi_env, napi_value, char*, size_t, size_t*);
36 typedef napi_status (*TwoByteCreateAPI)(napi_env,
37 const char16_t*,
38 size_t,
39 napi_value*);
40 typedef napi_status (*TwoByteGetAPI)(
41 napi_env, napi_value, char16_t*, size_t, size_t*);
42
43 // Test passing back the one-byte string we got from JS.
TestOneByteImpl(napi_env env,napi_callback_info info,OneByteGetAPI get_api,OneByteCreateAPI create_api,enum length_type length_mode)44 static napi_value TestOneByteImpl(napi_env env,
45 napi_callback_info info,
46 OneByteGetAPI get_api,
47 OneByteCreateAPI create_api,
48 enum length_type length_mode) {
49 napi_value args[1];
50 NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
51
52 char buffer[128];
53 size_t buffer_size = 128;
54 size_t copied;
55
56 NODE_API_CALL(env, get_api(env, args[0], buffer, buffer_size, &copied));
57
58 napi_value output;
59 if (length_mode == auto_length) {
60 copied = NAPI_AUTO_LENGTH;
61 }
62 NODE_API_CALL(env, create_api(env, buffer, copied, &output));
63
64 return output;
65 }
66
67 // Test passing back the two-byte string we got from JS.
TestTwoByteImpl(napi_env env,napi_callback_info info,TwoByteGetAPI get_api,TwoByteCreateAPI create_api,enum length_type length_mode)68 static napi_value TestTwoByteImpl(napi_env env,
69 napi_callback_info info,
70 TwoByteGetAPI get_api,
71 TwoByteCreateAPI create_api,
72 enum length_type length_mode) {
73 napi_value args[1];
74 NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
75
76 char16_t buffer[128];
77 size_t buffer_size = 128;
78 size_t copied;
79
80 NODE_API_CALL(env, get_api(env, args[0], buffer, buffer_size, &copied));
81
82 napi_value output;
83 if (length_mode == auto_length) {
84 copied = NAPI_AUTO_LENGTH;
85 }
86 NODE_API_CALL(env, create_api(env, buffer, copied, &output));
87
88 return output;
89 }
90
free_string(node_api_nogc_env env,void * data,void * hint)91 static void free_string(node_api_nogc_env env, void* data, void* hint) {
92 free(data);
93 }
94
create_external_latin1(napi_env env,const char * string,size_t length,napi_value * result)95 static napi_status create_external_latin1(napi_env env,
96 const char* string,
97 size_t length,
98 napi_value* result) {
99 napi_status status;
100 // Initialize to true, because that is the value we don't want.
101 bool copied = true;
102 char* string_copy;
103 const size_t actual_length =
104 (length == NAPI_AUTO_LENGTH ? strlen(string) : length);
105 const size_t length_bytes = (actual_length + 1) * sizeof(*string_copy);
106 string_copy = malloc(length_bytes);
107 memcpy(string_copy, string, length_bytes);
108 string_copy[actual_length] = 0;
109
110 status = node_api_create_external_string_latin1(
111 env, string_copy, length, free_string, NULL, result, &copied);
112 // We do not want the string to be copied.
113 if (copied) {
114 return napi_generic_failure;
115 }
116 if (status != napi_ok) {
117 free(string_copy);
118 return status;
119 }
120 return napi_ok;
121 }
122
123 // strlen for char16_t. Needed in case we're copying a string of length
124 // NAPI_AUTO_LENGTH.
strlen16(const char16_t * string)125 static size_t strlen16(const char16_t* string) {
126 for (const char16_t* iter = string;; iter++) {
127 if (*iter == 0) {
128 return iter - string;
129 }
130 }
131 // We should never get here.
132 abort();
133 }
134
create_external_utf16(napi_env env,const char16_t * string,size_t length,napi_value * result)135 static napi_status create_external_utf16(napi_env env,
136 const char16_t* string,
137 size_t length,
138 napi_value* result) {
139 napi_status status;
140 // Initialize to true, because that is the value we don't want.
141 bool copied = true;
142 char16_t* string_copy;
143 const size_t actual_length =
144 (length == NAPI_AUTO_LENGTH ? strlen16(string) : length);
145 const size_t length_bytes = (actual_length + 1) * sizeof(*string_copy);
146 string_copy = malloc(length_bytes);
147 memcpy(string_copy, string, length_bytes);
148 string_copy[actual_length] = 0;
149
150 status = node_api_create_external_string_utf16(
151 env, string_copy, length, free_string, NULL, result, &copied);
152 if (status != napi_ok) {
153 free(string_copy);
154 return status;
155 }
156
157 return napi_ok;
158 }
159
TestLatin1(napi_env env,napi_callback_info info)160 static napi_value TestLatin1(napi_env env, napi_callback_info info) {
161 return TestOneByteImpl(env,
162 info,
163 napi_get_value_string_latin1,
164 napi_create_string_latin1,
165 actual_length);
166 }
167
TestUtf8(napi_env env,napi_callback_info info)168 static napi_value TestUtf8(napi_env env, napi_callback_info info) {
169 return TestOneByteImpl(env,
170 info,
171 napi_get_value_string_utf8,
172 napi_create_string_utf8,
173 actual_length);
174 }
175
TestUtf16(napi_env env,napi_callback_info info)176 static napi_value TestUtf16(napi_env env, napi_callback_info info) {
177 return TestTwoByteImpl(env,
178 info,
179 napi_get_value_string_utf16,
180 napi_create_string_utf16,
181 actual_length);
182 }
183
TestLatin1AutoLength(napi_env env,napi_callback_info info)184 static napi_value TestLatin1AutoLength(napi_env env, napi_callback_info info) {
185 return TestOneByteImpl(env,
186 info,
187 napi_get_value_string_latin1,
188 napi_create_string_latin1,
189 auto_length);
190 }
191
TestUtf8AutoLength(napi_env env,napi_callback_info info)192 static napi_value TestUtf8AutoLength(napi_env env, napi_callback_info info) {
193 return TestOneByteImpl(env,
194 info,
195 napi_get_value_string_utf8,
196 napi_create_string_utf8,
197 auto_length);
198 }
199
TestUtf16AutoLength(napi_env env,napi_callback_info info)200 static napi_value TestUtf16AutoLength(napi_env env, napi_callback_info info) {
201 return TestTwoByteImpl(env,
202 info,
203 napi_get_value_string_utf16,
204 napi_create_string_utf16,
205 auto_length);
206 }
207
TestLatin1External(napi_env env,napi_callback_info info)208 static napi_value TestLatin1External(napi_env env, napi_callback_info info) {
209 return TestOneByteImpl(env,
210 info,
211 napi_get_value_string_latin1,
212 create_external_latin1,
213 actual_length);
214 }
215
TestUtf16External(napi_env env,napi_callback_info info)216 static napi_value TestUtf16External(napi_env env, napi_callback_info info) {
217 return TestTwoByteImpl(env,
218 info,
219 napi_get_value_string_utf16,
220 create_external_utf16,
221 actual_length);
222 }
223
TestLatin1ExternalAutoLength(napi_env env,napi_callback_info info)224 static napi_value TestLatin1ExternalAutoLength(napi_env env,
225 napi_callback_info info) {
226 return TestOneByteImpl(env,
227 info,
228 napi_get_value_string_latin1,
229 create_external_latin1,
230 auto_length);
231 }
232
TestUtf16ExternalAutoLength(napi_env env,napi_callback_info info)233 static napi_value TestUtf16ExternalAutoLength(napi_env env,
234 napi_callback_info info) {
235 return TestTwoByteImpl(env,
236 info,
237 napi_get_value_string_utf16,
238 create_external_utf16,
239 auto_length);
240 }
241
TestLatin1Insufficient(napi_env env,napi_callback_info info)242 static napi_value TestLatin1Insufficient(napi_env env,
243 napi_callback_info info) {
244 napi_value args[1];
245 NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
246
247 char buffer[4];
248 size_t buffer_size = 4;
249 size_t copied;
250
251 NODE_API_CALL(
252 env,
253 napi_get_value_string_latin1(env, args[0], buffer, buffer_size, &copied));
254
255 napi_value output;
256 NODE_API_CALL(env, napi_create_string_latin1(env, buffer, copied, &output));
257
258 return output;
259 }
260
TestUtf8Insufficient(napi_env env,napi_callback_info info)261 static napi_value TestUtf8Insufficient(napi_env env, napi_callback_info info) {
262 napi_value args[1];
263 NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
264
265 char buffer[4];
266 size_t buffer_size = 4;
267 size_t copied;
268
269 NODE_API_CALL(
270 env,
271 napi_get_value_string_utf8(env, args[0], buffer, buffer_size, &copied));
272
273 napi_value output;
274 NODE_API_CALL(env, napi_create_string_utf8(env, buffer, copied, &output));
275
276 return output;
277 }
278
TestUtf16Insufficient(napi_env env,napi_callback_info info)279 static napi_value TestUtf16Insufficient(napi_env env, napi_callback_info info) {
280 napi_value args[1];
281 NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
282
283 char16_t buffer[4];
284 size_t buffer_size = 4;
285 size_t copied;
286
287 NODE_API_CALL(
288 env,
289 napi_get_value_string_utf16(env, args[0], buffer, buffer_size, &copied));
290
291 napi_value output;
292 NODE_API_CALL(env, napi_create_string_utf16(env, buffer, copied, &output));
293
294 return output;
295 }
296
Utf16Length(napi_env env,napi_callback_info info)297 static napi_value Utf16Length(napi_env env, napi_callback_info info) {
298 napi_value args[1];
299 NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
300
301 size_t length;
302 NODE_API_CALL(env,
303 napi_get_value_string_utf16(env, args[0], NULL, 0, &length));
304
305 napi_value output;
306 NODE_API_CALL(env, napi_create_uint32(env, (uint32_t)length, &output));
307
308 return output;
309 }
310
Utf8Length(napi_env env,napi_callback_info info)311 static napi_value Utf8Length(napi_env env, napi_callback_info info) {
312 napi_value args[1];
313 NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
314
315 size_t length;
316 NODE_API_CALL(env,
317 napi_get_value_string_utf8(env, args[0], NULL, 0, &length));
318
319 napi_value output;
320 NODE_API_CALL(env, napi_create_uint32(env, (uint32_t)length, &output));
321
322 return output;
323 }
324
TestLargeUtf8(napi_env env,napi_callback_info info)325 static napi_value TestLargeUtf8(napi_env env, napi_callback_info info) {
326 napi_value output;
327 if (SIZE_MAX > INT_MAX) {
328 NODE_API_CALL(
329 env, napi_create_string_utf8(env, "", ((size_t)INT_MAX) + 1, &output));
330 } else {
331 // just throw the expected error as there is nothing to test
332 // in this case since we can't overflow
333 NODE_API_CALL(env, napi_throw_error(env, NULL, "Invalid argument"));
334 }
335
336 return output;
337 }
338
TestLargeLatin1(napi_env env,napi_callback_info info)339 static napi_value TestLargeLatin1(napi_env env, napi_callback_info info) {
340 napi_value output;
341 if (SIZE_MAX > INT_MAX) {
342 NODE_API_CALL(
343 env,
344 napi_create_string_latin1(env, "", ((size_t)INT_MAX) + 1, &output));
345 } else {
346 // just throw the expected error as there is nothing to test
347 // in this case since we can't overflow
348 NODE_API_CALL(env, napi_throw_error(env, NULL, "Invalid argument"));
349 }
350
351 return output;
352 }
353
TestLargeUtf16(napi_env env,napi_callback_info info)354 static napi_value TestLargeUtf16(napi_env env, napi_callback_info info) {
355 napi_value output;
356 if (SIZE_MAX > INT_MAX) {
357 NODE_API_CALL(
358 env,
359 napi_create_string_utf16(
360 env, ((const char16_t*)""), ((size_t)INT_MAX) + 1, &output));
361 } else {
362 // just throw the expected error as there is nothing to test
363 // in this case since we can't overflow
364 NODE_API_CALL(env, napi_throw_error(env, NULL, "Invalid argument"));
365 }
366
367 return output;
368 }
369
TestMemoryCorruption(napi_env env,napi_callback_info info)370 static napi_value TestMemoryCorruption(napi_env env, napi_callback_info info) {
371 size_t argc = 1;
372 napi_value args[1];
373 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
374
375 NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
376
377 char buf[10] = {0};
378 NODE_API_CALL(env, napi_get_value_string_utf8(env, args[0], buf, 0, NULL));
379
380 char zero[10] = {0};
381 if (memcmp(buf, zero, sizeof(buf)) != 0) {
382 NODE_API_CALL(env, napi_throw_error(env, NULL, "Buffer overwritten"));
383 }
384
385 return NULL;
386 }
387
388 EXTERN_C_START
Init(napi_env env,napi_value exports)389 napi_value Init(napi_env env, napi_value exports) {
390 napi_property_descriptor properties[] = {
391 DECLARE_NODE_API_PROPERTY("TestLatin1", TestLatin1),
392 DECLARE_NODE_API_PROPERTY("TestLatin1AutoLength", TestLatin1AutoLength),
393 DECLARE_NODE_API_PROPERTY("TestLatin1External", TestLatin1External),
394 DECLARE_NODE_API_PROPERTY("TestLatin1ExternalAutoLength",
395 TestLatin1ExternalAutoLength),
396 DECLARE_NODE_API_PROPERTY("TestLatin1Insufficient",
397 TestLatin1Insufficient),
398 DECLARE_NODE_API_PROPERTY("TestUtf8", TestUtf8),
399 DECLARE_NODE_API_PROPERTY("TestUtf8AutoLength", TestUtf8AutoLength),
400 DECLARE_NODE_API_PROPERTY("TestUtf8Insufficient", TestUtf8Insufficient),
401 DECLARE_NODE_API_PROPERTY("TestUtf16", TestUtf16),
402 DECLARE_NODE_API_PROPERTY("TestUtf16AutoLength", TestUtf16AutoLength),
403 DECLARE_NODE_API_PROPERTY("TestUtf16External", TestUtf16External),
404 DECLARE_NODE_API_PROPERTY("TestUtf16ExternalAutoLength",
405 TestUtf16ExternalAutoLength),
406 DECLARE_NODE_API_PROPERTY("TestUtf16Insufficient", TestUtf16Insufficient),
407 DECLARE_NODE_API_PROPERTY("Utf16Length", Utf16Length),
408 DECLARE_NODE_API_PROPERTY("Utf8Length", Utf8Length),
409 DECLARE_NODE_API_PROPERTY("TestLargeUtf8", TestLargeUtf8),
410 DECLARE_NODE_API_PROPERTY("TestLargeLatin1", TestLargeLatin1),
411 DECLARE_NODE_API_PROPERTY("TestLargeUtf16", TestLargeUtf16),
412 DECLARE_NODE_API_PROPERTY("TestMemoryCorruption", TestMemoryCorruption),
413 };
414
415 init_test_null(env, exports);
416
417 NODE_API_CALL(
418 env,
419 napi_define_properties(
420 env, exports, sizeof(properties) / sizeof(*properties), properties));
421
422 return exports;
423 }
424 EXTERN_C_END
425