src: factor out some common vm functions
PR-URL: https://github.com/nodejs/node/pull/17560 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
This commit is contained in:
parent
3bf34f27a1
commit
2033a9f436
@ -586,6 +586,227 @@ void ContextifyContext::IndexedPropertyDeleterCallback(
|
|||||||
args.GetReturnValue().Set(false);
|
args.GetReturnValue().Set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Maybe<bool> GetBreakOnSigintArg(Environment* env,
|
||||||
|
Local<Value> options) {
|
||||||
|
if (options->IsUndefined() || options->IsString()) {
|
||||||
|
return Just(false);
|
||||||
|
}
|
||||||
|
if (!options->IsObject()) {
|
||||||
|
env->ThrowTypeError("options must be an object");
|
||||||
|
return Nothing<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "breakOnSigint");
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(), key);
|
||||||
|
if (maybe_value.IsEmpty())
|
||||||
|
return Nothing<bool>();
|
||||||
|
|
||||||
|
Local<Value> value = maybe_value.ToLocalChecked();
|
||||||
|
return Just(value->IsTrue());
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<int64_t> GetTimeoutArg(Environment* env, Local<Value> options) {
|
||||||
|
if (options->IsUndefined() || options->IsString()) {
|
||||||
|
return Just<int64_t>(-1);
|
||||||
|
}
|
||||||
|
if (!options->IsObject()) {
|
||||||
|
env->ThrowTypeError("options must be an object");
|
||||||
|
return Nothing<int64_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(), env->timeout_string());
|
||||||
|
if (maybe_value.IsEmpty())
|
||||||
|
return Nothing<int64_t>();
|
||||||
|
|
||||||
|
Local<Value> value = maybe_value.ToLocalChecked();
|
||||||
|
if (value->IsUndefined()) {
|
||||||
|
return Just<int64_t>(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<int64_t> timeout = value->IntegerValue(env->context());
|
||||||
|
|
||||||
|
if (timeout.IsJust() && timeout.ToChecked() <= 0) {
|
||||||
|
env->ThrowRangeError("timeout must be a positive number");
|
||||||
|
return Nothing<int64_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<Integer> GetLineOffsetArg(Environment* env,
|
||||||
|
Local<Value> options) {
|
||||||
|
Local<Integer> defaultLineOffset = Integer::New(env->isolate(), 0);
|
||||||
|
|
||||||
|
if (!options->IsObject()) {
|
||||||
|
return defaultLineOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "lineOffset");
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(), key);
|
||||||
|
if (maybe_value.IsEmpty())
|
||||||
|
return MaybeLocal<Integer>();
|
||||||
|
|
||||||
|
Local<Value> value = maybe_value.ToLocalChecked();
|
||||||
|
if (value->IsUndefined())
|
||||||
|
return defaultLineOffset;
|
||||||
|
|
||||||
|
return value->ToInteger(env->context());
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<Integer> GetColumnOffsetArg(Environment* env,
|
||||||
|
Local<Value> options) {
|
||||||
|
Local<Integer> defaultColumnOffset = Integer::New(env->isolate(), 0);
|
||||||
|
|
||||||
|
if (!options->IsObject()) {
|
||||||
|
return defaultColumnOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "columnOffset");
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(), key);
|
||||||
|
if (maybe_value.IsEmpty())
|
||||||
|
return MaybeLocal<Integer>();
|
||||||
|
|
||||||
|
Local<Value> value = maybe_value.ToLocalChecked();
|
||||||
|
if (value->IsUndefined())
|
||||||
|
return defaultColumnOffset;
|
||||||
|
|
||||||
|
return value->ToInteger(env->context());
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<Context> GetContextArg(Environment* env,
|
||||||
|
Local<Value> options) {
|
||||||
|
if (!options->IsObject())
|
||||||
|
return MaybeLocal<Context>();
|
||||||
|
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(),
|
||||||
|
env->vm_parsing_context_symbol());
|
||||||
|
Local<Value> value;
|
||||||
|
if (!maybe_value.ToLocal(&value))
|
||||||
|
return MaybeLocal<Context>();
|
||||||
|
|
||||||
|
if (!value->IsObject()) {
|
||||||
|
if (!value->IsNullOrUndefined()) {
|
||||||
|
env->ThrowTypeError(
|
||||||
|
"contextifiedSandbox argument must be an object.");
|
||||||
|
}
|
||||||
|
return MaybeLocal<Context>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextifyContext* sandbox =
|
||||||
|
ContextifyContext::ContextFromContextifiedSandbox(
|
||||||
|
env, value.As<Object>());
|
||||||
|
if (!sandbox) {
|
||||||
|
env->ThrowTypeError(
|
||||||
|
"sandbox argument must have been converted to a context.");
|
||||||
|
return MaybeLocal<Context>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Context> context = sandbox->context();
|
||||||
|
if (context.IsEmpty())
|
||||||
|
return MaybeLocal<Context>();
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Maybe<bool> GetDisplayErrorsArg(Environment* env,
|
||||||
|
Local<Value> options) {
|
||||||
|
if (options->IsUndefined() || options->IsString()) {
|
||||||
|
return Just(true);
|
||||||
|
}
|
||||||
|
if (!options->IsObject()) {
|
||||||
|
env->ThrowTypeError("options must be an object");
|
||||||
|
return Nothing<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "displayErrors");
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(), key);
|
||||||
|
if (maybe_value.IsEmpty())
|
||||||
|
return Nothing<bool>();
|
||||||
|
|
||||||
|
Local<Value> value = maybe_value.ToLocalChecked();
|
||||||
|
if (value->IsUndefined())
|
||||||
|
return Just(true);
|
||||||
|
|
||||||
|
return value->BooleanValue(env->context());
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<String> GetFilenameArg(Environment* env,
|
||||||
|
Local<Value> options) {
|
||||||
|
Local<String> defaultFilename =
|
||||||
|
FIXED_ONE_BYTE_STRING(env->isolate(), "evalmachine.<anonymous>");
|
||||||
|
|
||||||
|
if (options->IsUndefined()) {
|
||||||
|
return defaultFilename;
|
||||||
|
}
|
||||||
|
if (options->IsString()) {
|
||||||
|
return options.As<String>();
|
||||||
|
}
|
||||||
|
if (!options->IsObject()) {
|
||||||
|
env->ThrowTypeError("options must be an object");
|
||||||
|
return Local<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "filename");
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(), key);
|
||||||
|
if (maybe_value.IsEmpty())
|
||||||
|
return MaybeLocal<String>();
|
||||||
|
|
||||||
|
Local<Value> value = maybe_value.ToLocalChecked();
|
||||||
|
if (value->IsUndefined())
|
||||||
|
return defaultFilename;
|
||||||
|
return value->ToString(env->context());
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<Uint8Array> GetCachedData(Environment* env,
|
||||||
|
Local<Value> options) {
|
||||||
|
if (!options->IsObject()) {
|
||||||
|
return MaybeLocal<Uint8Array>();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(), env->cached_data_string());
|
||||||
|
if (maybe_value.IsEmpty())
|
||||||
|
return MaybeLocal<Uint8Array>();
|
||||||
|
|
||||||
|
Local<Value> value = maybe_value.ToLocalChecked();
|
||||||
|
if (value->IsUndefined()) {
|
||||||
|
return MaybeLocal<Uint8Array>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value->IsUint8Array()) {
|
||||||
|
env->ThrowTypeError("options.cachedData must be a Buffer instance");
|
||||||
|
return MaybeLocal<Uint8Array>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.As<Uint8Array>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<bool> GetProduceCachedData(Environment* env,
|
||||||
|
Local<Value> options) {
|
||||||
|
if (!options->IsObject()) {
|
||||||
|
return Just(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<Value> maybe_value =
|
||||||
|
options.As<Object>()->Get(env->context(),
|
||||||
|
env->produce_cached_data_string());
|
||||||
|
if (maybe_value.IsEmpty())
|
||||||
|
return Nothing<bool>();
|
||||||
|
|
||||||
|
Local<Value> value = maybe_value.ToLocalChecked();
|
||||||
|
return Just(value->IsTrue());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
class ContextifyScript : public BaseObject {
|
class ContextifyScript : public BaseObject {
|
||||||
private:
|
private:
|
||||||
Persistent<UnboundScript> script_;
|
Persistent<UnboundScript> script_;
|
||||||
@ -639,7 +860,7 @@ class ContextifyScript : public BaseObject {
|
|||||||
MaybeLocal<Integer> columnOffset = GetColumnOffsetArg(env, options);
|
MaybeLocal<Integer> columnOffset = GetColumnOffsetArg(env, options);
|
||||||
MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options);
|
MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options);
|
||||||
Maybe<bool> maybe_produce_cached_data = GetProduceCachedData(env, options);
|
Maybe<bool> maybe_produce_cached_data = GetProduceCachedData(env, options);
|
||||||
MaybeLocal<Context> maybe_context = GetContext(env, options);
|
MaybeLocal<Context> maybe_context = GetContextArg(env, options);
|
||||||
if (try_catch.HasCaught()) {
|
if (try_catch.HasCaught()) {
|
||||||
no_abort_scope.Close();
|
no_abort_scope.Close();
|
||||||
try_catch.ReThrow();
|
try_catch.ReThrow();
|
||||||
@ -831,230 +1052,6 @@ class ContextifyScript : public BaseObject {
|
|||||||
True(env->isolate()));
|
True(env->isolate()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Maybe<bool> GetBreakOnSigintArg(Environment* env,
|
|
||||||
Local<Value> options) {
|
|
||||||
if (options->IsUndefined() || options->IsString()) {
|
|
||||||
return Just(false);
|
|
||||||
}
|
|
||||||
if (!options->IsObject()) {
|
|
||||||
env->ThrowTypeError("options must be an object");
|
|
||||||
return Nothing<bool>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "breakOnSigint");
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(), key);
|
|
||||||
if (maybe_value.IsEmpty())
|
|
||||||
return Nothing<bool>();
|
|
||||||
|
|
||||||
Local<Value> value = maybe_value.ToLocalChecked();
|
|
||||||
return Just(value->IsTrue());
|
|
||||||
}
|
|
||||||
|
|
||||||
static Maybe<int64_t> GetTimeoutArg(Environment* env, Local<Value> options) {
|
|
||||||
if (options->IsUndefined() || options->IsString()) {
|
|
||||||
return Just<int64_t>(-1);
|
|
||||||
}
|
|
||||||
if (!options->IsObject()) {
|
|
||||||
env->ThrowTypeError("options must be an object");
|
|
||||||
return Nothing<int64_t>();
|
|
||||||
}
|
|
||||||
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(), env->timeout_string());
|
|
||||||
if (maybe_value.IsEmpty())
|
|
||||||
return Nothing<int64_t>();
|
|
||||||
|
|
||||||
Local<Value> value = maybe_value.ToLocalChecked();
|
|
||||||
if (value->IsUndefined()) {
|
|
||||||
return Just<int64_t>(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Maybe<int64_t> timeout = value->IntegerValue(env->context());
|
|
||||||
|
|
||||||
if (timeout.IsJust() && timeout.ToChecked() <= 0) {
|
|
||||||
env->ThrowRangeError("timeout must be a positive number");
|
|
||||||
return Nothing<int64_t>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static Maybe<bool> GetDisplayErrorsArg(Environment* env,
|
|
||||||
Local<Value> options) {
|
|
||||||
if (options->IsUndefined() || options->IsString()) {
|
|
||||||
return Just(true);
|
|
||||||
}
|
|
||||||
if (!options->IsObject()) {
|
|
||||||
env->ThrowTypeError("options must be an object");
|
|
||||||
return Nothing<bool>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "displayErrors");
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(), key);
|
|
||||||
if (maybe_value.IsEmpty())
|
|
||||||
return Nothing<bool>();
|
|
||||||
|
|
||||||
Local<Value> value = maybe_value.ToLocalChecked();
|
|
||||||
if (value->IsUndefined())
|
|
||||||
return Just(true);
|
|
||||||
|
|
||||||
return value->BooleanValue(env->context());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static MaybeLocal<String> GetFilenameArg(Environment* env,
|
|
||||||
Local<Value> options) {
|
|
||||||
Local<String> defaultFilename =
|
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "evalmachine.<anonymous>");
|
|
||||||
|
|
||||||
if (options->IsUndefined()) {
|
|
||||||
return defaultFilename;
|
|
||||||
}
|
|
||||||
if (options->IsString()) {
|
|
||||||
return options.As<String>();
|
|
||||||
}
|
|
||||||
if (!options->IsObject()) {
|
|
||||||
env->ThrowTypeError("options must be an object");
|
|
||||||
return Local<String>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "filename");
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(), key);
|
|
||||||
if (maybe_value.IsEmpty())
|
|
||||||
return MaybeLocal<String>();
|
|
||||||
|
|
||||||
Local<Value> value = maybe_value.ToLocalChecked();
|
|
||||||
if (value->IsUndefined())
|
|
||||||
return defaultFilename;
|
|
||||||
return value->ToString(env->context());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static MaybeLocal<Uint8Array> GetCachedData(Environment* env,
|
|
||||||
Local<Value> options) {
|
|
||||||
if (!options->IsObject()) {
|
|
||||||
return MaybeLocal<Uint8Array>();
|
|
||||||
}
|
|
||||||
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(), env->cached_data_string());
|
|
||||||
if (maybe_value.IsEmpty())
|
|
||||||
return MaybeLocal<Uint8Array>();
|
|
||||||
|
|
||||||
Local<Value> value = maybe_value.ToLocalChecked();
|
|
||||||
if (value->IsUndefined()) {
|
|
||||||
return MaybeLocal<Uint8Array>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value->IsUint8Array()) {
|
|
||||||
env->ThrowTypeError("options.cachedData must be a Buffer instance");
|
|
||||||
return MaybeLocal<Uint8Array>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value.As<Uint8Array>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static Maybe<bool> GetProduceCachedData(Environment* env,
|
|
||||||
Local<Value> options) {
|
|
||||||
if (!options->IsObject()) {
|
|
||||||
return Just(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(),
|
|
||||||
env->produce_cached_data_string());
|
|
||||||
if (maybe_value.IsEmpty())
|
|
||||||
return Nothing<bool>();
|
|
||||||
|
|
||||||
Local<Value> value = maybe_value.ToLocalChecked();
|
|
||||||
return Just(value->IsTrue());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static MaybeLocal<Integer> GetLineOffsetArg(Environment* env,
|
|
||||||
Local<Value> options) {
|
|
||||||
Local<Integer> defaultLineOffset = Integer::New(env->isolate(), 0);
|
|
||||||
|
|
||||||
if (!options->IsObject()) {
|
|
||||||
return defaultLineOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "lineOffset");
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(), key);
|
|
||||||
if (maybe_value.IsEmpty())
|
|
||||||
return MaybeLocal<Integer>();
|
|
||||||
|
|
||||||
Local<Value> value = maybe_value.ToLocalChecked();
|
|
||||||
if (value->IsUndefined())
|
|
||||||
return defaultLineOffset;
|
|
||||||
|
|
||||||
return value->ToInteger(env->context());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static MaybeLocal<Integer> GetColumnOffsetArg(Environment* env,
|
|
||||||
Local<Value> options) {
|
|
||||||
Local<Integer> defaultColumnOffset = Integer::New(env->isolate(), 0);
|
|
||||||
|
|
||||||
if (!options->IsObject()) {
|
|
||||||
return defaultColumnOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "columnOffset");
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(), key);
|
|
||||||
if (maybe_value.IsEmpty())
|
|
||||||
return MaybeLocal<Integer>();
|
|
||||||
|
|
||||||
Local<Value> value = maybe_value.ToLocalChecked();
|
|
||||||
if (value->IsUndefined())
|
|
||||||
return defaultColumnOffset;
|
|
||||||
|
|
||||||
return value->ToInteger(env->context());
|
|
||||||
}
|
|
||||||
|
|
||||||
static MaybeLocal<Context> GetContext(Environment* env,
|
|
||||||
Local<Value> options) {
|
|
||||||
if (!options->IsObject())
|
|
||||||
return MaybeLocal<Context>();
|
|
||||||
|
|
||||||
MaybeLocal<Value> maybe_value =
|
|
||||||
options.As<Object>()->Get(env->context(),
|
|
||||||
env->vm_parsing_context_symbol());
|
|
||||||
Local<Value> value;
|
|
||||||
if (!maybe_value.ToLocal(&value))
|
|
||||||
return MaybeLocal<Context>();
|
|
||||||
|
|
||||||
if (!value->IsObject()) {
|
|
||||||
if (!value->IsNullOrUndefined()) {
|
|
||||||
env->ThrowTypeError(
|
|
||||||
"contextifiedSandbox argument must be an object.");
|
|
||||||
}
|
|
||||||
return MaybeLocal<Context>();
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextifyContext* sandbox =
|
|
||||||
ContextifyContext::ContextFromContextifiedSandbox(
|
|
||||||
env, value.As<Object>());
|
|
||||||
if (!sandbox) {
|
|
||||||
env->ThrowTypeError(
|
|
||||||
"sandbox argument must have been converted to a context.");
|
|
||||||
return MaybeLocal<Context>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Local<Context> context = sandbox->context();
|
|
||||||
if (context.IsEmpty())
|
|
||||||
return MaybeLocal<Context>();
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool EvalMachine(Environment* env,
|
static bool EvalMachine(Environment* env,
|
||||||
const int64_t timeout,
|
const int64_t timeout,
|
||||||
const bool display_errors,
|
const bool display_errors,
|
||||||
|
@ -92,6 +92,17 @@ class ContextifyContext {
|
|||||||
const v8::PropertyCallbackInfo<v8::Boolean>& args);
|
const v8::PropertyCallbackInfo<v8::Boolean>& args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
v8::Maybe<bool> GetBreakOnSigintArg(
|
||||||
|
Environment* env, v8::Local<v8::Value> options);
|
||||||
|
v8::Maybe<int64_t> GetTimeoutArg(
|
||||||
|
Environment* env, v8::Local<v8::Value> options);
|
||||||
|
v8::MaybeLocal<v8::Integer> GetLineOffsetArg(
|
||||||
|
Environment* env, v8::Local<v8::Value> options);
|
||||||
|
v8::MaybeLocal<v8::Integer> GetColumnOffsetArg(
|
||||||
|
Environment* env, v8::Local<v8::Value> options);
|
||||||
|
v8::MaybeLocal<v8::Context> GetContextArg(
|
||||||
|
Environment* env, v8::Local<v8::Value> options);
|
||||||
|
|
||||||
} // namespace contextify
|
} // namespace contextify
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user