Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: simplify exit code accesses #45125

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const {
} = require('internal/validators');
const {
exiting_aliased_Uint32Array,
exit_info_private_symbol,
getHiddenValue,
} = internalBinding('util');

Expand Down Expand Up @@ -108,6 +109,11 @@ process.domain = null;
process._exiting = false;

{
// Must match `Environment::ExitInfo::Fields` in `src/env.h`.
const kExitCode = 0;
const kHasExitCode = 1;
const fields = getHiddenValue(process, exit_info_private_symbol);

let exitCode;

ObjectDefineProperty(process, 'exitCode', {
Expand All @@ -123,6 +129,10 @@ process._exiting = false;
value = code;
}
validateInteger(value, 'code');
fields[kExitCode] = value;
fields[kHasExitCode] = 1;
} else {
fields[kHasExitCode] = 0;
}
exitCode = code;
},
Expand Down
46 changes: 15 additions & 31 deletions src/api/hooks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ using v8::NewStringType;
using v8::Nothing;
using v8::Object;
using v8::String;
using v8::Value;

void RunAtExit(Environment* env) {
env->RunAtExitCallbacks();
Expand All @@ -36,18 +35,13 @@ Maybe<bool> EmitProcessBeforeExit(Environment* env) {
if (!env->destroy_async_id_list()->empty())
AsyncWrap::DestroyAsyncIdsCallback(env);

HandleScope handle_scope(env->isolate());
Local<Context> context = env->context();
Context::Scope context_scope(context);

Local<Value> exit_code_v;
if (!env->process_object()->Get(context, env->exit_code_string())
.ToLocal(&exit_code_v)) return Nothing<bool>();
Isolate* isolate = env->isolate();
HandleScope handle_scope(isolate);
Context::Scope context_scope(env->context());

Local<Integer> exit_code;
if (!exit_code_v->ToInteger(context).ToLocal(&exit_code)) {
return Nothing<bool>();
}
Local<Integer> exit_code = v8::Integer::New(
isolate,
env->maybe_exit_code(static_cast<int32_t>(ExitCode::kNoFailure)));

return ProcessEmit(env, "beforeExit", exit_code).IsEmpty() ?
Nothing<bool>() : Just(true);
Expand All @@ -65,29 +59,19 @@ Maybe<ExitCode> EmitProcessExitInternal(Environment* env) {
// process.emit('exit')
Isolate* isolate = env->isolate();
HandleScope handle_scope(isolate);
Local<Context> context = env->context();
Context::Scope context_scope(context);
Local<Object> process_object = env->process_object();

// TODO(addaleax): It might be nice to share process.exitCode via
// getter/setter pairs that pass data directly to the native side, so that we
// don't manually have to read and write JS properties here. These getters
// could use e.g. a typed array for performance.
Context::Scope context_scope(env->context());

env->set_exiting(true);

Local<String> exit_code = env->exit_code_string();
Local<Value> code_v;
int code;
if (!process_object->Get(context, exit_code).ToLocal(&code_v) ||
!code_v->Int32Value(context).To(&code) ||
ProcessEmit(env, "exit", Integer::New(isolate, code)).IsEmpty() ||
// Reload exit code, it may be changed by `emit('exit')`
!process_object->Get(context, exit_code).ToLocal(&code_v) ||
!code_v->Int32Value(context).To(&code)) {
const int no_failure = static_cast<int32_t>(ExitCode::kNoFailure);

if (ProcessEmit(
env, "exit", Integer::New(isolate, env->maybe_exit_code(no_failure)))
.IsEmpty()) {
return Nothing<ExitCode>();
}

return Just(static_cast<ExitCode>(code));
// Reload exit code, it may be changed by `emit('exit')`
return Just(static_cast<ExitCode>(env->maybe_exit_code(no_failure)));
}

Maybe<int> EmitProcessExit(Environment* env) {
Expand Down
20 changes: 20 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,18 @@ inline bool TickInfo::has_rejection_to_warn() const {
return fields_[kHasRejectionToWarn] == 1;
}

inline const AliasedInt32Array& ExitInfo::fields() {
return fields_;
}

inline bool ExitInfo::has_exit_code() const {
return fields_[kHasExitCode] == 1;
}

inline int32_t ExitInfo::exit_code() const {
return fields_[kExitCode];
}

inline Environment* Environment::GetCurrent(v8::Isolate* isolate) {
if (UNLIKELY(!isolate->InContext())) return nullptr;
v8::HandleScope handle_scope(isolate);
Expand Down Expand Up @@ -327,6 +339,14 @@ inline TickInfo* Environment::tick_info() {
return &tick_info_;
}

inline ExitInfo* Environment::exit_info() {
return &exit_info_;
}

inline int32_t Environment::maybe_exit_code(const int32_t default_code) const {
return exit_info_.has_exit_code() ? exit_info_.exit_code() : default_code;
}

inline uint64_t Environment::timer_base() const {
return timer_base_;
}
Expand Down
27 changes: 27 additions & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@ Environment::Environment(IsolateData* isolate_data,
async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)),
immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)),
tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)),
exit_info_(isolate, MAYBE_FIELD_PTR(env_info, exit_info)),
timer_base_(uv_now(isolate_data->event_loop())),
exec_argv_(exec_args),
argv_(args),
Expand Down Expand Up @@ -1507,6 +1508,29 @@ void AsyncHooks::FailWithCorruptedAsyncStack(double expected_async_id) {
ABORT_NO_BACKTRACE();
}

ExitInfo::SerializeInfo ExitInfo::Serialize(Local<Context> context,
SnapshotCreator* creator) {
return {fields_.Serialize(context, creator)};
}

void ExitInfo::Deserialize(Local<Context> context) {
fields_.Deserialize(context);
}

std::ostream& operator<<(std::ostream& output,
const ExitInfo::SerializeInfo& i) {
output << "{ " << i.fields << " }";
return output;
}

void ExitInfo::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("fields", fields_);
}

ExitInfo::ExitInfo(Isolate* isolate, const SerializeInfo* info)
: fields_(
isolate, kFieldsCount, info == nullptr ? nullptr : &(info->fields)) {}

void Environment::Exit(ExitCode exit_code) {
if (options()->trace_exit) {
HandleScope handle_scope(isolate());
Expand Down Expand Up @@ -1595,6 +1619,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
info.async_hooks = async_hooks_.Serialize(ctx, creator);
info.immediate_info = immediate_info_.Serialize(ctx, creator);
info.tick_info = tick_info_.Serialize(ctx, creator);
info.exit_info = exit_info_.Serialize(ctx, creator);
info.performance_state = performance_state_->Serialize(ctx, creator);
info.exiting = exiting_.Serialize(ctx, creator);
info.stream_base_state = stream_base_state_.Serialize(ctx, creator);
Expand Down Expand Up @@ -1637,6 +1662,7 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
async_hooks_.Deserialize(ctx);
immediate_info_.Deserialize(ctx);
tick_info_.Deserialize(ctx);
exit_info_.Deserialize(ctx);
performance_state_->Deserialize(ctx);
exiting_.Deserialize(ctx);
stream_base_state_.Deserialize(ctx);
Expand Down Expand Up @@ -1833,6 +1859,7 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("async_hooks", async_hooks_);
tracker->TrackField("immediate_info", immediate_info_);
tracker->TrackField("tick_info", tick_info_);
tracker->TrackField("exit_info", exit_info_);
tracker->TrackField("principal_realm", principal_realm_);

// FIXME(joyeecheung): track other fields in Environment.
Expand Down
37 changes: 37 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,38 @@ class TickInfo : public MemoryRetainer {
AliasedUint8Array fields_;
};

class ExitInfo : public MemoryRetainer {
public:
inline const AliasedInt32Array& fields();
inline bool has_exit_code() const;
inline int32_t exit_code() const;

SET_MEMORY_INFO_NAME(ExitInfo)
SET_SELF_SIZE(ExitInfo)
void MemoryInfo(MemoryTracker* tracker) const override;

ExitInfo(const ExitInfo&) = delete;
ExitInfo& operator=(const ExitInfo&) = delete;
ExitInfo(ExitInfo&&) = delete;
ExitInfo& operator=(ExitInfo&&) = delete;
~ExitInfo() = default;

struct SerializeInfo {
AliasedBufferIndex fields;
};
SerializeInfo Serialize(v8::Local<v8::Context> context,
v8::SnapshotCreator* creator);
void Deserialize(v8::Local<v8::Context> context);

enum Fields { kExitCode = 0, kHasExitCode, kFieldsCount };

private:
friend class Environment; // So we can call the constructor.
explicit ExitInfo(v8::Isolate* isolate, const SerializeInfo* info);

AliasedInt32Array fields_;
joyeecheung marked this conversation as resolved.
Show resolved Hide resolved
};

class TrackingTraceStateObserver :
public v8::TracingController::TraceStateObserver {
public:
Expand Down Expand Up @@ -514,6 +546,7 @@ struct EnvSerializeInfo {
TickInfo::SerializeInfo tick_info;
ImmediateInfo::SerializeInfo immediate_info;
performance::PerformanceState::SerializeInfo performance_state;
ExitInfo::SerializeInfo exit_info;
AliasedBufferIndex exiting;
AliasedBufferIndex stream_base_state;
AliasedBufferIndex should_abort_on_uncaught_toggle;
Expand Down Expand Up @@ -726,6 +759,8 @@ class Environment : public MemoryRetainer {
inline AsyncHooks* async_hooks();
inline ImmediateInfo* immediate_info();
inline TickInfo* tick_info();
inline ExitInfo* exit_info();
inline int32_t maybe_exit_code(const int32_t default_code) const;
inline uint64_t timer_base() const;
inline std::shared_ptr<KVStore> env_vars();
inline void set_env_vars(std::shared_ptr<KVStore> env_vars);
Expand Down Expand Up @@ -1054,6 +1089,7 @@ class Environment : public MemoryRetainer {
AsyncHooks async_hooks_;
ImmediateInfo immediate_info_;
TickInfo tick_info_;
ExitInfo exit_info_;
const uint64_t timer_base_;
std::shared_ptr<KVStore> env_vars_;
bool printed_error_ = false;
Expand Down Expand Up @@ -1101,6 +1137,7 @@ class Environment : public MemoryRetainer {
uint32_t script_id_counter_ = 0;
uint32_t function_id_counter_ = 0;

// TODO(daeyeon): merge into `exit_info_`
AliasedUint32Array exiting_;

AliasedUint32Array should_abort_on_uncaught_toggle_;
Expand Down
4 changes: 2 additions & 2 deletions src/env_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
V(napi_type_tag, "node:napi:type_tag") \
V(napi_wrapper, "node:napi:wrapper") \
V(untransferable_object_private_symbol, "node:untransferableObject") \
V(exiting_aliased_Uint32Array, "node:exiting_aliased_Uint32Array")
V(exiting_aliased_Uint32Array, "node:exiting_aliased_Uint32Array") \
V(exit_info_private_symbol, "node:exit_info_private_symbol")

// Symbols are per-isolate primitives but Environment proxies them
// for the sake of convenience.
Expand Down Expand Up @@ -114,7 +115,6 @@
V(errno_string, "errno") \
V(error_string, "error") \
V(exchange_string, "exchange") \
V(exit_code_string, "exitCode") \
V(expire_string, "expire") \
V(exponent_string, "exponent") \
V(exports_string, "exports") \
Expand Down
12 changes: 3 additions & 9 deletions src/node_errors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1151,15 +1151,9 @@ void TriggerUncaughtException(Isolate* isolate,
RunAtExit(env);

// If the global uncaught exception handler sets process.exitCode,
// exit with that code. Otherwise, exit with 1.
Local<String> exit_code = env->exit_code_string();
Local<Value> code;
if (process_object->Get(env->context(), exit_code).ToLocal(&code) &&
code->IsInt32()) {
env->Exit(static_cast<ExitCode>(code.As<Int32>()->Value()));
} else {
env->Exit(ExitCode::kGenericUserError);
}
// exit with that code. Otherwise, exit with `ExitCode::kGenericUserError`.
env->Exit(static_cast<ExitCode>(
env->maybe_exit_code(static_cast<int>(ExitCode::kGenericUserError))));
}

void TriggerUncaughtException(Isolate* isolate, const v8::TryCatch& try_catch) {
Expand Down
2 changes: 2 additions & 0 deletions src/node_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ std::ostream& operator<<(std::ostream& output,
std::ostream& operator<<(std::ostream& output,
const AsyncHooks::SerializeInfo& d);
std::ostream& operator<<(std::ostream& output, const SnapshotMetadata& d);
std::ostream& operator<<(std::ostream& output,
const ExitInfo::SerializeInfo& d);

namespace performance {
std::ostream& operator<<(std::ostream& output,
Expand Down
9 changes: 9 additions & 0 deletions src/node_process_object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ MaybeLocal<Object> CreateProcessObject(Realm* realm) {
return {};
}

// process[exit_info_private_symbol]
if (process
->SetPrivate(context,
realm->env()->exit_info_private_symbol(),
realm->env()->exit_info()->fields().GetJSArray())
.IsNothing()) {
return {};
}

// process.version
READONLY_PROPERTY(
process, "version", FIXED_ONE_BYTE_STRING(isolate, NODE_VERSION));
Expand Down
33 changes: 33 additions & 0 deletions src/node_snapshotable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
<< "// -- performance_state begins --\n"
<< i.performance_state << ",\n"
<< "// -- performance_state ends --\n"
<< i.exit_info << ", // exit_info\n"
<< i.exiting << ", // exiting\n"
<< i.stream_base_state << ", // stream_base_state\n"
<< i.should_abort_on_uncaught_toggle
Expand Down Expand Up @@ -666,6 +667,36 @@ size_t FileWriter::Write(
return written_total;
}

// Layout of ExitInfo::SerializeInfo
// [ 4/8 bytes ] snapshot index of fields
template <>
ExitInfo::SerializeInfo FileReader::Read() {
Debug("Read<ExitInfo::SerializeInfo>()\n");

ExitInfo::SerializeInfo result;
result.fields = Read<AliasedBufferIndex>();

if (is_debug) {
std::string str = ToStr(result);
Debug("Read<ExitInfo::SerializeInfo>() %s\n", str.c_str());
}

return result;
}

template <>
size_t FileWriter::Write(const ExitInfo::SerializeInfo& data) {
if (is_debug) {
std::string str = ToStr(data);
Debug("Write<ExitInfo::SerializeInfo>() %s\n", str.c_str());
}

size_t written_total = Write<AliasedBufferIndex>(data.fields);

Debug("Write<ExitInfo::SerializeInfo>() wrote %d bytes\n", written_total);
return written_total;
}

// Layout of IsolateDataSerializeInfo
// [ 4/8 bytes ] length of primitive_values vector
// [ ... ] |length| of primitive_values indices
Expand Down Expand Up @@ -736,6 +767,7 @@ EnvSerializeInfo FileReader::Read() {
result.immediate_info = Read<ImmediateInfo::SerializeInfo>();
result.performance_state =
Read<performance::PerformanceState::SerializeInfo>();
result.exit_info = Read<ExitInfo::SerializeInfo>();
result.exiting = Read<AliasedBufferIndex>();
result.stream_base_state = Read<AliasedBufferIndex>();
result.should_abort_on_uncaught_toggle = Read<AliasedBufferIndex>();
Expand All @@ -757,6 +789,7 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) {
written_total += Write<ImmediateInfo::SerializeInfo>(data.immediate_info);
written_total += Write<performance::PerformanceState::SerializeInfo>(
data.performance_state);
written_total += Write<ExitInfo::SerializeInfo>(data.exit_info);
written_total += Write<AliasedBufferIndex>(data.exiting);
written_total += Write<AliasedBufferIndex>(data.stream_base_state);
written_total +=
Expand Down