Permalink
Browse files

src: initialize ICU version in per_process::metadata.versions

Instead of

- Initialize the ICU versions in JS land after consulting
  internalBinding('config').hasIntl
- Joining the version keys in C++
- Splitting the keys in JS and call into C++ again to get the value for
  each of the keys

Do:

- Guard the initialization code behind `NODE_HAVE_I18N_SUPPORT`
- Do the initialization in C++ right after ICU data is loaded
- Initialize each version directly using ICU functions/constants,
  and put them in per_process::metadata.versions. These will be
  copied into `process.versions` naturally later.
  This way, the initialization of the versions won't be called
  in worker threads again.

PR-URL: #25115
Reviewed-By: Steven R Loomis <srloomis@us.ibm.com>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Minwoo Jung <minwoo@nodesource.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information...
joyeecheung committed Dec 18, 2018
1 parent a29c93a commit 263d13766f08fb3444c205ce7ecaaa3efd89546a
Showing with 61 additions and 85 deletions.
  1. +0 −21 lib/internal/bootstrap/node.js
  2. +5 −1 src/node.cc
  3. +0 −62 src/node_i18n.cc
  4. +31 −0 src/node_metadata.cc
  5. +25 −1 src/node_metadata.h
@@ -38,8 +38,6 @@ function startup() {
// Do this good and early, since it handles errors.
setupProcessFatal();

setupProcessICUVersions();

setupGlobalVariables();

// Bootstrappers for all threads, including worker threads and main thread
@@ -638,25 +636,6 @@ function setupProcessFatal() {
};
}

function setupProcessICUVersions() {
const icu = internalBinding('config').hasIntl ?
internalBinding('icu') : undefined;
if (!icu) return; // no Intl/ICU: nothing to add here.
// With no argument, getVersion() returns a comma separated list
// of possible types.
const versionTypes = icu.getVersion().split(',');

for (var n = 0; n < versionTypes.length; n++) {
const name = versionTypes[n];
const version = icu.getVersion(name);
Object.defineProperty(process.versions, name, {
writable: false,
enumerable: true,
value: version
});
}
}

function wrapForBreakOnFirstLine(source) {
if (!process._breakFirstLine)
return source;
@@ -877,7 +877,10 @@ void SetupProcessObject(Environment* env,
READONLY_PROPERTY(process, "versions", versions);

#define V(key) \
READONLY_STRING_PROPERTY(versions, #key, per_process::metadata.versions.key);
if (!per_process::metadata.versions.key.empty()) { \
READONLY_STRING_PROPERTY( \
versions, #key, per_process::metadata.versions.key); \
}
NODE_VERSIONS_KEYS(V)
#undef V

@@ -1664,6 +1667,7 @@ void Init(std::vector<std::string>* argv,
argv->at(0).c_str());
exit(9);
}
per_process::metadata.versions.InitializeIntlVersions();
#endif

// We should set node_is_initialized here instead of in node::Start,
@@ -510,67 +510,6 @@ void ICUErrorName(const FunctionCallbackInfo<Value>& args) {
NewStringType::kNormal).ToLocalChecked());
}

#define TYPE_ICU "icu"
#define TYPE_UNICODE "unicode"
#define TYPE_CLDR "cldr"
#define TYPE_TZ "tz"

/**
* This is the workhorse function that deals with the actual version info.
* Get an ICU version.
* @param type the type of version to get. One of VERSION_TYPES
* @param buf optional buffer for result
* @param status ICU error status. If failure, assume result is undefined.
* @return version number, or NULL. May or may not be buf.
*/
const char* GetVersion(const char* type,
char buf[U_MAX_VERSION_STRING_LENGTH],
UErrorCode* status) {
if (!strcmp(type, TYPE_ICU)) {
return U_ICU_VERSION;
} else if (!strcmp(type, TYPE_UNICODE)) {
return U_UNICODE_VERSION;
} else if (!strcmp(type, TYPE_TZ)) {
return icu::TimeZone::getTZDataVersion(*status);
} else if (!strcmp(type, TYPE_CLDR)) {
UVersionInfo versionArray;
ulocdata_getCLDRVersion(versionArray, status);
if (U_SUCCESS(*status)) {
u_versionToString(versionArray, buf);
return buf;
}
}
// Fall through - unknown type or error case
return nullptr;
}

void GetVersion(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if ( args.Length() == 0 ) {
// With no args - return a comma-separated list of allowed values
args.GetReturnValue().Set(
String::NewFromUtf8(env->isolate(),
TYPE_ICU ","
TYPE_UNICODE ","
TYPE_CLDR ","
TYPE_TZ, NewStringType::kNormal).ToLocalChecked());
} else {
CHECK_GE(args.Length(), 1);
CHECK(args[0]->IsString());
Utf8Value val(env->isolate(), args[0]);
UErrorCode status = U_ZERO_ERROR;
char buf[U_MAX_VERSION_STRING_LENGTH] = ""; // Possible output buffer.
const char* versionString = GetVersion(*val, buf, &status);

if (U_SUCCESS(status) && versionString) {
// Success.
args.GetReturnValue().Set(
String::NewFromUtf8(env->isolate(),
versionString, NewStringType::kNormal).ToLocalChecked());
}
}
}

} // anonymous namespace

bool InitializeICUDirectory(const std::string& path) {
@@ -868,7 +807,6 @@ void Initialize(Local<Object> target,
env->SetMethod(target, "toUnicode", ToUnicode);
env->SetMethod(target, "toASCII", ToASCII);
env->SetMethod(target, "getStringWidth", GetStringWidth);
env->SetMethod(target, "getVersion", GetVersion);

// One-shot converters
env->SetMethod(target, "icuErrName", ICUErrorName);
@@ -11,6 +11,13 @@
#include <openssl/opensslv.h>
#endif // HAVE_OPENSSL

#ifdef NODE_HAVE_I18N_SUPPORT
#include <unicode/timezone.h>
#include <unicode/ulocdata.h>
#include <unicode/uvernum.h>
#include <unicode/uversion.h>
#endif // NODE_HAVE_I18N_SUPPORT

namespace node {

namespace per_process {
@@ -34,6 +41,25 @@ std::string GetOpenSSLVersion() {
}
#endif // HAVE_OPENSSL

#ifdef NODE_HAVE_I18N_SUPPORT
void Metadata::Versions::InitializeIntlVersions() {
UErrorCode status = U_ZERO_ERROR;

const char* tz_version = icu::TimeZone::getTZDataVersion(status);
if (U_SUCCESS(status)) {
tz = tz_version;
}

char buf[U_MAX_VERSION_STRING_LENGTH];
UVersionInfo versionArray;
ulocdata_getCLDRVersion(versionArray, &status);
if (U_SUCCESS(status)) {
u_versionToString(versionArray, buf);
cldr = buf;
}
}
#endif // NODE_HAVE_I18N_SUPPORT

Metadata::Versions::Versions() {
node = NODE_VERSION_STRING;
v8 = v8::V8::GetVersion();
@@ -49,6 +75,11 @@ Metadata::Versions::Versions() {
#if HAVE_OPENSSL
openssl = GetOpenSSLVersion();
#endif

#ifdef NODE_HAVE_I18N_SUPPORT
icu = U_ICU_VERSION;
unicode = U_UNICODE_VERSION;
#endif // NODE_HAVE_I18N_SUPPORT
}

} // namespace node
@@ -25,14 +25,38 @@ namespace node {
#define NODE_VERSIONS_KEY_CRYPTO(V)
#endif

#ifdef NODE_HAVE_I18N_SUPPORT
#define NODE_VERSIONS_KEY_INTL(V) \
V(cldr) \
V(icu) \
V(tz) \
V(unicode)
#else
#define NODE_VERSIONS_KEY_INTL(V)
#endif // NODE_HAVE_I18N_SUPPORT

#define NODE_VERSIONS_KEYS(V) \
NODE_VERSIONS_KEYS_BASE(V) \
NODE_VERSIONS_KEY_CRYPTO(V)
NODE_VERSIONS_KEY_CRYPTO(V) \
NODE_VERSIONS_KEY_INTL(V)

class Metadata {
public:
Metadata() = default;
Metadata(Metadata&) = delete;
Metadata(Metadata&&) = delete;
Metadata operator=(Metadata&) = delete;
Metadata operator=(Metadata&&) = delete;

struct Versions {
Versions();

#ifdef NODE_HAVE_I18N_SUPPORT
// Must be called on the main thread after
// i18n::InitializeICUDirectory()
void InitializeIntlVersions();
#endif // NODE_HAVE_I18N_SUPPORT

#define V(key) std::string key;
NODE_VERSIONS_KEYS(V)
#undef V

0 comments on commit 263d137

Please sign in to comment.