• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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