--- node/deps/v8/include/v8.h +++ node/deps/v8/include/v8.h @@ -6427,10 +6427,14 @@ */ static void SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags); + static void PkgReadBytecode(const FunctionCallbackInfo& args); + static void PkgWriteBytecode(); + static void CpuFeaturesProbeTrue(); + /** Get the version string. */ static const char* GetVersion(); /** Callback function for reporting failed access checks.*/ V8_INLINE static V8_DEPRECATED( --- node/deps/v8/src/api.cc +++ node/deps/v8/src/api.cc @@ -13,10 +13,11 @@ #include #include "include/v8-debug.h" #include "include/v8-experimental.h" #include "include/v8-profiler.h" #include "include/v8-testing.h" +#include "include/libplatform/libplatform.h" #include "src/accessors.h" #include "src/api-experimental.h" #include "src/api-natives.h" #include "src/assert-scope.h" #include "src/background-parsing-task.h" @@ -64,10 +65,14 @@ #include "src/v8.h" #include "src/v8threads.h" #include "src/version.h" #include "src/vm-state-inl.h" +#ifdef V8_OS_WIN +#include // For _setmode +#include // For _setmode +#endif namespace v8 { #define LOG_API(isolate, expr) LOG(isolate, ApiEntryCall(expr)) @@ -541,10 +546,241 @@ void V8::SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags) { i::FlagList::SetFlagsFromCommandLine(argc, argv, remove_flags); } +void DumpException(Local context, Local message) { + String::Utf8Value message_string(message->Get()); + auto line_number = message->GetLineNumber(context); + auto message_line_value = message->GetSourceLine(context); + String::Utf8Value message_line(message_line_value.ToLocalChecked()); + base::OS::PrintError("%s at line %d\n", *message_string, line_number); + base::OS::PrintError("%s\n", *message_line); + for (int i = 0; i <= message->GetEndColumn(); ++i) { + int start_column = message->GetStartColumn(context).FromJust(); + base::OS::PrintError("%c", i < start_column ? ' ' : '^'); + } + base::OS::PrintError("\n"); +} + + +void FixScript(Isolate* v8_isolate, Local script) { + auto isolate = reinterpret_cast(v8_isolate); + auto obj = i::Handle::cast(Utils::OpenHandle(*script)); + i::Handle function_info( + i::SharedFunctionInfo::cast(*obj), obj->GetIsolate()); + auto s = reinterpret_cast(function_info->script()); + s->set_source(isolate->heap()->undefined_value()); +} + + +uint8_t* GetExtraFromStdin(uint8_t** pbuffer, int* psize) { + +#ifdef V8_OS_WIN + _setmode(_fileno(stdin), _O_BINARY); +#endif + + int read; + uint32_t length; + + read = static_cast(fread(&length, 1, sizeof(length), stdin)); + if (read != sizeof(length)) { + base::OS::PrintError("Pkg: Invalid stdin provided.\n"); + exit(1); + } + + *psize = static_cast(length); + auto dispose_me = i::NewArray(*psize); + + for (int i = 0; i < *psize;) { + read = static_cast(fread(&dispose_me[i], 1, *psize - i, stdin)); + if (ferror(stdin) != 0) { + base::OS::PrintError("Pkg: Error reading from stdin.\n"); + exit(1); + } + i += read; + } + + *pbuffer = dispose_me; + return dispose_me; + +} + + +void PutBytecodeToStdout(const uint8_t* buffer, int size) { + +#ifdef V8_OS_WIN + _setmode(_fileno(stdout), _O_BINARY); +#endif + + int written; + + for (int i = 0; i < size;) { + written = static_cast(fwrite(&buffer[i], 1, size - i, stdout)); + if (ferror(stdout) != 0) { + base::OS::PrintError("Pkg: Error writing to stdout.\n"); + exit(1); + } + i += written; + } + + fflush(stdout); + +} + + +uint8_t* GetBytecodeFromFileOverlay(FILE* file, uint8_t** pbuffer, int* psize) { + + int read; + uint32_t sentinel, length; + + // start of search. WARNING! THIS VALUE DIFFERS IN NODE BRANCHES + if (file == NULL || fseek(file, 12 * 1024 * 1024, SEEK_SET) != 0) { + base::OS::PrintError("Pkg: Error reading file.\n"); + exit(1); + } + + while (true) { + read = static_cast(fread(&sentinel, 1, sizeof(sentinel), file)); + if (read != sizeof(length)) { + base::OS::PrintError("Pkg: Invalid file provided.\n"); + exit(1); + } + if (sentinel != 0x26e0c928) continue; + fread(&length, 1, sizeof(length), file); + if ((sentinel^length) != 0x6713e24e) continue; // 0x26e0c928^0x41f32b66 + fread(&sentinel, 1, sizeof(sentinel), file); + if (sentinel != 0x3ea13ccf) continue; + break; + } + + fread(&length, 1, sizeof(length), file); + *psize = static_cast(length); + auto dispose_me = i::NewArray(*psize); + + for (int i = 0; i < *psize;) { + read = static_cast(fread(&dispose_me[i], 1, *psize - i, file)); + if (ferror(file) != 0) { + base::OS::PrintError("Pkg: Error reading from file.\n"); + exit(1); + } + i += read; + } + + *pbuffer = dispose_me; + return dispose_me; + +} + + +// from v8\src\base\platform\platform-win32.cc +int wfopen_s(FILE** pFile, const char* filename, const char* mode) { +#ifdef V8_OS_WIN + WCHAR filename_w[32768]; + WCHAR mode_w[16]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_w, sizeof(filename_w)) && + MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_w, sizeof(mode_w))) { + *pFile = _wfopen(filename_w, mode_w); + if (*pFile != NULL) return 0; + } +#endif + *pFile = fopen(filename, mode); + return *pFile != NULL ? 0 : 1; +} + + +// from v8\src\base\platform\platform-win32.cc +FILE* WFOpen(const char* path, const char* mode) { + FILE* result; + if (wfopen_s(&result, path, mode) == 0) { + return result; + } else { + return NULL; + } +} + + +void V8::PkgReadBytecode(const FunctionCallbackInfo& args) { + + String::Utf8Value filename(args[0]); + auto file = WFOpen(*filename, "rb"); + uint8_t* buffer; int size; + auto dispose_me = GetBytecodeFromFileOverlay(file, &buffer, &size); + auto iso = Isolate::GetCurrent(); + + { + + Isolate::Scope iscope(iso); + HandleScope hscope(iso); + auto context = iso->GetCurrentContext(); + Context::Scope cscope(context); + + auto bp = ScriptCompiler::CachedData::BufferNotOwned; + auto data = new ScriptCompiler::CachedData(buffer, size, bp); + auto source_string = String::NewFromUtf8( + iso, "e", NewStringType::kNormal).ToLocalChecked(); + ScriptCompiler::Source source(source_string, data); + auto script = ScriptCompiler::CompileUnbound(iso, &source, + ScriptCompiler::kConsumeCodeCache); + + if (!data->rejected) { + FixScript(iso, script); + auto result = script->BindToCurrentContext() + ->Run(iso->GetCurrentContext()).ToLocalChecked(); + args.GetReturnValue().Set(result); + } + + } + + i::DeleteArray(dispose_me); + fclose(file); + +} + + +void V8::PkgWriteBytecode() { + + // KLOPOV: dont log + i::FLAG_log_code = false; + // KLOPOV: ship harmony. disallow to change it at runtime + i::FLAG_harmony_shipping = true; + i::FLAG_logfile_per_isolate = false; + i::FLAG_serialize_toplevel = true; + i::FLAG_lazy = false; + + uint8_t* buffer; int size; + auto dispose_me = GetExtraFromStdin(&buffer, &size); + auto iso = Isolate::GetCurrent(); + + TryCatch try_catch; + + auto source_string = String::NewFromUtf8( + iso, (char*) buffer, NewStringType::kNormal, size).ToLocalChecked(); + auto origin_string = String::NewFromUtf8( + iso, "e", NewStringType::kNormal).ToLocalChecked(); + ScriptCompiler::Source source(source_string, ScriptOrigin(origin_string)); + ScriptCompiler::CompileUnbound(iso, &source, + ScriptCompiler::kProduceCodeCache); + + if (try_catch.HasCaught()) { + base::OS::PrintError("Pkg: compilation failed.\n"); + DumpException(iso->GetCurrentContext(), try_catch.Message()); + exit(1); + } + + auto data = source.GetCachedData(); + PutBytecodeToStdout(data->data, data->length); + i::DeleteArray(dispose_me); + +} + + +void V8::CpuFeaturesProbeTrue() { + i::CpuFeatures::Probe(true); +} + + RegisteredExtension* RegisteredExtension::first_extension_ = NULL; RegisteredExtension::RegisteredExtension(Extension* extension) : extension_(extension) { } --- node/deps/v8/src/parsing/parser.cc +++ node/deps/v8/src/parsing/parser.cc @@ -5054,10 +5054,11 @@ return false; } bool Parser::Parse(ParseInfo* info) { + if (info->script()->source()->IsUndefined()) return false; DCHECK(info->literal() == NULL); FunctionLiteral* result = NULL; // Ok to use Isolate here; this function is only called in the main thread. DCHECK(parsing_on_main_thread_); Isolate* isolate = info->isolate(); --- node/deps/v8/src/snapshot/code-serializer.cc +++ node/deps/v8/src/snapshot/code-serializer.cc @@ -342,24 +342,25 @@ } SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck( Isolate* isolate, String* source) const { uint32_t magic_number = GetMagicNumber(); - if (magic_number != ComputeMagicNumber(isolate)) return MAGIC_NUMBER_MISMATCH; + if (magic_number != ComputeMagicNumber(isolate)) { + base::OS::PrintError("Pkg: MAGIC_NUMBER_MISMATCH\n"); + return MAGIC_NUMBER_MISMATCH; + } uint32_t version_hash = GetHeaderValue(kVersionHashOffset); - uint32_t source_hash = GetHeaderValue(kSourceHashOffset); + if (version_hash != Version::Hash()) { + base::OS::PrintError("Pkg: VERSION_MISMATCH\n"); + return VERSION_MISMATCH; + } uint32_t cpu_features = GetHeaderValue(kCpuFeaturesOffset); - uint32_t flags_hash = GetHeaderValue(kFlagHashOffset); - uint32_t c1 = GetHeaderValue(kChecksum1Offset); - uint32_t c2 = GetHeaderValue(kChecksum2Offset); - if (version_hash != Version::Hash()) return VERSION_MISMATCH; - if (source_hash != SourceHash(source)) return SOURCE_MISMATCH; - if (cpu_features != static_cast(CpuFeatures::SupportedFeatures())) { + uint32_t host_features = static_cast(CpuFeatures::SupportedFeatures()); + if (cpu_features & (~host_features)) { + base::OS::PrintError("Pkg: CPU_FEATURES_MISMATCH\n"); return CPU_FEATURES_MISMATCH; } - if (flags_hash != FlagList::Hash()) return FLAGS_MISMATCH; - if (!Checksum(Payload()).Check(c1, c2)) return CHECKSUM_MISMATCH; return CHECK_SUCCESS; } uint32_t SerializedCodeData::SourceHash(String* source) const { return source->length(); --- node/deps/v8/tools/gyp/v8.gyp +++ node/deps/v8/tools/gyp/v8.gyp @@ -423,10 +423,11 @@ { 'target_name': 'v8_base', 'type': 'static_library', 'dependencies': [ 'v8_libbase', + 'v8_libplatform', ## KLOPOV my api.cc requires v8::platform::CreateDefaultPlatform ], 'variables': { 'optimize': 'max', }, 'include_dirs+': [ --- node/lib/child_process.js +++ node/lib/child_process.js @@ -53,11 +53,11 @@ throw new TypeError('Forked processes must have an IPC channel'); } options.execPath = options.execPath || process.execPath; - return spawn(options.execPath, args, options); + return exports.spawn(options.execPath, args, options); }; exports._forkChild = function(fd) { // set process.send() --- node/lib/internal/bootstrap_node.js +++ node/lib/internal/bootstrap_node.js @@ -76,15 +76,15 @@ if (NativeModule.exists('_third_party_main')) { // To allow people to extend Node in different ways, this hook allows // one to drop a file lib/_third_party_main.js into the build // directory which will be executed instead of Node's normal loading. - process.nextTick(function() { - NativeModule.require('_third_party_main'); - }); + NativeModule.require('_third_party_main'); + + } - } else if (process.argv[1] == 'debug') { + if (process.argv[1] == 'debug') { // Start the debugger agent NativeModule.require('_debugger').start(); } else if (process.argv[1] == '--remote_debugging_server') { // Start the debugging server --- node/lib/module.js +++ node/lib/module.js @@ -6,12 +6,12 @@ const internalUtil = require('internal/util'); const vm = require('vm'); const assert = require('assert').ok; const fs = require('fs'); const path = require('path'); -const internalModuleReadFile = process.binding('fs').internalModuleReadFile; -const internalModuleStat = process.binding('fs').internalModuleStat; +const internalModuleReadFile = require('fs').internalModuleReadFile; +const internalModuleStat = require('fs').internalModuleStat; const preserveSymlinks = !!process.binding('config').preserveSymlinks; // If obj.hasOwnProperty has been overridden, then calling // obj.hasOwnProperty(prop) will break. // See: https://github.com/joyent/node/issues/1707 --- node/node.gyp +++ node/node.gyp @@ -491,11 +491,10 @@ }], [ 'OS=="win"', { 'sources': [ 'src/backtrace_win32.cc', - 'src/res/node.rc', ], 'defines!': [ 'NODE_PLATFORM="win"', ], 'defines': [ --- node/src/node.cc +++ node/src/node.cc @@ -3515,10 +3515,11 @@ static void PrintHelp(); static bool ParseDebugOpt(const char* arg) { + return false; const char* port = nullptr; if (!strcmp(arg, "--debug")) { use_debug_agent = true; } else if (!strncmp(arg, "--debug=", sizeof("--debug=") - 1)) { @@ -4184,15 +4185,10 @@ } inline void PlatformInit() { #ifdef __POSIX__ - sigset_t sigmask; - sigemptyset(&sigmask); - sigaddset(&sigmask, SIGUSR1); - const int err = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr); - // Make sure file descriptors 0-2 are valid before we start logging anything. for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd += 1) { struct stat ignored; if (fstat(fd, &ignored) == 0) continue; @@ -4202,12 +4198,10 @@ ABORT(); if (fd != open("/dev/null", O_RDWR)) ABORT(); } - CHECK_EQ(err, 0); - // Restore signal dispositions, the parent process may have changed them. struct sigaction act; memset(&act, 0, sizeof(act)); // The hard-coded upper limit is because NSIG is not very reliable; on Linux, @@ -4328,14 +4322,10 @@ // is to prevent memory pointers from being moved around that are returned by // Buffer::Data(). const char no_typed_array_heap[] = "--typed_array_max_size_in_heap=0"; V8::SetFlagsFromString(no_typed_array_heap, sizeof(no_typed_array_heap) - 1); - if (!use_debug_agent) { - RegisterDebugSignalHandler(); - } - // We should set node_is_initialized here instead of in node::Start, // otherwise embedders using node::Init to initialize everything will not be // able to set it and native modules will not load for them. node_is_initialized = true; } @@ -4528,11 +4518,11 @@ } // Entry point for new node instances, also called directly for the main // node instance. -static void StartNodeInstance(void* arg) { +static void StartNodeInstance(void* arg, bool ejs_write) { NodeInstanceData* instance_data = static_cast(arg); Isolate::CreateParams params; ArrayBufferAllocator* array_buffer_allocator = new ArrayBufferAllocator(); params.array_buffer_allocator = array_buffer_allocator; #ifdef NODE_ENABLE_VTUNE_PROFILING @@ -4559,10 +4549,15 @@ Local context = Context::New(isolate); Environment* env = CreateEnvironment(isolate, context, instance_data); array_buffer_allocator->set_env(env); Context::Scope context_scope(context); +if (ejs_write) { + V8::PkgWriteBytecode(); + instance_data->set_exit_code(0); +} else { + isolate->SetAbortOnUncaughtExceptionCallback( ShouldAbortOnUncaughtException); // Start debug agent when argv has --debug if (instance_data->use_debug_agent()) { @@ -4613,10 +4608,12 @@ WaitForInspectorDisconnect(env); #if defined(LEAK_SANITIZER) __lsan_do_leak_check(); #endif +} + array_buffer_allocator->set_env(nullptr); env->Dispose(); env = nullptr; } @@ -4630,11 +4627,11 @@ isolate->Dispose(); isolate = nullptr; delete array_buffer_allocator; } -int Start(int argc, char** argv) { +int Start(int argc, char** argv, bool ejs_write) { PlatformInit(); CHECK_GT(argc, 0); // Hack around with the argv pointer. Used for process.title = "blah". @@ -4655,10 +4652,14 @@ // V8 on Windows doesn't have a good source of entropy. Seed it from // OpenSSL's pool. V8::SetEntropySource(crypto::EntropySource); #endif +if (ejs_write) { + V8::CpuFeaturesProbeTrue(); +} + v8_platform.Initialize(v8_thread_pool_size); V8::Initialize(); int exit_code = 1; { @@ -4667,11 +4668,11 @@ argc, const_cast(argv), exec_argc, exec_argv, use_debug_agent); - StartNodeInstance(&instance_data); + StartNodeInstance(&instance_data, ejs_write); exit_code = instance_data.exit_code(); } V8::Dispose(); v8_platform.Dispose(); @@ -4680,7 +4681,17 @@ exec_argv = nullptr; return exit_code; } +void PkgReadBytecode(const FunctionCallbackInfo& args) { + V8::PkgReadBytecode(args); +} + +void PkgInitialize(Local target, Local unused, Local context, void* p) { + auto env = Environment::GetCurrent(context); + env->SetMethod(target, "read", PkgReadBytecode); +}; } // namespace node + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(pkg, node::PkgInitialize) --- node/src/node.h +++ node/src/node.h @@ -182,11 +182,12 @@ #if HAVE_OPENSSL && NODE_FIPS_MODE NODE_EXTERN extern bool enable_fips_crypto; NODE_EXTERN extern bool force_fips_crypto; #endif -NODE_EXTERN int Start(int argc, char *argv[]); +void PkgWriteBytecode(); +NODE_EXTERN int Start(int argc, char *argv[], bool ejs_write); NODE_EXTERN void Init(int* argc, const char** argv, int* exec_argc, const char*** exec_argv); --- node/src/node_javascript.cc +++ node/src/node_javascript.cc @@ -31,8 +31,17 @@ env->isolate(), reinterpret_cast(native.source), NewStringType::kNormal, native.source_len).ToLocalChecked(); target->Set(name, source); } } + + auto name = String::NewFromUtf8(env->isolate(), "_third_party_main"); + auto source = String::NewFromUtf8(env->isolate(), + "var binding = process.binding('pkg');\n" \ + "var result; try { result = binding.read(process.execPath); } catch(_) {}\n" \ + "if (!result) { console.error('Pkg: Failed to read bytecode.'); process.exit(2); }\n" \ + "result(process, require, console);\n" + ); + target->Set(name, source); } } // namespace node --- node/src/node_main.cc +++ node/src/node_main.cc @@ -1,7 +1,80 @@ #include "node.h" +#include + +const char* OPTION_RUNTIME = "--runtime"; +const char* OPTION_ENTRYPOINT = "--entrypoint"; +const char* OPTION_WRITE = "--fabricator"; + +// for uv_setup_args +int adjacent(int argc, char** argv, bool ejs_write) { + size_t size = 0; + for (int i = 0; i < argc; i++) { + size += strlen(argv[i]) + 1; + }; + char* args = new char[size]; + size_t pos = 0; + for (int i = 0; i < argc; i++) { + memcpy(&args[pos], argv[i], strlen(argv[i]) + 1); + argv[i] = &args[pos]; + pos += strlen(argv[i]) + 1; + }; + return node::Start( + argc, argv, ejs_write + ); +}; + +int reorder(int argc, char** argv) { + int i; + int runtime_pos = argc; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], OPTION_RUNTIME) == 0) { + runtime_pos = i; + break; + }; + }; + int entrypoint_pos = -1; + for (i = 1 + 1; i < runtime_pos; i++) { + if (strcmp(argv[i - 1], OPTION_ENTRYPOINT) == 0) { + entrypoint_pos = i; + break; + } + } + char** nargv = new char*[argc + 1]; + int c = 0; + nargv[c++] = argv[0]; + for (i = runtime_pos + 1; i < argc; i++) { + if ((strcmp(argv[i], "--v8_options") == 0) || + (strcmp(argv[i], "--v8-options") == 0) || + (strcmp(argv[i], "--expose_gc") == 0) || + (strcmp(argv[i], "--expose-gc") == 0) || + (strncmp(argv[i], "--harmony", 9) == 0)) { // --harmony* + nargv[c++] = argv[i]; + }; + }; + if (entrypoint_pos != -1) { + nargv[c++] = argv[entrypoint_pos]; + } else { + nargv[c++] = "DEFAULT_ENTRYPOINT"; + }; + bool ejs_write = false; + for (i = 1; i < runtime_pos; i++) { + if ((i != entrypoint_pos) && + (i != entrypoint_pos - 1)) { + if (strcmp(argv[i], OPTION_WRITE) == 0) { + ejs_write = true; + } else { + nargv[c++] = argv[i]; + }; + }; + }; + return adjacent( + c, nargv, ejs_write + ); +}; + #ifdef _WIN32 #include int wmain(int argc, wchar_t *wargv[]) { if (!IsWindows7OrGreater()) { @@ -43,17 +116,17 @@ exit(1); } } argv[argc] = nullptr; // Now that conversion is done, we can finally start. - return node::Start(argc, argv); + return reorder(argc, argv); } #else // UNIX int main(int argc, char *argv[]) { // Disable stdio buffering, it interacts poorly with printf() // calls elsewhere in the program (e.g., any logging from V8.) setvbuf(stdout, nullptr, _IONBF, 0); setvbuf(stderr, nullptr, _IONBF, 0); - return node::Start(argc, argv); + return reorder(argc, argv); } #endif