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: https://github.com/nodejs/node/pull/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>
This commit is contained in:
Joyee Cheung 2018-12-19 05:23:47 +08:00
parent a29c93a147
commit 263d13766f
No known key found for this signature in database
GPG Key ID: 92B78A53C8303B8D
5 changed files with 61 additions and 85 deletions

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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