GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/node_i18n.cc Lines: 239 284 84.2 %
Date: 2016-11-30 Branches: 105 179 58.7 %

Line Branch Exec Source
1
/*
2
 * notes: by srl295
3
 *  - When in NODE_HAVE_SMALL_ICU mode, ICU is linked against "stub" (null) data
4
 *     ( stubdata/libicudata.a ) containing nothing, no data, and it's also
5
 *    linked against a "small" data file which the SMALL_ICUDATA_ENTRY_POINT
6
 *    macro names. That's the "english+root" data.
7
 *
8
 *    If icu_data_path is non-null, the user has provided a path and we assume
9
 *    it goes somewhere useful. We set that path in ICU, and exit.
10
 *    If icu_data_path is null, they haven't set a path and we want the
11
 *    "english+root" data.  We call
12
 *       udata_setCommonData(SMALL_ICUDATA_ENTRY_POINT,...)
13
 *    to load up the english+root data.
14
 *
15
 *  - when NOT in NODE_HAVE_SMALL_ICU mode, ICU is linked directly with its full
16
 *    data. All of the variables and command line options for changing data at
17
 *    runtime are disabled, as they wouldn't fully override the internal data.
18
 *    See:  http://bugs.icu-project.org/trac/ticket/10924
19
 */
20
21
22
#include "node_i18n.h"
23
24
#if defined(NODE_HAVE_I18N_SUPPORT)
25
26
#include "node.h"
27
#include "node_buffer.h"
28
#include "env.h"
29
#include "env-inl.h"
30
#include "util.h"
31
#include "util-inl.h"
32
#include "v8.h"
33
34
#include <unicode/utypes.h>
35
#include <unicode/putil.h>
36
#include <unicode/uchar.h>
37
#include <unicode/udata.h>
38
#include <unicode/uidna.h>
39
#include <unicode/ucnv.h>
40
#include <unicode/utf8.h>
41
#include <unicode/utf16.h>
42
#include <unicode/timezone.h>
43
#include <unicode/ulocdata.h>
44
#include <unicode/uvernum.h>
45
#include <unicode/uversion.h>
46
47
#ifdef NODE_HAVE_SMALL_ICU
48
/* if this is defined, we have a 'secondary' entry point.
49
   compare following to utypes.h defs for U_ICUDATA_ENTRY_POINT */
50
#define SMALL_ICUDATA_ENTRY_POINT \
51
  SMALL_DEF2(U_ICU_VERSION_MAJOR_NUM, U_LIB_SUFFIX_C_NAME)
52
#define SMALL_DEF2(major, suff) SMALL_DEF(major, suff)
53
#ifndef U_LIB_SUFFIX_C_NAME
54
#define SMALL_DEF(major, suff) icusmdt##major##_dat
55
#else
56
#define SMALL_DEF(major, suff) icusmdt##suff##major##_dat
57
#endif
58
59
extern "C" const char U_DATA_API SMALL_ICUDATA_ENTRY_POINT[];
60
#endif
61
62
namespace node {
63
64
using v8::Context;
65
using v8::FunctionCallbackInfo;
66
using v8::Isolate;
67
using v8::Local;
68
using v8::MaybeLocal;
69
using v8::Object;
70
using v8::String;
71
using v8::Value;
72
73
bool flag_icu_data_dir = false;
74
75
namespace i18n {
76
77
const size_t kStorageSize = 1024;
78
79
// TODO(jasnell): This could potentially become a member of MaybeStackBuffer
80
// at some point in the future. Care would need to be taken with the
81
// MaybeStackBuffer<UChar> variant below.
82
3
MaybeLocal<Object> AsBuffer(Isolate* isolate,
83
                            MaybeStackBuffer<char>* buf,
84
                            size_t len) {
85
3
  if (buf->IsAllocated()) {
86
    MaybeLocal<Object> ret = Buffer::New(isolate, buf->out(), len);
87
    if (!ret.IsEmpty()) buf->Release();
88
    return ret;
89
  }
90
3
  return Buffer::Copy(isolate, buf->out(), len);
91
}
92
93
2
MaybeLocal<Object> AsBuffer(Isolate* isolate,
94
                            MaybeStackBuffer<UChar>* buf,
95
                            size_t len) {
96
2
  char* dst = reinterpret_cast<char*>(**buf);
97
2
  MaybeLocal<Object> ret;
98
2
  if (buf->IsAllocated()) {
99
1
    ret = Buffer::New(isolate, dst, len);
100
1
    if (!ret.IsEmpty()) buf->Release();
101
  } else {
102
1
    ret = Buffer::Copy(isolate, dst, len);
103
  }
104

4
  if (!ret.IsEmpty() && IsBigEndian()) {
105
    SPREAD_BUFFER_ARG(ret.ToLocalChecked(), buf);
106
    SwapBytes16(buf_data, buf_length);
107
  }
108
2
  return ret;
109
}
110
111
struct Converter {
112
4
  explicit Converter(const char* name, const char* sub = NULL)
113
4
      : conv(nullptr) {
114
4
    UErrorCode status = U_ZERO_ERROR;
115
4
    conv = ucnv_open(name, &status);
116
4
    CHECK(U_SUCCESS(status));
117
4
    if (sub != NULL) {
118
2
      ucnv_setSubstChars(conv, sub, strlen(sub), &status);
119
    }
120
4
  }
121
122
4
  ~Converter() {
123
4
    ucnv_close(conv);
124
  }
125
126
  UConverter* conv;
127
};
128
129
// One-Shot Converters
130
131
2
void CopySourceBuffer(MaybeStackBuffer<UChar>* dest,
132
                      const char* data,
133
                      const size_t length,
134
                      const size_t length_in_chars) {
135
2
  dest->AllocateSufficientStorage(length_in_chars);
136
2
  char* dst = reinterpret_cast<char*>(**dest);
137
2
  memcpy(dst, data, length);
138
2
  if (IsBigEndian()) {
139
    SwapBytes16(dst, length);
140
  }
141
2
}
142
143
typedef MaybeLocal<Object> (*TranscodeFunc)(Isolate* isolate,
144
                                            const char* fromEncoding,
145
                                            const char* toEncoding,
146
                                            const char* source,
147
                                            const size_t source_length,
148
                                            UErrorCode* status);
149
150
2
MaybeLocal<Object> Transcode(Isolate* isolate,
151
                             const char* fromEncoding,
152
                             const char* toEncoding,
153
                             const char* source,
154
                             const size_t source_length,
155
                             UErrorCode* status) {
156
2
  *status = U_ZERO_ERROR;
157
2
  MaybeLocal<Object> ret;
158
4
  MaybeStackBuffer<char> result;
159
4
  Converter to(toEncoding, "?");
160
4
  Converter from(fromEncoding);
161
2
  const uint32_t limit = source_length * ucnv_getMaxCharSize(to.conv);
162
4
  result.AllocateSufficientStorage(limit);
163
2
  char* target = *result;
164
2
  ucnv_convertEx(to.conv, from.conv, &target, target + limit,
165
                 &source, source + source_length, nullptr, nullptr,
166
2
                 nullptr, nullptr, true, true, status);
167
2
  if (U_SUCCESS(*status))
168
4
    ret = AsBuffer(isolate, &result, target - &result[0]);
169
4
  return ret;
170
}
171
172
MaybeLocal<Object> TranscodeToUcs2(Isolate* isolate,
173
                                   const char* fromEncoding,
174
                                   const char* toEncoding,
175
                                   const char* source,
176
                                   const size_t source_length,
177
                                   UErrorCode* status) {
178
  *status = U_ZERO_ERROR;
179
  MaybeLocal<Object> ret;
180
  MaybeStackBuffer<UChar> destbuf(source_length);
181
  Converter from(fromEncoding);
182
  const size_t length_in_chars = source_length * sizeof(*destbuf);
183
  ucnv_toUChars(from.conv, *destbuf, length_in_chars,
184
                source, source_length, status);
185
  if (U_SUCCESS(*status))
186
    ret = AsBuffer(isolate, &destbuf, length_in_chars);
187
  return ret;
188
}
189
190
MaybeLocal<Object> TranscodeFromUcs2(Isolate* isolate,
191
                                     const char* fromEncoding,
192
                                     const char* toEncoding,
193
                                     const char* source,
194
                                     const size_t source_length,
195
                                     UErrorCode* status) {
196
  *status = U_ZERO_ERROR;
197
  MaybeStackBuffer<UChar> sourcebuf;
198
  MaybeLocal<Object> ret;
199
  Converter to(toEncoding, "?");
200
  const size_t length_in_chars = source_length / sizeof(UChar);
201
  CopySourceBuffer(&sourcebuf, source, source_length, length_in_chars);
202
  MaybeStackBuffer<char> destbuf(length_in_chars);
203
  const uint32_t len = ucnv_fromUChars(to.conv, *destbuf, length_in_chars,
204
                                       *sourcebuf, length_in_chars, status);
205
  if (U_SUCCESS(*status))
206
    ret = AsBuffer(isolate, &destbuf, len);
207
  return ret;
208
}
209
210
2
MaybeLocal<Object> TranscodeUcs2FromUtf8(Isolate* isolate,
211
                                         const char* fromEncoding,
212
                                         const char* toEncoding,
213
                                         const char* source,
214
                                         const size_t source_length,
215
                                         UErrorCode* status) {
216
2
  *status = U_ZERO_ERROR;
217
4
  MaybeStackBuffer<UChar, kStorageSize> destbuf;
218
  int32_t result_length;
219
2
  u_strFromUTF8(*destbuf, kStorageSize, &result_length,
220
2
                source, source_length, status);
221
2
  MaybeLocal<Object> ret;
222
2
  if (U_SUCCESS(*status)) {
223
1
    ret = AsBuffer(isolate, &destbuf, result_length * sizeof(**destbuf));
224
1
  } else if (*status == U_BUFFER_OVERFLOW_ERROR) {
225
1
    *status = U_ZERO_ERROR;
226
2
    destbuf.AllocateSufficientStorage(result_length);
227
1
    u_strFromUTF8(*destbuf, result_length, &result_length,
228
2
                  source, source_length, status);
229
1
    if (U_SUCCESS(*status))
230
1
      ret = AsBuffer(isolate, &destbuf, result_length * sizeof(**destbuf));
231
  }
232
4
  return ret;
233
}
234
235
2
MaybeLocal<Object> TranscodeUtf8FromUcs2(Isolate* isolate,
236
                                         const char* fromEncoding,
237
                                         const char* toEncoding,
238
                                         const char* source,
239
                                         const size_t source_length,
240
                                         UErrorCode* status) {
241
2
  *status = U_ZERO_ERROR;
242
2
  MaybeLocal<Object> ret;
243
2
  const size_t length_in_chars = source_length / sizeof(UChar);
244
  int32_t result_length;
245
4
  MaybeStackBuffer<UChar> sourcebuf;
246
4
  MaybeStackBuffer<char, kStorageSize> destbuf;
247
2
  CopySourceBuffer(&sourcebuf, source, source_length, length_in_chars);
248
4
  u_strToUTF8(*destbuf, kStorageSize, &result_length,
249
4
              *sourcebuf, length_in_chars, status);
250
2
  if (U_SUCCESS(*status)) {
251
1
    ret = AsBuffer(isolate, &destbuf, result_length);
252
1
  } else if (*status == U_BUFFER_OVERFLOW_ERROR) {
253
1
    *status = U_ZERO_ERROR;
254
2
    destbuf.AllocateSufficientStorage(result_length);
255
2
    u_strToUTF8(*destbuf, result_length, &result_length, *sourcebuf,
256
2
                length_in_chars, status);
257
1
    if (U_SUCCESS(*status)) {
258
1
      ret = Buffer::New(isolate, *destbuf, result_length);
259
      destbuf.Release();
260
    }
261
  }
262
4
  return ret;
263
}
264
265
const char* EncodingName(const enum encoding encoding) {
266
  switch (encoding) {
267
    case ASCII: return "us-ascii";
268
    case LATIN1: return "iso8859-1";
269
    case UCS2: return "utf16le";
270
    case UTF8: return "utf-8";
271
    default: return NULL;
272
  }
273
}
274
275
bool SupportedEncoding(const enum encoding encoding) {
276
  switch (encoding) {
277
    case ASCII:
278
    case LATIN1:
279
    case UCS2:
280
    case UTF8: return true;
281
    default: return false;
282
  }
283
}
284
285
8
void Transcode(const FunctionCallbackInfo<Value>&args) {
286
8
  Environment* env = Environment::GetCurrent(args);
287
8
  Isolate* isolate = env->isolate();
288
8
  UErrorCode status = U_ZERO_ERROR;
289
8
  MaybeLocal<Object> result;
290
291
8
  THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
292

32
  SPREAD_BUFFER_ARG(args[0], ts_obj);
293
8
  const enum encoding fromEncoding = ParseEncoding(isolate, args[1], BUFFER);
294

8
  const enum encoding toEncoding = ParseEncoding(isolate, args[2], BUFFER);
295
296


8
  if (SupportedEncoding(fromEncoding) && SupportedEncoding(toEncoding)) {
297
6
    TranscodeFunc tfn = &Transcode;
298

6
    switch (fromEncoding) {
299
      case ASCII:
300
      case LATIN1:
301
        if (toEncoding == UCS2)
302
          tfn = &TranscodeToUcs2;
303
        break;
304
      case UTF8:
305
4
        if (toEncoding == UCS2)
306
2
          tfn = &TranscodeUcs2FromUtf8;
307
        break;
308
      case UCS2:
309
2
        switch (toEncoding) {
310
          case UCS2:
311
            tfn = &Transcode;
312
            break;
313
          case UTF8:
314
2
            tfn = &TranscodeUtf8FromUcs2;
315
2
            break;
316
          default:
317
            tfn = TranscodeFromUcs2;
318
        }
319
        break;
320
      default:
321
        // This should not happen because of the SupportedEncoding checks
322
        ABORT();
323
    }
324
325
    result = tfn(isolate, EncodingName(fromEncoding), EncodingName(toEncoding),
326
6
                 ts_obj_data, ts_obj_length, &status);
327
  } else {
328
2
    status = U_ILLEGAL_ARGUMENT_ERROR;
329
  }
330
331
8
  if (result.IsEmpty())
332
6
    return args.GetReturnValue().Set(status);
333
334
12
  return args.GetReturnValue().Set(result.ToLocalChecked());
335
}
336
337
2
static void ICUErrorName(const FunctionCallbackInfo<Value>& args) {
338
2
  Environment* env = Environment::GetCurrent(args);
339
2
  UErrorCode status = static_cast<UErrorCode>(args[0]->Int32Value());
340
4
  args.GetReturnValue().Set(
341
2
      String::NewFromUtf8(env->isolate(),
342
                          u_errorName(status),
343
4
                          v8::NewStringType::kNormal).ToLocalChecked());
344
2
}
345
346
#define TYPE_ICU "icu"
347
#define TYPE_UNICODE "unicode"
348
#define TYPE_CLDR "cldr"
349
#define TYPE_TZ "tz"
350
351
/**
352
 * This is the workhorse function that deals with the actual version info.
353
 * Get an ICU version.
354
 * @param type the type of version to get. One of VERSION_TYPES
355
 * @param buf optional buffer for result
356
 * @param status ICU error status. If failure, assume result is undefined.
357
 * @return version number, or NULL. May or may not be buf.
358
 */
359
5
static const char* GetVersion(const char* type,
360
                              char buf[U_MAX_VERSION_STRING_LENGTH],
361
                              UErrorCode* status) {
362
5
  if (!strcmp(type, TYPE_ICU)) {
363
    return U_ICU_VERSION;
364
3
  } else if (!strcmp(type, TYPE_UNICODE)) {
365
    return U_UNICODE_VERSION;
366
2
  } else if (!strcmp(type, TYPE_TZ)) {
367
1
    return TimeZone::getTZDataVersion(*status);
368
1
  } else if (!strcmp(type, TYPE_CLDR)) {
369
    UVersionInfo versionArray;
370
1
    ulocdata_getCLDRVersion(versionArray, status);
371
1
    if (U_SUCCESS(*status)) {
372
1
      u_versionToString(versionArray, buf);
373
1
      return buf;
374
    }
375
  }
376
  // Fall through - unknown type or error case
377
  return nullptr;
378
}
379
380
1703
static void GetVersion(const FunctionCallbackInfo<Value>& args) {
381
1703
  Environment* env = Environment::GetCurrent(args);
382
1703
  if ( args.Length() == 0 ) {
383
    // With no args - return a comma-separated list of allowed values
384
3396
      args.GetReturnValue().Set(
385
          String::NewFromUtf8(env->isolate(),
386
            TYPE_ICU ","
387
            TYPE_UNICODE ","
388
            TYPE_CLDR ","
389
            TYPE_TZ));
390
  } else {
391
5
    CHECK_GE(args.Length(), 1);
392
10
    CHECK(args[0]->IsString());
393
10
    Utf8Value val(env->isolate(), args[0]);
394
5
    UErrorCode status = U_ZERO_ERROR;
395
5
    char buf[U_MAX_VERSION_STRING_LENGTH] = "";  // Possible output buffer.
396
5
    const char* versionString = GetVersion(*val, buf, &status);
397
398

5
    if (U_SUCCESS(status) && versionString) {
399
      // Success.
400
10
      args.GetReturnValue().Set(
401
          String::NewFromUtf8(env->isolate(),
402
          versionString));
403
    }
404
  }
405
1703
}
406
407
1702
bool InitializeICUDirectory(const char* icu_data_path) {
408
1702
  if (icu_data_path != nullptr) {
409
    flag_icu_data_dir = true;
410
    u_setDataDirectory(icu_data_path);
411
    return true;  // no error
412
  } else {
413
1702
    UErrorCode status = U_ZERO_ERROR;
414
#ifdef NODE_HAVE_SMALL_ICU
415
    // install the 'small' data.
416
1702
    udata_setCommonData(&SMALL_ICUDATA_ENTRY_POINT, &status);
417
#else  // !NODE_HAVE_SMALL_ICU
418
    // no small data, so nothing to do.
419
#endif  // !NODE_HAVE_SMALL_ICU
420
1702
    return (status == U_ZERO_ERROR);
421
  }
422
}
423
424
45
int32_t ToUnicode(MaybeStackBuffer<char>* buf,
425
                  const char* input,
426
                  size_t length) {
427
45
  UErrorCode status = U_ZERO_ERROR;
428
45
  uint32_t options = UIDNA_DEFAULT;
429
45
  options |= UIDNA_NONTRANSITIONAL_TO_UNICODE;
430
45
  UIDNA* uidna = uidna_openUTS46(options, &status);
431
45
  if (U_FAILURE(status))
432
    return -1;
433
45
  UIDNAInfo info = UIDNA_INFO_INITIALIZER;
434
435
45
  int32_t len = uidna_nameToUnicodeUTF8(uidna,
436
                                        input, length,
437
45
                                        **buf, buf->length(),
438
                                        &info,
439
45
                                        &status);
440
441
45
  if (status == U_BUFFER_OVERFLOW_ERROR) {
442
45
    status = U_ZERO_ERROR;
443
90
    buf->AllocateSufficientStorage(len);
444
45
    len = uidna_nameToUnicodeUTF8(uidna,
445
                                  input, length,
446
45
                                  **buf, buf->length(),
447
                                  &info,
448
45
                                  &status);
449
  }
450
451
45
  if (U_FAILURE(status))
452
    len = -1;
453
454
45
  uidna_close(uidna);
455
45
  return len;
456
}
457
458
2961
int32_t ToASCII(MaybeStackBuffer<char>* buf,
459
                const char* input,
460
                size_t length) {
461
2961
  UErrorCode status = U_ZERO_ERROR;
462
2961
  uint32_t options = UIDNA_DEFAULT;
463
2961
  options |= UIDNA_NONTRANSITIONAL_TO_ASCII;
464
2961
  UIDNA* uidna = uidna_openUTS46(options, &status);
465
2961
  if (U_FAILURE(status))
466
    return -1;
467
2961
  UIDNAInfo info = UIDNA_INFO_INITIALIZER;
468
469
2961
  int32_t len = uidna_nameToASCII_UTF8(uidna,
470
                                       input, length,
471
2961
                                       **buf, buf->length(),
472
                                       &info,
473
2961
                                       &status);
474
475
2961
  if (status == U_BUFFER_OVERFLOW_ERROR) {
476
2630
    status = U_ZERO_ERROR;
477
5260
    buf->AllocateSufficientStorage(len);
478
2630
    len = uidna_nameToASCII_UTF8(uidna,
479
                                 input, length,
480
2630
                                 **buf, buf->length(),
481
                                 &info,
482
2630
                                 &status);
483
  }
484
485
2961
  if (U_FAILURE(status))
486
    len = -1;
487
488
2961
  uidna_close(uidna);
489
2961
  return len;
490
}
491
492
45
static void ToUnicode(const FunctionCallbackInfo<Value>& args) {
493
45
  Environment* env = Environment::GetCurrent(args);
494
45
  CHECK_GE(args.Length(), 1);
495
90
  CHECK(args[0]->IsString());
496
90
  Utf8Value val(env->isolate(), args[0]);
497
90
  MaybeStackBuffer<char> buf;
498
45
  int32_t len = ToUnicode(&buf, *val, val.length());
499
500
45
  if (len < 0) {
501
    return env->ThrowError("Cannot convert name to Unicode");
502
  }
503
504
90
  args.GetReturnValue().Set(
505
90
      String::NewFromUtf8(env->isolate(),
506
45
                          *buf,
507
                          v8::NewStringType::kNormal,
508
90
                          len).ToLocalChecked());
509
}
510
511
2518
static void ToASCII(const FunctionCallbackInfo<Value>& args) {
512
2518
  Environment* env = Environment::GetCurrent(args);
513
2518
  CHECK_GE(args.Length(), 1);
514
5036
  CHECK(args[0]->IsString());
515
5036
  Utf8Value val(env->isolate(), args[0]);
516
5036
  MaybeStackBuffer<char> buf;
517
2518
  int32_t len = ToASCII(&buf, *val, val.length());
518
519
2518
  if (len < 0) {
520
    return env->ThrowError("Cannot convert name to ASCII");
521
  }
522
523
5036
  args.GetReturnValue().Set(
524
5036
      String::NewFromUtf8(env->isolate(),
525
2518
                          *buf,
526
                          v8::NewStringType::kNormal,
527
5036
                          len).ToLocalChecked());
528
}
529
530
// This is similar to wcwidth except that it takes the current unicode
531
// character properties database into consideration, allowing it to
532
// correctly calculate the column widths of things like emoji's and
533
// newer wide characters. wcwidth, on the other hand, uses a fixed
534
// algorithm that does not take things like emoji into proper
535
// consideration.
536
9421
static int GetColumnWidth(UChar32 codepoint,
537
                          bool ambiguous_as_full_width = false) {
538

28263
  if (!u_isdefined(codepoint) ||
539
18818
      u_iscntrl(codepoint) ||
540

28214
      u_getCombiningClass(codepoint) > 0 ||
541
9396
      u_hasBinaryProperty(codepoint, UCHAR_EMOJI_MODIFIER)) {
542
    return 0;
543
  }
544
  // UCHAR_EAST_ASIAN_WIDTH is the Unicode property that identifies a
545
  // codepoint as being full width, wide, ambiguous, neutral, narrow,
546
  // or halfwidth.
547
9395
  const int eaw = u_getIntPropertyValue(codepoint, UCHAR_EAST_ASIAN_WIDTH);
548

9395
  switch (eaw) {
549
    case U_EA_FULLWIDTH:
550
    case U_EA_WIDE:
551
      return 2;
552
    case U_EA_AMBIGUOUS:
553
      // See: http://www.unicode.org/reports/tr11/#Ambiguous for details
554
2
      if (ambiguous_as_full_width) {
555
        return 2;
556
      }
557
      // Fall through if ambiguous_as_full_width if false.
558
    case U_EA_NEUTRAL:
559
7
      if (u_hasBinaryProperty(codepoint, UCHAR_EMOJI_PRESENTATION)) {
560
        return 2;
561
      }
562
      // Fall through
563
    case U_EA_HALFWIDTH:
564
    case U_EA_NARROW:
565
    default:
566
      return 1;
567
  }
568
}
569
570
// Returns the column width for the given String.
571
9341
static void GetStringWidth(const FunctionCallbackInfo<Value>& args) {
572
9341
  Environment* env = Environment::GetCurrent(args);
573
9341
  if (args.Length() < 1)
574
9307
    return;
575
576
9341
  bool ambiguous_as_full_width = args[1]->BooleanValue();
577
9341
  bool expand_emoji_sequence = args[2]->BooleanValue();
578
579
9341
  if (args[0]->IsNumber()) {
580
27921
    args.GetReturnValue().Set(
581
9307
        GetColumnWidth(args[0]->Uint32Value(),
582
9307
                       ambiguous_as_full_width));
583
9307
    return;
584
  }
585
586
68
  TwoByteValue value(env->isolate(), args[0]);
587
  // reinterpret_cast is required by windows to compile
588
34
  UChar* str = reinterpret_cast<UChar*>(*value);
589
  static_assert(sizeof(*str) == sizeof(**value),
590
                "sizeof(*str) == sizeof(**value)");
591
34
  UChar32 c = 0;
592
  UChar32 p;
593
34
  size_t n = 0;
594
34
  uint32_t width = 0;
595
596
151
  while (n < value.length()) {
597
117
    p = c;
598


117
    U16_NEXT(str, n, value.length(), c);
599
    // Don't count individual emoji codepoints that occur within an
600
    // emoji sequence. This is not necessarily foolproof. Some
601
    // environments display emoji sequences in the appropriate
602
    // condensed form (as a single emoji glyph), other environments
603
    // may not understand an emoji sequence and will display each
604
    // individual emoji separately. When this happens, the width
605
    // calculated will be off, and there's no reliable way of knowing
606
    // in advance if a particular sequence is going to be supported.
607
    // The expand_emoji_sequence option allows the caller to skip this
608
    // check and count each code within an emoji sequence separately.
609
117
    if (!expand_emoji_sequence &&
610

120
        n > 0 && p == 0x200d &&  // 0x200d == ZWJ (zero width joiner)
611
3
        (u_hasBinaryProperty(c, UCHAR_EMOJI_PRESENTATION) ||
612
         u_hasBinaryProperty(c, UCHAR_EMOJI_MODIFIER))) {
613
      continue;
614
    }
615
114
    width += GetColumnWidth(c, ambiguous_as_full_width);
616
  }
617
68
  args.GetReturnValue().Set(width);
618
}
619
620
1698
void Init(Local<Object> target,
621
          Local<Value> unused,
622
          Local<Context> context,
623
          void* priv) {
624
1698
  Environment* env = Environment::GetCurrent(context);
625
1698
  env->SetMethod(target, "toUnicode", ToUnicode);
626
1698
  env->SetMethod(target, "toASCII", ToASCII);
627
1698
  env->SetMethod(target, "getStringWidth", GetStringWidth);
628
1698
  env->SetMethod(target, "getVersion", GetVersion);
629
630
  // One-shot converters
631
1698
  env->SetMethod(target, "icuErrName", ICUErrorName);
632
1698
  env->SetMethod(target, "transcode", Transcode);
633
1698
}
634
635
}  // namespace i18n
636
}  // namespace node
637
638
1705
NODE_MODULE_CONTEXT_AWARE_BUILTIN(icu, node::i18n::Init)
639
640
#endif  // NODE_HAVE_I18N_SUPPORT