process: patch more process properties during pre-execution

Delay the creation of process properties that depend on
runtime states and properties that should not be accessed
during bootstrap and patch them during pre-execution:

- process.argv
- process.execPath
- process.title
- process.pid
- process.ppid
- process.REVERT_*
- process.debugPort

PR-URL: https://github.com/nodejs/node/pull/26945
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Joyee Cheung 2019-03-20 09:02:57 +08:00
parent d005f382cd
commit 9dba96dc1a
No known key found for this signature in database
GPG Key ID: 92B78A53C8303B8D
7 changed files with 73 additions and 64 deletions

View File

@ -3,9 +3,9 @@
const { getOptionValue } = require('internal/options'); const { getOptionValue } = require('internal/options');
const { Buffer } = require('buffer'); const { Buffer } = require('buffer');
function prepareMainThreadExecution() { function prepareMainThreadExecution(expandArgv1 = false) {
// Patch the process object with legacy properties and normalizations // Patch the process object with legacy properties and normalizations
patchProcessObject(); patchProcessObject(expandArgv1);
setupTraceCategoryState(); setupTraceCategoryState();
setupInspectorHooks(); setupInspectorHooks();
setupWarningHandler(); setupWarningHandler();
@ -48,7 +48,13 @@ function prepareMainThreadExecution() {
loadPreloadModules(); loadPreloadModules();
} }
function patchProcessObject() { function patchProcessObject(expandArgv1) {
const {
patchProcessObject: patchProcessObjectNative
} = internalBinding('process_methods');
patchProcessObjectNative(process);
Object.defineProperty(process, 'argv0', { Object.defineProperty(process, 'argv0', {
enumerable: true, enumerable: true,
configurable: false, configurable: false,
@ -56,6 +62,12 @@ function patchProcessObject() {
}); });
process.argv[0] = process.execPath; process.argv[0] = process.execPath;
if (expandArgv1 && process.argv[1] && !process.argv[1].startsWith('-')) {
// Expand process.argv[1] into a full path.
const path = require('path');
process.argv[1] = path.resolve(process.argv[1]);
}
// TODO(joyeecheung): most of these should be deprecated and removed, // TODO(joyeecheung): most of these should be deprecated and removed,
// execpt some that we need to be able to mutate during run time. // execpt some that we need to be able to mutate during run time.
addReadOnlyProcessAlias('_eval', '--eval'); addReadOnlyProcessAlias('_eval', '--eval');

View File

@ -18,20 +18,18 @@ const {
stripShebang, stripBOM stripShebang, stripBOM
} = require('internal/modules/cjs/helpers'); } = require('internal/modules/cjs/helpers');
let CJSModule; // TODO(joyeecheung): not every one of these are necessary
function CJSModuleInit() { prepareMainThreadExecution(true);
if (!CJSModule)
CJSModule = require('internal/modules/cjs/loader');
}
if (process.argv[1] && process.argv[1] !== '-') { if (process.argv[1] && process.argv[1] !== '-') {
// Expand process.argv[1] into a full path. // Expand process.argv[1] into a full path.
const path = require('path'); const path = require('path');
process.argv[1] = path.resolve(process.argv[1]); process.argv[1] = path.resolve(process.argv[1]);
// TODO(joyeecheung): not every one of these are necessary // This has to be done after prepareMainThreadExecution because it
prepareMainThreadExecution(); // relies on process.execPath
CJSModuleInit(); const CJSModule = require('internal/modules/cjs/loader');
// Read the source. // Read the source.
const filename = CJSModule._resolveFilename(process.argv[1]); const filename = CJSModule._resolveFilename(process.argv[1]);
@ -42,9 +40,6 @@ if (process.argv[1] && process.argv[1] !== '-') {
checkSyntax(source, filename); checkSyntax(source, filename);
} else { } else {
// TODO(joyeecheung): not every one of these are necessary
prepareMainThreadExecution();
CJSModuleInit();
markBootstrapComplete(); markBootstrapComplete();
readStdin((code) => { readStdin((code) => {
@ -56,6 +51,10 @@ function checkSyntax(source, filename) {
// Remove Shebang. // Remove Shebang.
source = stripShebang(source); source = stripShebang(source);
// This has to be done after prepareMainThreadExecution because it
// relies on process.execPath
const CJSModule = require('internal/modules/cjs/loader');
const { getOptionValue } = require('internal/options'); const { getOptionValue } = require('internal/options');
const experimentalModules = getOptionValue('--experimental-modules'); const experimentalModules = getOptionValue('--experimental-modules');
if (experimentalModules) { if (experimentalModules) {

View File

@ -4,11 +4,7 @@ const {
prepareMainThreadExecution prepareMainThreadExecution
} = require('internal/bootstrap/pre_execution'); } = require('internal/bootstrap/pre_execution');
// Expand process.argv[1] into a full path. prepareMainThreadExecution(true);
const path = require('path');
process.argv[1] = path.resolve(process.argv[1]);
prepareMainThreadExecution();
const CJSModule = require('internal/modules/cjs/loader'); const CJSModule = require('internal/modules/cjs/loader');

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const prefix = `(${process.release.name}:${process.pid}) `;
const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes;
// Lazily loaded // Lazily loaded
@ -55,7 +54,7 @@ function onWarning(warning) {
if (isDeprecation && process.noDeprecation) return; if (isDeprecation && process.noDeprecation) return;
const trace = process.traceProcessWarnings || const trace = process.traceProcessWarnings ||
(isDeprecation && process.traceDeprecation); (isDeprecation && process.traceDeprecation);
var msg = prefix; var msg = `(${process.release.name}:${process.pid}) `;
if (warning.code) if (warning.code)
msg += `[${warning.code}] `; msg += `[${warning.code}] `;
if (trace && warning.stack) { if (trace && warning.stack) {

View File

@ -35,7 +35,7 @@ v8::MaybeLocal<v8::Object> CreateProcessObject(
Environment* env, Environment* env,
const std::vector<std::string>& args, const std::vector<std::string>& args,
const std::vector<std::string>& exec_args); const std::vector<std::string>& exec_args);
void PatchProcessObject(const v8::FunctionCallbackInfo<v8::Value>& args);
} // namespace node } // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_NODE_PROCESS_H_ #endif // SRC_NODE_PROCESS_H_

View File

@ -426,6 +426,7 @@ static void InitializeProcessMethods(Local<Object> target,
env->SetMethod(target, "dlopen", binding::DLOpen); env->SetMethod(target, "dlopen", binding::DLOpen);
env->SetMethod(target, "reallyExit", ReallyExit); env->SetMethod(target, "reallyExit", ReallyExit);
env->SetMethodNoSideEffect(target, "uptime", Uptime); env->SetMethodNoSideEffect(target, "uptime", Uptime);
env->SetMethod(target, "patchProcessObject", PatchProcessObject);
} }
} // namespace node } // namespace node

View File

@ -13,6 +13,7 @@ using v8::Context;
using v8::DEFAULT; using v8::DEFAULT;
using v8::EscapableHandleScope; using v8::EscapableHandleScope;
using v8::Function; using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate; using v8::FunctionTemplate;
using v8::HandleScope; using v8::HandleScope;
using v8::Integer; using v8::Integer;
@ -84,20 +85,6 @@ MaybeLocal<Object> CreateProcessObject(
return MaybeLocal<Object>(); return MaybeLocal<Object>();
} }
// process.title
auto title_string = FIXED_ONE_BYTE_STRING(env->isolate(), "title");
CHECK(process
->SetAccessor(
env->context(),
title_string,
ProcessTitleGetter,
env->owns_process_state() ? ProcessTitleSetter : nullptr,
env->as_callback_data(),
DEFAULT,
None,
SideEffectType::kHasNoSideEffect)
.FromJust());
// process.version // process.version
READONLY_PROPERTY(process, READONLY_PROPERTY(process,
"version", "version",
@ -140,34 +127,56 @@ MaybeLocal<Object> CreateProcessObject(
#endif // _WIN32 #endif // _WIN32
#endif // NODE_HAS_RELEASE_URLS #endif // NODE_HAS_RELEASE_URLS
// process._rawDebug: may be overwritten later in JS land, but should be
// availbale from the begining for debugging purposes
env->SetMethod(process, "_rawDebug", RawDebug);
return scope.Escape(process);
}
void PatchProcessObject(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Environment* env = Environment::GetCurrent(context);
CHECK(args[0]->IsObject());
Local<Object> process = args[0].As<Object>();
// process.title
CHECK(process
->SetAccessor(
context,
FIXED_ONE_BYTE_STRING(isolate, "title"),
ProcessTitleGetter,
env->owns_process_state() ? ProcessTitleSetter : nullptr,
env->as_callback_data(),
DEFAULT,
None,
SideEffectType::kHasNoSideEffect)
.FromJust());
// process.argv // process.argv
process->Set(env->context(), process->Set(context,
FIXED_ONE_BYTE_STRING(env->isolate(), "argv"), FIXED_ONE_BYTE_STRING(isolate, "argv"),
ToV8Value(env->context(), args).ToLocalChecked()).FromJust(); ToV8Value(context, env->argv()).ToLocalChecked()).FromJust();
// process.execArgv // process.execArgv
process->Set(env->context(), process->Set(context,
FIXED_ONE_BYTE_STRING(env->isolate(), "execArgv"), FIXED_ONE_BYTE_STRING(isolate, "execArgv"),
ToV8Value(env->context(), exec_args) ToV8Value(context, env->exec_argv())
.ToLocalChecked()).FromJust(); .ToLocalChecked()).FromJust();
READONLY_PROPERTY(process, "pid", READONLY_PROPERTY(process, "pid",
Integer::New(env->isolate(), uv_os_getpid())); Integer::New(isolate, uv_os_getpid()));
CHECK(process->SetAccessor(env->context(), CHECK(process->SetAccessor(context,
FIXED_ONE_BYTE_STRING(env->isolate(), "ppid"), FIXED_ONE_BYTE_STRING(isolate, "ppid"),
GetParentProcessId).FromJust()); GetParentProcessId).FromJust());
// TODO(joyeecheung): make this available in JS during pre-execution.
// Note that to use this in releases the code doing the revert need to be
// careful to delay the check until after the bootstrap but that may not
// be possible depending on the feature being reverted.
// --security-revert flags // --security-revert flags
#define V(code, _, __) \ #define V(code, _, __) \
do { \ do { \
if (IsReverted(SECURITY_REVERT_ ## code)) { \ if (IsReverted(SECURITY_REVERT_ ## code)) { \
READONLY_PROPERTY(process, "REVERT_" #code, True(env->isolate())); \ READONLY_PROPERTY(process, "REVERT_" #code, True(isolate)); \
} \ } \
} while (0); } while (0);
SECURITY_REVERSIONS(V) SECURITY_REVERSIONS(V)
@ -181,7 +190,7 @@ MaybeLocal<Object> CreateProcessObject(
if (uv_exepath(exec_path_buf, &exec_path_len) == 0) { if (uv_exepath(exec_path_buf, &exec_path_len) == 0) {
exec_path = std::string(exec_path_buf, exec_path_len); exec_path = std::string(exec_path_buf, exec_path_len);
} else { } else {
exec_path = args[0]; exec_path = env->argv()[0];
} }
// On OpenBSD process.execPath will be relative unless we // On OpenBSD process.execPath will be relative unless we
// get the full path before process.execPath is used. // get the full path before process.execPath is used.
@ -196,9 +205,9 @@ MaybeLocal<Object> CreateProcessObject(
uv_fs_req_cleanup(&req); uv_fs_req_cleanup(&req);
#endif #endif
process process
->Set(env->context(), ->Set(context,
FIXED_ONE_BYTE_STRING(env->isolate(), "execPath"), FIXED_ONE_BYTE_STRING(isolate, "execPath"),
String::NewFromUtf8(env->isolate(), String::NewFromUtf8(isolate,
exec_path.c_str(), exec_path.c_str(),
NewStringType::kInternalized, NewStringType::kInternalized,
exec_path.size()) exec_path.size())
@ -207,20 +216,13 @@ MaybeLocal<Object> CreateProcessObject(
} }
// process.debugPort // process.debugPort
auto debug_port_string = FIXED_ONE_BYTE_STRING(env->isolate(), "debugPort");
CHECK(process CHECK(process
->SetAccessor(env->context(), ->SetAccessor(context,
debug_port_string, FIXED_ONE_BYTE_STRING(isolate, "debugPort"),
DebugPortGetter, DebugPortGetter,
env->owns_process_state() ? DebugPortSetter : nullptr, env->owns_process_state() ? DebugPortSetter : nullptr,
env->as_callback_data()) env->as_callback_data())
.FromJust()); .FromJust());
// process._rawDebug: may be overwritten later in JS land, but should be
// availbale from the begining for debugging purposes
env->SetMethod(process, "_rawDebug", RawDebug);
return scope.Escape(process);
} }
} // namespace node } // namespace node