~yolanda.robla/ubuntu/trusty/nodejs/add_distribution

« back to all changes in this revision

Viewing changes to deps/v8/src/extensions/experimental/number-format.cc

  • Committer: Package Import Robot
  • Author(s): Jérémy Lal
  • Date: 2013-08-14 00:16:46 UTC
  • mfrom: (7.1.40 sid)
  • Revision ID: package-import@ubuntu.com-20130814001646-bzlysfh8sd6mukbo
Tags: 0.10.15~dfsg1-4
* Update 2005 patch, adding a handful of tests that can fail on
  slow platforms.
* Add 1004 patch to fix test failures when writing NaN to buffer
  on mipsel.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright 2011 the V8 project authors. All rights reserved.
2
 
// Redistribution and use in source and binary forms, with or without
3
 
// modification, are permitted provided that the following conditions are
4
 
// met:
5
 
//
6
 
//     * Redistributions of source code must retain the above copyright
7
 
//       notice, this list of conditions and the following disclaimer.
8
 
//     * Redistributions in binary form must reproduce the above
9
 
//       copyright notice, this list of conditions and the following
10
 
//       disclaimer in the documentation and/or other materials provided
11
 
//       with the distribution.
12
 
//     * Neither the name of Google Inc. nor the names of its
13
 
//       contributors may be used to endorse or promote products derived
14
 
//       from this software without specific prior written permission.
15
 
//
16
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 
 
28
 
#include "src/extensions/experimental/number-format.h"
29
 
 
30
 
#include <string.h>
31
 
 
32
 
#include "src/extensions/experimental/i18n-utils.h"
33
 
#include "unicode/dcfmtsym.h"
34
 
#include "unicode/decimfmt.h"
35
 
#include "unicode/locid.h"
36
 
#include "unicode/numfmt.h"
37
 
#include "unicode/uchar.h"
38
 
#include "unicode/ucurr.h"
39
 
#include "unicode/unum.h"
40
 
#include "unicode/uversion.h"
41
 
 
42
 
namespace v8 {
43
 
namespace internal {
44
 
 
45
 
const int NumberFormat::kCurrencyCodeLength = 4;
46
 
 
47
 
v8::Persistent<v8::FunctionTemplate> NumberFormat::number_format_template_;
48
 
 
49
 
static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String>,
50
 
                                              v8::Handle<v8::String>,
51
 
                                              v8::Handle<v8::Object>);
52
 
static icu::DecimalFormat* CreateFormatterFromSkeleton(
53
 
    const icu::Locale&, const icu::UnicodeString&, UErrorCode*);
54
 
static icu::DecimalFormatSymbols* GetFormatSymbols(const icu::Locale&);
55
 
static bool GetCurrencyCode(const icu::Locale&,
56
 
                            const char* const,
57
 
                            v8::Handle<v8::Object>,
58
 
                            UChar*);
59
 
static v8::Handle<v8::Value> ThrowUnexpectedObjectError();
60
 
 
61
 
icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
62
 
    v8::Handle<v8::Object> obj) {
63
 
  if (number_format_template_->HasInstance(obj)) {
64
 
    return static_cast<icu::DecimalFormat*>(
65
 
        obj->GetPointerFromInternalField(0));
66
 
  }
67
 
 
68
 
  return NULL;
69
 
}
70
 
 
71
 
void NumberFormat::DeleteNumberFormat(v8::Persistent<v8::Value> object,
72
 
                                      void* param) {
73
 
  v8::Persistent<v8::Object> persistent_object =
74
 
      v8::Persistent<v8::Object>::Cast(object);
75
 
 
76
 
  // First delete the hidden C++ object.
77
 
  // Unpacking should never return NULL here. That would only happen if
78
 
  // this method is used as the weak callback for persistent handles not
79
 
  // pointing to a number formatter.
80
 
  delete UnpackNumberFormat(persistent_object);
81
 
 
82
 
  // Then dispose of the persistent handle to JS object.
83
 
  persistent_object.Dispose();
84
 
}
85
 
 
86
 
v8::Handle<v8::Value> NumberFormat::Format(const v8::Arguments& args) {
87
 
  v8::HandleScope handle_scope;
88
 
 
89
 
  if (args.Length() != 1 || !args[0]->IsNumber()) {
90
 
    // Just return NaN on invalid input.
91
 
    return v8::String::New("NaN");
92
 
  }
93
 
 
94
 
  icu::DecimalFormat* number_format = UnpackNumberFormat(args.Holder());
95
 
  if (!number_format) {
96
 
    return ThrowUnexpectedObjectError();
97
 
  }
98
 
 
99
 
  // ICU will handle actual NaN value properly and return NaN string.
100
 
  icu::UnicodeString result;
101
 
  number_format->format(args[0]->NumberValue(), result);
102
 
 
103
 
  return v8::String::New(
104
 
      reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length());
105
 
}
106
 
 
107
 
v8::Handle<v8::Value> NumberFormat::JSNumberFormat(const v8::Arguments& args) {
108
 
  v8::HandleScope handle_scope;
109
 
 
110
 
  // Expect locale id, region id and settings.
111
 
  if (args.Length() != 3 ||
112
 
      !args[0]->IsString() || !args[1]->IsString() || !args[2]->IsObject()) {
113
 
    return v8::ThrowException(v8::Exception::SyntaxError(
114
 
        v8::String::New("Locale, region and number settings are required.")));
115
 
  }
116
 
 
117
 
  icu::DecimalFormat* number_format = CreateNumberFormat(
118
 
      args[0]->ToString(), args[1]->ToString(), args[2]->ToObject());
119
 
 
120
 
  if (number_format_template_.IsEmpty()) {
121
 
    v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New());
122
 
 
123
 
    raw_template->SetClassName(v8::String::New("v8Locale.NumberFormat"));
124
 
 
125
 
    // Define internal field count on instance template.
126
 
    v8::Local<v8::ObjectTemplate> object_template =
127
 
        raw_template->InstanceTemplate();
128
 
 
129
 
    // Set aside internal field for icu number formatter.
130
 
    object_template->SetInternalFieldCount(1);
131
 
 
132
 
    // Define all of the prototype methods on prototype template.
133
 
    v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate();
134
 
    proto->Set(v8::String::New("format"),
135
 
               v8::FunctionTemplate::New(Format));
136
 
 
137
 
    number_format_template_ =
138
 
        v8::Persistent<v8::FunctionTemplate>::New(raw_template);
139
 
  }
140
 
 
141
 
  // Create an empty object wrapper.
142
 
  v8::Local<v8::Object> local_object =
143
 
      number_format_template_->GetFunction()->NewInstance();
144
 
  v8::Persistent<v8::Object> wrapper =
145
 
      v8::Persistent<v8::Object>::New(local_object);
146
 
 
147
 
  // Set number formatter as internal field of the resulting JS object.
148
 
  wrapper->SetPointerInInternalField(0, number_format);
149
 
 
150
 
  // Create options key.
151
 
  v8::Local<v8::Object> options = v8::Object::New();
152
 
 
153
 
  // Show what ICU decided to use for easier problem tracking.
154
 
  // Keep it as v8 specific extension.
155
 
  icu::UnicodeString pattern;
156
 
  number_format->toPattern(pattern);
157
 
  options->Set(v8::String::New("v8ResolvedPattern"),
158
 
               v8::String::New(reinterpret_cast<const uint16_t*>(
159
 
                   pattern.getBuffer()), pattern.length()));
160
 
 
161
 
  // Set resolved currency code in options.currency if not empty.
162
 
  icu::UnicodeString currency(number_format->getCurrency());
163
 
  if (!currency.isEmpty()) {
164
 
    options->Set(v8::String::New("currencyCode"),
165
 
                 v8::String::New(reinterpret_cast<const uint16_t*>(
166
 
                     currency.getBuffer()), currency.length()));
167
 
  }
168
 
 
169
 
  wrapper->Set(v8::String::New("options"), options);
170
 
 
171
 
  // Make object handle weak so we can delete iterator once GC kicks in.
172
 
  wrapper.MakeWeak(NULL, DeleteNumberFormat);
173
 
 
174
 
  return wrapper;
175
 
}
176
 
 
177
 
// Returns DecimalFormat.
178
 
static icu::DecimalFormat* CreateNumberFormat(v8::Handle<v8::String> locale,
179
 
                                              v8::Handle<v8::String> region,
180
 
                                              v8::Handle<v8::Object> settings) {
181
 
  v8::HandleScope handle_scope;
182
 
 
183
 
  v8::String::AsciiValue ascii_locale(locale);
184
 
  icu::Locale icu_locale(*ascii_locale);
185
 
 
186
 
  // Make formatter from skeleton.
187
 
  icu::DecimalFormat* number_format = NULL;
188
 
  UErrorCode status = U_ZERO_ERROR;
189
 
  icu::UnicodeString setting;
190
 
 
191
 
  if (I18NUtils::ExtractStringSetting(settings, "skeleton", &setting)) {
192
 
    // TODO(cira): Use ICU skeleton once
193
 
    // http://bugs.icu-project.org/trac/ticket/8610 is resolved.
194
 
    number_format = CreateFormatterFromSkeleton(icu_locale, setting, &status);
195
 
  } else if (I18NUtils::ExtractStringSetting(settings, "pattern", &setting)) {
196
 
    number_format =
197
 
        new icu::DecimalFormat(setting, GetFormatSymbols(icu_locale), status);
198
 
  } else if (I18NUtils::ExtractStringSetting(settings, "style", &setting)) {
199
 
    if (setting == UNICODE_STRING_SIMPLE("currency")) {
200
 
      number_format = static_cast<icu::DecimalFormat*>(
201
 
          icu::NumberFormat::createCurrencyInstance(icu_locale, status));
202
 
    } else if (setting == UNICODE_STRING_SIMPLE("percent")) {
203
 
      number_format = static_cast<icu::DecimalFormat*>(
204
 
          icu::NumberFormat::createPercentInstance(icu_locale, status));
205
 
    } else if (setting == UNICODE_STRING_SIMPLE("scientific")) {
206
 
      number_format = static_cast<icu::DecimalFormat*>(
207
 
          icu::NumberFormat::createScientificInstance(icu_locale, status));
208
 
    } else {
209
 
      // Make it decimal in any other case.
210
 
      number_format = static_cast<icu::DecimalFormat*>(
211
 
          icu::NumberFormat::createInstance(icu_locale, status));
212
 
    }
213
 
  }
214
 
 
215
 
  if (U_FAILURE(status)) {
216
 
    delete number_format;
217
 
    status = U_ZERO_ERROR;
218
 
    number_format = static_cast<icu::DecimalFormat*>(
219
 
        icu::NumberFormat::createInstance(icu_locale, status));
220
 
  }
221
 
 
222
 
  // Attach appropriate currency code to the formatter.
223
 
  // It affects currency formatters only.
224
 
  // Region is full language identifier in form 'und_' + region id.
225
 
  v8::String::AsciiValue ascii_region(region);
226
 
 
227
 
  UChar currency_code[NumberFormat::kCurrencyCodeLength];
228
 
  if (GetCurrencyCode(icu_locale, *ascii_region, settings, currency_code)) {
229
 
    number_format->setCurrency(currency_code, status);
230
 
  }
231
 
 
232
 
  return number_format;
233
 
}
234
 
 
235
 
// Generates ICU number format pattern from given skeleton.
236
 
// TODO(cira): Remove once ICU includes equivalent method
237
 
// (see http://bugs.icu-project.org/trac/ticket/8610).
238
 
static icu::DecimalFormat* CreateFormatterFromSkeleton(
239
 
    const icu::Locale& icu_locale,
240
 
    const icu::UnicodeString& skeleton,
241
 
    UErrorCode* status) {
242
 
  icu::DecimalFormat skeleton_format(
243
 
      skeleton, GetFormatSymbols(icu_locale), *status);
244
 
 
245
 
  // Find out if skeleton contains currency or percent symbol and create
246
 
  // proper instance to tweak.
247
 
  icu::DecimalFormat* base_format = NULL;
248
 
 
249
 
  // UChar representation of U+00A4 currency symbol.
250
 
  const UChar currency_symbol = 0xA4u;
251
 
 
252
 
  int32_t index = skeleton.indexOf(currency_symbol);
253
 
  if (index != -1) {
254
 
    // Find how many U+00A4 are there. There is at least one.
255
 
    // Case of non-consecutive U+00A4 is taken care of in i18n.js.
256
 
    int32_t end_index = skeleton.lastIndexOf(currency_symbol, index);
257
 
 
258
 
#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
259
 
    icu::NumberFormat::EStyles style;
260
 
    switch (end_index - index) {
261
 
      case 0:
262
 
        style = icu::NumberFormat::kCurrencyStyle;
263
 
        break;
264
 
      case 1:
265
 
        style = icu::NumberFormat::kIsoCurrencyStyle;
266
 
        break;
267
 
      default:
268
 
        style = icu::NumberFormat::kPluralCurrencyStyle;
269
 
    }
270
 
#else  // ICU version is 4.8 or above (we ignore versions below 4.0).
271
 
    UNumberFormatStyle style;
272
 
    switch (end_index - index) {
273
 
      case 0:
274
 
        style = UNUM_CURRENCY;
275
 
        break;
276
 
      case 1:
277
 
        style = UNUM_CURRENCY_ISO;
278
 
        break;
279
 
      default:
280
 
        style = UNUM_CURRENCY_PLURAL;
281
 
    }
282
 
#endif
283
 
 
284
 
    base_format = static_cast<icu::DecimalFormat*>(
285
 
        icu::NumberFormat::createInstance(icu_locale, style, *status));
286
 
  } else if (skeleton.indexOf('%') != -1) {
287
 
    base_format = static_cast<icu::DecimalFormat*>(
288
 
        icu::NumberFormat::createPercentInstance(icu_locale, *status));
289
 
  } else {
290
 
    // TODO(cira): Handle scientific skeleton.
291
 
    base_format = static_cast<icu::DecimalFormat*>(
292
 
        icu::NumberFormat::createInstance(icu_locale, *status));
293
 
  }
294
 
 
295
 
  if (U_FAILURE(*status)) {
296
 
    delete base_format;
297
 
    return NULL;
298
 
  }
299
 
 
300
 
  // Copy important information from skeleton to the new formatter.
301
 
  // TODO(cira): copy rounding information from skeleton?
302
 
  base_format->setGroupingUsed(skeleton_format.isGroupingUsed());
303
 
 
304
 
  base_format->setMinimumIntegerDigits(
305
 
      skeleton_format.getMinimumIntegerDigits());
306
 
 
307
 
  base_format->setMinimumFractionDigits(
308
 
      skeleton_format.getMinimumFractionDigits());
309
 
 
310
 
  base_format->setMaximumFractionDigits(
311
 
      skeleton_format.getMaximumFractionDigits());
312
 
 
313
 
  return base_format;
314
 
}
315
 
 
316
 
// Gets decimal symbols for a locale.
317
 
static icu::DecimalFormatSymbols* GetFormatSymbols(
318
 
    const icu::Locale& icu_locale) {
319
 
  UErrorCode status = U_ZERO_ERROR;
320
 
  icu::DecimalFormatSymbols* symbols =
321
 
      new icu::DecimalFormatSymbols(icu_locale, status);
322
 
 
323
 
  if (U_FAILURE(status)) {
324
 
    delete symbols;
325
 
    // Use symbols from default locale.
326
 
    symbols = new icu::DecimalFormatSymbols(status);
327
 
  }
328
 
 
329
 
  return symbols;
330
 
}
331
 
 
332
 
// Gets currency ISO 4217 3-letter code.
333
 
// Check currencyCode setting first, then @currency=code and in the end
334
 
// try to infer currency code from locale in the form 'und_' + region id.
335
 
// Returns false in case of error.
336
 
static bool GetCurrencyCode(const icu::Locale& icu_locale,
337
 
                            const char* const und_region_locale,
338
 
                            v8::Handle<v8::Object> settings,
339
 
                            UChar* code) {
340
 
  UErrorCode status = U_ZERO_ERROR;
341
 
 
342
 
  // If there is user specified currency code, use it.
343
 
  icu::UnicodeString currency;
344
 
  if (I18NUtils::ExtractStringSetting(settings, "currencyCode", &currency)) {
345
 
    currency.extract(code, NumberFormat::kCurrencyCodeLength, status);
346
 
    return true;
347
 
  }
348
 
 
349
 
  // If ICU locale has -cu- currency code use it.
350
 
  char currency_code[NumberFormat::kCurrencyCodeLength];
351
 
  int32_t length = icu_locale.getKeywordValue(
352
 
      "currency", currency_code, NumberFormat::kCurrencyCodeLength, status);
353
 
  if (length != 0) {
354
 
    I18NUtils::AsciiToUChar(currency_code, length + 1,
355
 
                            code, NumberFormat::kCurrencyCodeLength);
356
 
    return true;
357
 
  }
358
 
 
359
 
  // Otherwise infer currency code from the region id.
360
 
  ucurr_forLocale(
361
 
      und_region_locale, code, NumberFormat::kCurrencyCodeLength, &status);
362
 
 
363
 
  return !!U_SUCCESS(status);
364
 
}
365
 
 
366
 
// Throws a JavaScript exception.
367
 
static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
368
 
  // Returns undefined, and schedules an exception to be thrown.
369
 
  return v8::ThrowException(v8::Exception::Error(
370
 
      v8::String::New("NumberFormat method called on an object "
371
 
                      "that is not a NumberFormat.")));
372
 
}
373
 
 
374
 
} }  // namespace v8::internal